diff --git a/samples/src/main/java/com/github/knokko/boiler/samples/HelloTriangle.java b/samples/src/main/java/com/github/knokko/boiler/samples/HelloTriangle.java index 54e4fba..06faeca 100644 --- a/samples/src/main/java/com/github/knokko/boiler/samples/HelloTriangle.java +++ b/samples/src/main/java/com/github/knokko/boiler/samples/HelloTriangle.java @@ -245,7 +245,7 @@ public static void main(String[] args) throws InterruptedException { commandBuffer, "SubmitDraw", waitSemaphores, fence, swapchainImage.presentSemaphore() ); - boiler.swapchains.presentImage(swapchainImage); + boiler.swapchains.presentImage(swapchainImage, fence); frameCounter += 1; } } diff --git a/samples/src/main/java/com/github/knokko/boiler/samples/HelloXR.java b/samples/src/main/java/com/github/knokko/boiler/samples/HelloXR.java index fffd40f..47af9b3 100644 --- a/samples/src/main/java/com/github/knokko/boiler/samples/HelloXR.java +++ b/samples/src/main/java/com/github/knokko/boiler/samples/HelloXR.java @@ -66,7 +66,7 @@ public static void main(String[] args) throws InterruptedException { vkGetPhysicalDeviceFeatures2KHR(physicalDevice, features2); return dynamicRendering.dynamicRendering() && multiview.multiview(); }) - .beforeDeviceCreation((ciDevice, physicalDevice, stack) -> { + .beforeDeviceCreation((ciDevice, instanceExtensions, physicalDevice, stack) -> { var dynamicRendering = VkPhysicalDeviceDynamicRenderingFeaturesKHR.calloc(stack); dynamicRendering.sType$Default(); dynamicRendering.dynamicRendering(true); diff --git a/samples/src/main/java/com/github/knokko/boiler/samples/SimpleRingApproximation.java b/samples/src/main/java/com/github/knokko/boiler/samples/SimpleRingApproximation.java index fffc64d..1a0c6b2 100644 --- a/samples/src/main/java/com/github/knokko/boiler/samples/SimpleRingApproximation.java +++ b/samples/src/main/java/com/github/knokko/boiler/samples/SimpleRingApproximation.java @@ -218,7 +218,7 @@ public static void main(String[] args) throws InterruptedException { commandBuffer, "RingApproximation", waitSemaphores, fence, swapchainImage.presentSemaphore() ); - boiler.swapchains.presentImage(swapchainImage); + boiler.swapchains.presentImage(swapchainImage, fence); frameCounter += 1; } } diff --git a/samples/src/main/java/com/github/knokko/boiler/samples/TerrainPlayground.java b/samples/src/main/java/com/github/knokko/boiler/samples/TerrainPlayground.java index 1fe8019..4668b81 100644 --- a/samples/src/main/java/com/github/knokko/boiler/samples/TerrainPlayground.java +++ b/samples/src/main/java/com/github/knokko/boiler/samples/TerrainPlayground.java @@ -670,7 +670,7 @@ class CameraController { commandBuffer, "TerrainDraw", waitSemaphores, fence, swapchainImage.presentSemaphore() ); - boiler.swapchains.presentImage(swapchainImage); + boiler.swapchains.presentImage(swapchainImage, fence); frameCounter += 1; } } diff --git a/samples/src/main/java/com/github/knokko/boiler/samples/TranslucentWindowPlayground.java b/samples/src/main/java/com/github/knokko/boiler/samples/TranslucentWindowPlayground.java index 5f08a9c..d151d2b 100644 --- a/samples/src/main/java/com/github/knokko/boiler/samples/TranslucentWindowPlayground.java +++ b/samples/src/main/java/com/github/knokko/boiler/samples/TranslucentWindowPlayground.java @@ -126,7 +126,7 @@ public static void main(String[] args) throws InterruptedException { ) }, fence, acquired.presentSemaphore() ); - boiler.swapchains.presentImage(acquired); + boiler.swapchains.presentImage(acquired, fence); } counter += 1; diff --git a/src/main/java/com/github/knokko/boiler/builder/BoilerBuilder.java b/src/main/java/com/github/knokko/boiler/builder/BoilerBuilder.java index 55a77df..cbc84fb 100644 --- a/src/main/java/com/github/knokko/boiler/builder/BoilerBuilder.java +++ b/src/main/java/com/github/knokko/boiler/builder/BoilerBuilder.java @@ -19,21 +19,27 @@ import static com.github.knokko.boiler.builder.BoilerSwapchainBuilder.createSurface; import static com.github.knokko.boiler.exceptions.VulkanFailureException.assertVkSuccess; +import static com.github.knokko.boiler.util.CollectionHelper.decodeStringSet; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.glfw.GLFWVulkan.*; import static org.lwjgl.system.MemoryStack.stackPush; import static org.lwjgl.system.MemoryUtil.memUTF8; import static org.lwjgl.vulkan.EXTDebugUtils.VK_EXT_DEBUG_UTILS_EXTENSION_NAME; import static org.lwjgl.vulkan.EXTMemoryBudget.VK_EXT_MEMORY_BUDGET_EXTENSION_NAME; +import static org.lwjgl.vulkan.EXTSurfaceMaintenance1.VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME; +import static org.lwjgl.vulkan.EXTSwapchainMaintenance1.VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME; import static org.lwjgl.vulkan.EXTValidationFeatures.*; import static org.lwjgl.vulkan.KHRBindMemory2.VK_KHR_BIND_MEMORY_2_EXTENSION_NAME; import static org.lwjgl.vulkan.KHRDedicatedAllocation.VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME; import static org.lwjgl.vulkan.KHRGetMemoryRequirements2.VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME; import static org.lwjgl.vulkan.KHRGetPhysicalDeviceProperties2.VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; +import static org.lwjgl.vulkan.KHRGetPhysicalDeviceProperties2.vkGetPhysicalDeviceFeatures2KHR; +import static org.lwjgl.vulkan.KHRGetSurfaceCapabilities2.VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME; import static org.lwjgl.vulkan.KHRPortabilityEnumeration.VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME; import static org.lwjgl.vulkan.KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME; import static org.lwjgl.vulkan.VK10.*; import static org.lwjgl.vulkan.VK11.VK_API_VERSION_1_1; +import static org.lwjgl.vulkan.VK11.vkGetPhysicalDeviceFeatures2; import static org.lwjgl.vulkan.VK12.VK_API_VERSION_1_2; import static org.lwjgl.vulkan.VK13.VK_API_VERSION_1_3; @@ -45,7 +51,7 @@ public class BoilerBuilder { return new VkInstance(pInstance.get(0), ciInstance); }; - public static final VkDeviceCreator DEFAULT_VK_DEVICE_CREATOR = (ciDevice, physicalDevice, stack) -> { + public static final VkDeviceCreator DEFAULT_VK_DEVICE_CREATOR = (ciDevice, instanceExtensions, physicalDevice, stack) -> { var pDevice = stack.callocPointer(1); assertVkSuccess(vkCreateDevice(physicalDevice, ciDevice, null, pDevice), "CreateDevice", "BoilerBuilder"); return new VkDevice(pDevice.get(0), physicalDevice, ciDevice); @@ -351,6 +357,8 @@ else throw new GLFWFailureException( } } + boolean[] pHasSwapchainMaintenance = { false }; + if (window != 0L) { checkMainThread(); if (!glfwVulkanSupported()) throw new GLFWFailureException("glfwVulkanSupported() returned false"); @@ -360,6 +368,36 @@ else throw new GLFWFailureException( this.requiredVulkanInstanceExtensions.add(memUTF8(glfwExtensions.get(extensionIndex))); } this.requiredVulkanDeviceExtensions.add(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + this.desiredVulkanDeviceExtensions.add(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME); + this.desiredVulkanInstanceExtensions.add(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME); + this.desiredVulkanInstanceExtensions.add(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME); + if (apiVersion == VK_API_VERSION_1_0) { + this.desiredVulkanInstanceExtensions.add(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + } + + this.beforeDeviceCreation((ciDevice, instanceExtensions, physicalDevice, stack) -> { + Set deviceExtensions = decodeStringSet(ciDevice.ppEnabledExtensionNames()); + if (deviceExtensions.contains(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME)) { + var swapchainFeatures = VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT.calloc(stack); + swapchainFeatures.sType$Default(); + + var features = VkPhysicalDeviceFeatures2.calloc(stack); + features.sType$Default(); + features.pNext(swapchainFeatures); + + if (apiVersion != VK_API_VERSION_1_0) { + vkGetPhysicalDeviceFeatures2(physicalDevice, features); + } + if (instanceExtensions.contains(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { + vkGetPhysicalDeviceFeatures2KHR(physicalDevice, features); + } + + if (swapchainFeatures.swapchainMaintenance1()) { + ciDevice.pNext(swapchainFeatures); + pHasSwapchainMaintenance[0] = true; + } + } + }); } XrBoiler xr = null; @@ -372,7 +410,7 @@ else throw new GLFWFailureException( } // Nice for VMA - if (VK_API_VERSION_MAJOR(apiVersion) == 1 && VK_API_VERSION_MINOR(apiVersion) == 0) { + if (apiVersion == VK_API_VERSION_1_0) { this.desiredVulkanInstanceExtensions.add(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); this.desiredVulkanDeviceExtensions.add(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); this.desiredVulkanDeviceExtensions.add(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); @@ -387,14 +425,14 @@ else throw new GLFWFailureException( } var instanceResult = BoilerInstanceBuilder.createInstance(this); - var deviceResult = BoilerDeviceBuilder.createDevice(this, instanceResult.vkInstance()); + var deviceResult = BoilerDeviceBuilder.createDevice(this, instanceResult); var windowSurface = deviceResult.windowSurface() != 0L ? createSurface(deviceResult.vkPhysicalDevice(), deviceResult.windowSurface()) : null; var swapchainSettings = windowSurface != null ? swapchainBuilder.chooseSwapchainSettings(windowSurface) : null; var instance = new BoilerInstance( - window, windowSurface, swapchainSettings, xr, + window, windowSurface, swapchainSettings, pHasSwapchainMaintenance[0], xr, instanceResult.vkInstance(), deviceResult.vkPhysicalDevice(), deviceResult.vkDevice(), instanceResult.enabledExtensions(), deviceResult.enabledExtensions(), deviceResult.queueFamilies(), deviceResult.vmaAllocator() diff --git a/src/main/java/com/github/knokko/boiler/builder/BoilerDeviceBuilder.java b/src/main/java/com/github/knokko/boiler/builder/BoilerDeviceBuilder.java index 9a2845a..d8c544f 100644 --- a/src/main/java/com/github/knokko/boiler/builder/BoilerDeviceBuilder.java +++ b/src/main/java/com/github/knokko/boiler/builder/BoilerDeviceBuilder.java @@ -27,7 +27,7 @@ class BoilerDeviceBuilder { - static Result createDevice(BoilerBuilder builder, VkInstance vkInstance) { + static Result createDevice(BoilerBuilder builder, BoilerInstanceBuilder.Result instanceResult) { VkPhysicalDevice vkPhysicalDevice; VkDevice vkDevice; Set enabledExtensions; @@ -40,17 +40,19 @@ static Result createDevice(BoilerBuilder builder, VkInstance vkInstance) { if (builder.window != 0L) { var pSurface = stack.callocLong(1); assertVkSuccess(glfwCreateWindowSurface( - vkInstance, builder.window, null, pSurface + instanceResult.vkInstance(), builder.window, null, pSurface ), "glfwCreateWindowSurface", null); windowSurface = pSurface.get(0); } else windowSurface = 0L; VkPhysicalDevice[] candidateDevices = BasicDeviceFilter.getCandidates( - builder, vkInstance, windowSurface, builder.printDeviceRejectionInfo + builder, instanceResult.vkInstance(), windowSurface, builder.printDeviceRejectionInfo ); if (candidateDevices.length == 0) throw new NoVkPhysicalDeviceException(); - vkPhysicalDevice = builder.deviceSelector.choosePhysicalDevice(stack, candidateDevices, vkInstance); + vkPhysicalDevice = builder.deviceSelector.choosePhysicalDevice( + stack, candidateDevices, instanceResult.vkInstance() + ); if (vkPhysicalDevice == null) throw new NoVkPhysicalDeviceException(); } @@ -186,10 +188,12 @@ static Result createDevice(BoilerBuilder builder, VkInstance vkInstance) { if (enabledFeatures2 == null) ciDevice.pEnabledFeatures(enabledFeatures10); for (var preCreator : builder.preDeviceCreators) { - preCreator.beforeDeviceCreation(ciDevice, vkPhysicalDevice, stack); + preCreator.beforeDeviceCreation(ciDevice, instanceResult.enabledExtensions(), vkPhysicalDevice, stack); } - vkDevice = builder.vkDeviceCreator.vkCreateDevice(ciDevice, vkPhysicalDevice, stack); + vkDevice = builder.vkDeviceCreator.vkCreateDevice( + ciDevice, instanceResult.enabledExtensions(), vkPhysicalDevice, stack + ); var queueFamilyMap = new HashMap(); for (var entry : uniqueQueueFamilies.entrySet()) { @@ -204,7 +208,7 @@ static Result createDevice(BoilerBuilder builder, VkInstance vkInstance) { ); var vmaVulkanFunctions = VmaVulkanFunctions.calloc(stack); - vmaVulkanFunctions.set(vkInstance, vkDevice); + vmaVulkanFunctions.set(instanceResult.vkInstance(), vkDevice); int vmaFlags = getVmaFlags(enabledExtensions); @@ -212,7 +216,7 @@ static Result createDevice(BoilerBuilder builder, VkInstance vkInstance) { ciAllocator.flags(vmaFlags); ciAllocator.physicalDevice(vkPhysicalDevice); ciAllocator.device(vkDevice); - ciAllocator.instance(vkInstance); + ciAllocator.instance(instanceResult.vkInstance()); ciAllocator.pVulkanFunctions(vmaVulkanFunctions); ciAllocator.vulkanApiVersion(builder.apiVersion); diff --git a/src/main/java/com/github/knokko/boiler/builder/device/PreVkDeviceCreator.java b/src/main/java/com/github/knokko/boiler/builder/device/PreVkDeviceCreator.java index fad12e1..538471a 100644 --- a/src/main/java/com/github/knokko/boiler/builder/device/PreVkDeviceCreator.java +++ b/src/main/java/com/github/knokko/boiler/builder/device/PreVkDeviceCreator.java @@ -4,8 +4,15 @@ import org.lwjgl.vulkan.VkDeviceCreateInfo; import org.lwjgl.vulkan.VkPhysicalDevice; +import java.util.Set; + @FunctionalInterface public interface PreVkDeviceCreator { - void beforeDeviceCreation(VkDeviceCreateInfo ciDevice, VkPhysicalDevice physicalDevice, MemoryStack stack); + void beforeDeviceCreation( + VkDeviceCreateInfo ciDevice, + Set enabledInstanceExtensions, + VkPhysicalDevice physicalDevice, + MemoryStack stack + ); } diff --git a/src/main/java/com/github/knokko/boiler/builder/device/VkDeviceCreator.java b/src/main/java/com/github/knokko/boiler/builder/device/VkDeviceCreator.java index 6bbede8..99f1997 100644 --- a/src/main/java/com/github/knokko/boiler/builder/device/VkDeviceCreator.java +++ b/src/main/java/com/github/knokko/boiler/builder/device/VkDeviceCreator.java @@ -5,10 +5,15 @@ import org.lwjgl.vulkan.VkDeviceCreateInfo; import org.lwjgl.vulkan.VkPhysicalDevice; +import java.util.Set; + @FunctionalInterface public interface VkDeviceCreator { VkDevice vkCreateDevice( - VkDeviceCreateInfo ciDevice, VkPhysicalDevice physicalDevice, MemoryStack stack + VkDeviceCreateInfo ciDevice, + Set enabledInstanceExtensions, + VkPhysicalDevice physicalDevice, + MemoryStack stack ); } diff --git a/src/main/java/com/github/knokko/boiler/builder/xr/XrDeviceCreator.java b/src/main/java/com/github/knokko/boiler/builder/xr/XrDeviceCreator.java index b21d00f..dbb559b 100644 --- a/src/main/java/com/github/knokko/boiler/builder/xr/XrDeviceCreator.java +++ b/src/main/java/com/github/knokko/boiler/builder/xr/XrDeviceCreator.java @@ -9,6 +9,8 @@ import org.lwjgl.vulkan.VkDeviceCreateInfo; import org.lwjgl.vulkan.VkPhysicalDevice; +import java.util.Set; + import static com.github.knokko.boiler.exceptions.VulkanFailureException.assertVkSuccess; import static com.github.knokko.boiler.xr.OpenXrFailureException.assertXrSuccess; import static org.lwjgl.openxr.KHRVulkanEnable2.xrCreateVulkanDeviceKHR; @@ -24,7 +26,10 @@ class XrDeviceCreator implements VkDeviceCreator { } @Override - public VkDevice vkCreateDevice(VkDeviceCreateInfo ciDevice, VkPhysicalDevice physicalDevice, MemoryStack stack) { + public VkDevice vkCreateDevice( + VkDeviceCreateInfo ciDevice, Set instanceExtensions, + VkPhysicalDevice physicalDevice, MemoryStack stack + ) { var xrCiDevice = XrVulkanDeviceCreateInfoKHR.calloc(stack); xrCiDevice.type$Default(); xrCiDevice.systemId(xrSystem); diff --git a/src/main/java/com/github/knokko/boiler/instance/BoilerInstance.java b/src/main/java/com/github/knokko/boiler/instance/BoilerInstance.java index 7358e1d..824c243 100644 --- a/src/main/java/com/github/knokko/boiler/instance/BoilerInstance.java +++ b/src/main/java/com/github/knokko/boiler/instance/BoilerInstance.java @@ -51,7 +51,8 @@ public class BoilerInstance { private boolean destroyed = false; public BoilerInstance( - long glfwWindow, WindowSurface windowSurface, SwapchainSettings swapchainSettings, XrBoiler xr, + long glfwWindow, WindowSurface windowSurface, SwapchainSettings swapchainSettings, + boolean hasSwapchainMaintenance, XrBoiler xr, VkInstance vkInstance, VkPhysicalDevice vkPhysicalDevice, VkDevice vkDevice, Set instanceExtensions, Set deviceExtensions, QueueFamilies queueFamilies, long vmaAllocator @@ -74,7 +75,7 @@ public BoilerInstance( this.pipelines = new BoilerPipelines(this); this.commands = new BoilerCommands(this); this.sync = new BoilerSync(this); - this.swapchains = swapchainSettings != null ? new BoilerSwapchains(this) : null; + this.swapchains = swapchainSettings != null ? new BoilerSwapchains(this, hasSwapchainMaintenance) : null; this.debug = new BoilerDebug(this); } diff --git a/src/main/java/com/github/knokko/boiler/swapchain/AcquireResult.java b/src/main/java/com/github/knokko/boiler/swapchain/AcquireResult.java index 57683ea..717acad 100644 --- a/src/main/java/com/github/knokko/boiler/swapchain/AcquireResult.java +++ b/src/main/java/com/github/knokko/boiler/swapchain/AcquireResult.java @@ -9,8 +9,10 @@ public record AcquireResult( int numSwapchainImages, long acquireSemaphore, long presentSemaphore, + long presentFence, int width, int height, + Object swapchain, long swapchainID, Consumer addPreDestructionCallback ) { diff --git a/src/main/java/com/github/knokko/boiler/swapchain/BoilerSwapchains.java b/src/main/java/com/github/knokko/boiler/swapchain/BoilerSwapchains.java index 0dd7582..80384c2 100644 --- a/src/main/java/com/github/knokko/boiler/swapchain/BoilerSwapchains.java +++ b/src/main/java/com/github/knokko/boiler/swapchain/BoilerSwapchains.java @@ -3,6 +3,7 @@ import com.github.knokko.boiler.instance.BoilerInstance; import org.lwjgl.vulkan.VkPresentInfoKHR; import org.lwjgl.vulkan.VkSwapchainCreateInfoKHR; +import org.lwjgl.vulkan.VkSwapchainPresentFenceInfoEXT; import java.util.ArrayList; import java.util.Collection; @@ -21,14 +22,16 @@ public class BoilerSwapchains { private final BoilerInstance instance; + final boolean hasSwapchainMaintenance; private final Collection oldSwapchains = new ArrayList<>(); private Swapchain currentSwapchain; private long currentSwapchainID; private boolean isOutOfDate; - public BoilerSwapchains(BoilerInstance instance) { + public BoilerSwapchains(BoilerInstance instance, boolean hasSwapchainMaintenance) { this.instance = instance; + this.hasSwapchainMaintenance = hasSwapchainMaintenance; } private void recreateSwapchain(int presentMode) { @@ -40,13 +43,15 @@ private void recreateSwapchain(int presentMode) { } else isOutOfDate = true; } - public void presentImage(AcquireResult acquired) { - presentImage(acquired, null); + public void presentImage(AcquireResult acquired, long drawingFence) { + presentImage(acquired, drawingFence, null); } - public void presentImage(AcquireResult acquired, Consumer beforePresentCallback) { + public void presentImage(AcquireResult acquired, long drawingFence, Consumer beforePresentCallback) { if (isOutOfDate) return; try (var stack = stackPush()) { + var acquiredSwapchain = (Swapchain) acquired.swapchain(); + var presentInfo = VkPresentInfoKHR.calloc(stack); presentInfo.sType$Default(); presentInfo.pWaitSemaphores(stack.longs(acquired.presentSemaphore())); @@ -54,6 +59,16 @@ public void presentImage(AcquireResult acquired, Consumer befo presentInfo.pSwapchains(stack.longs(acquired.vkSwapchain())); presentInfo.pImageIndices(stack.ints(acquired.imageIndex())); presentInfo.pResults(stack.callocInt(1)); + if (hasSwapchainMaintenance) { + instance.sync.waitAndReset(stack, acquired.presentFence(), 1_000_000_000L); + acquiredSwapchain.images[acquired.imageIndex()].drawingFence = drawingFence; + + var fiPresent = VkSwapchainPresentFenceInfoEXT.calloc(stack); + fiPresent.sType$Default(); + fiPresent.pFences(stack.longs(acquired.presentFence())); + + presentInfo.pNext(fiPresent); + } if (beforePresentCallback != null) beforePresentCallback.accept(presentInfo); int presentResult = vkQueuePresentKHR( @@ -98,12 +113,25 @@ public AcquireResult acquireNextImage(int presentMode) { } // Prevent the number of swapchains from escalating when the window is being resized quickly - if (oldSwapchains.size() > 20) { + if (oldSwapchains.size() > 10) { assertVkSuccess(vkDeviceWaitIdle(instance.vkDevice()), "DeviceWaitIdle", "SwapchainGarbage"); destroyOldSwapchains(); } - if (currentSwapchain.canDestroyOldSwapchains) destroyOldSwapchains(); + if (hasSwapchainMaintenance) { + oldSwapchains.removeIf(oldSwapchain -> { + for (var presentFence : oldSwapchain.presentFences) { + if (vkGetFenceStatus(instance.vkDevice(), presentFence) != VK_SUCCESS) return false; + } + for (var image : oldSwapchain.images) { + if (image.drawingFence != VK_NULL_HANDLE && vkGetFenceStatus(instance.vkDevice(), image.drawingFence) != VK_SUCCESS) return false; + } + oldSwapchain.destroy(); + return true; + }); + } else { + if (currentSwapchain.canDestroyOldSwapchains) destroyOldSwapchains(); + } return new AcquireResult( currentSwapchain.vkSwapchain, @@ -112,8 +140,10 @@ public AcquireResult acquireNextImage(int presentMode) { currentSwapchain.images.length, swapchainImage.acquireSemaphore, swapchainImage.presentSemaphore, + swapchainImage.presentFence, currentSwapchain.width, currentSwapchain.height, + currentSwapchain, currentSwapchainID, currentSwapchain.destructionCallbacks::add ); diff --git a/src/main/java/com/github/knokko/boiler/swapchain/Swapchain.java b/src/main/java/com/github/knokko/boiler/swapchain/Swapchain.java index 5f33d7b..ba04aba 100644 --- a/src/main/java/com/github/knokko/boiler/swapchain/Swapchain.java +++ b/src/main/java/com/github/knokko/boiler/swapchain/Swapchain.java @@ -16,7 +16,7 @@ class Swapchain { final long vkSwapchain; final SwapchainImage[] images; final int width, height, presentMode; - final long[] acquireFences, acquireSemaphores, presentSemaphores; + final long[] acquireFences, acquireSemaphores, presentSemaphores, presentFences; final Collection destructionCallbacks = new ArrayList<>(); private int acquireIndex; @@ -47,6 +47,11 @@ class Swapchain { this.acquireSemaphores = instance.sync.createSemaphores("AcquireSwapchainImage", numImages); this.acquireFences = instance.sync.createFences(true, numImages, "AcquireSwapchainImage"); this.presentSemaphores = instance.sync.createSemaphores("PresentSwapchainImage", numImages); + if (instance.swapchains.hasSwapchainMaintenance) { + this.presentFences = instance.sync.createFences(true, numImages, "PresentSwapchainImage"); + } else { + this.presentFences = new long[numImages]; + } for (int index = 0; index < numImages; index++) { long vkImage = pImages.get(index); images[index] = new SwapchainImage(vkImage, index); @@ -59,6 +64,7 @@ SwapchainImage acquire() { long acquireSemaphore = acquireSemaphores[acquireIndex]; long acquireFence = acquireFences[acquireIndex]; long presentSemaphore = presentSemaphores[acquireIndex]; + long presentFence = presentFences[acquireIndex]; try (var stack = stackPush()) { instance.sync.waitAndReset(stack, acquireFence, 2_000_000_000L); @@ -76,6 +82,7 @@ SwapchainImage acquire() { image.acquireSemaphore = acquireSemaphore; image.acquireFence = acquireFence; image.presentSemaphore = presentSemaphore; + image.presentFence = presentFence; acquireIndex = (acquireIndex + 1) % images.length; acquireCounter += 1; @@ -104,6 +111,7 @@ void destroy() { } for (var callback : destructionCallbacks) callback.run(); for (long fence : acquireFences) vkDestroyFence(instance.vkDevice(), fence, null); + for (long fence : presentFences) vkDestroyFence(instance.vkDevice(), fence, null); for (long semaphore : acquireSemaphores) vkDestroySemaphore(instance.vkDevice(), semaphore, null); for (long semaphore : presentSemaphores) vkDestroySemaphore(instance.vkDevice(), semaphore, null); vkDestroySwapchainKHR(instance.vkDevice(), vkSwapchain, null); diff --git a/src/main/java/com/github/knokko/boiler/swapchain/SwapchainImage.java b/src/main/java/com/github/knokko/boiler/swapchain/SwapchainImage.java index a3a1c95..b475c07 100644 --- a/src/main/java/com/github/knokko/boiler/swapchain/SwapchainImage.java +++ b/src/main/java/com/github/knokko/boiler/swapchain/SwapchainImage.java @@ -5,7 +5,7 @@ class SwapchainImage { final long vkImage; final int index; - long acquireSemaphore, acquireFence, presentSemaphore; + long acquireSemaphore, acquireFence, presentSemaphore, presentFence, drawingFence; SwapchainImage(long vkImage, int index) { this.vkImage = vkImage; diff --git a/src/test/java/com/github/knokko/boiler/builder/TestBoilerBuilder.java b/src/test/java/com/github/knokko/boiler/builder/TestBoilerBuilder.java index c246461..8e63639 100644 --- a/src/test/java/com/github/knokko/boiler/builder/TestBoilerBuilder.java +++ b/src/test/java/com/github/knokko/boiler/builder/TestBoilerBuilder.java @@ -118,7 +118,7 @@ public void testComplexInstanceBuilder() { return BoilerBuilder.DEFAULT_VK_INSTANCE_CREATOR.vkCreateInstance(ciInstance, stack); }) - .vkDeviceCreator((ciDevice, physicalDevice, stack) -> { + .vkDeviceCreator((ciDevice, instanceExtensions, physicalDevice, stack) -> { VkPhysicalDeviceVulkan11Features enabledFeatures11 = null; VkPhysicalDeviceVulkan12Features enabledFeatures12 = null; VkPhysicalDeviceVulkan13Features enabledFeatures13 = null; @@ -144,7 +144,9 @@ public void testComplexInstanceBuilder() { assertTrue(enabledFeatures13.dynamicRendering()); pDidCallDeviceCreator[0] = true; - return BoilerBuilder.DEFAULT_VK_DEVICE_CREATOR.vkCreateDevice(ciDevice, physicalDevice, stack); + return BoilerBuilder.DEFAULT_VK_DEVICE_CREATOR.vkCreateDevice( + ciDevice, instanceExtensions, physicalDevice, stack + ); }) .build(); @@ -251,9 +253,9 @@ class TestException extends RuntimeException {} assertThrows(TestException.class, () -> { new BoilerBuilder(VK_API_VERSION_1_0, "TestPreDevice", 1) - .beforeDeviceCreation((ciDevice, physicalDevice, stack) -> didCall[0] = true) - .beforeDeviceCreation((ciDevice, physicalDevice, stack) -> didCall[1] = true) - .vkDeviceCreator((ciDevice, physicalDevice, stack) -> { + .beforeDeviceCreation((ciDevice, instanceExtensions, physicalDevice, stack) -> didCall[0] = true) + .beforeDeviceCreation((ciDevice, instanceExtensions, physicalDevice, stack) -> didCall[1] = true) + .vkDeviceCreator((ciDevice, instanceExtensions, physicalDevice, stack) -> { assertTrue(didCall[0]); assertTrue(didCall[1]); throw new TestException();