From d17d9379dfd31fdf64f31c64c4df9854e2a60967 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Mon, 20 Nov 2023 11:16:32 -0800 Subject: [PATCH] Improved vexillology - Separate concept of Flags from TaskExecutor. - Instead, allow TaskExecutor to sync until, or while a given condition is met. - Flags directly store their state as an AtomicBoolean. - Switch `executor.syncOn(flag)` to `executor.syncUntil(flag::isRaised)` - Remove tests made redundant by improved interface. --- .../com/jozufozu/flywheel/api/task/Flag.java | 12 ---- .../flywheel/api/task/TaskExecutor.java | 46 ++++++--------- .../engine/batching/BatchedStagePlan.java | 6 +- .../engine/batching/BatchingEngine.java | 12 ++-- .../engine/indirect/IndirectEngine.java | 8 +-- .../engine/instancing/InstancingEngine.java | 8 +-- .../impl/task/ParallelTaskExecutor.java | 51 ++++++++-------- .../impl/task/SerialTaskExecutor.java | 25 ++------ .../VisualizationManagerImpl.java | 12 ++-- .../com/jozufozu/flywheel/lib/task/Flag.java | 50 ++++++++++++++++ .../jozufozu/flywheel/lib/task/NamedFlag.java | 20 +++++-- .../jozufozu/flywheel/lib/task/RaisePlan.java | 3 +- .../jozufozu/flywheel/lib/task/StageFlag.java | 17 +++++- .../flywheel/lib/task/PlanExecutionTest.java | 58 +++++-------------- 14 files changed, 163 insertions(+), 165 deletions(-) delete mode 100644 src/main/java/com/jozufozu/flywheel/api/task/Flag.java create mode 100644 src/main/java/com/jozufozu/flywheel/lib/task/Flag.java diff --git a/src/main/java/com/jozufozu/flywheel/api/task/Flag.java b/src/main/java/com/jozufozu/flywheel/api/task/Flag.java deleted file mode 100644 index 9c6543653..000000000 --- a/src/main/java/com/jozufozu/flywheel/api/task/Flag.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.jozufozu.flywheel.api.task; - -/** - * Marker interface for flags that can be raised and lowered in a {@link TaskExecutor}. - *
- * Warning: flags will only be considered equal by reference. - * This is to allow multiple instances of the same high level structures to exist at once. - *
- * Keep this in mind when using records as flags. - */ -public interface Flag { -} diff --git a/src/main/java/com/jozufozu/flywheel/api/task/TaskExecutor.java b/src/main/java/com/jozufozu/flywheel/api/task/TaskExecutor.java index 5b5323c9b..6a67b8b51 100644 --- a/src/main/java/com/jozufozu/flywheel/api/task/TaskExecutor.java +++ b/src/main/java/com/jozufozu/flywheel/api/task/TaskExecutor.java @@ -1,52 +1,40 @@ package com.jozufozu.flywheel.api.task; import java.util.concurrent.Executor; +import java.util.function.BooleanSupplier; public interface TaskExecutor extends Executor { /** * Wait for all running tasks to finish. *
* This is useful as a nuclear option, but most of the time you should - * try to use {@link Flag flags} and {@link #syncTo(Flag) syncTo}. + * try to use {@link #syncUntil(BooleanSupplier) syncUntil}. */ void syncPoint(); /** - * Wait for running tasks, until the given Flag is {@link #raise raised}. + * Wait for running tasks, until the given condition is met + * ({@link BooleanSupplier#getAsBoolean()} returns {@code true}). *
- * The flag will remain raised until {@link #lower lowered} manually. + * This method is equivalent to {@code syncWhile(() -> !cond.getAsBoolean())}. * - * @param flag The flag to wait for. - * @return {@code true} if the flag was encountered. May return false if - * this executor runs out of tasks before the flag was raised. + * @param cond The condition to wait for. + * @return {@code true} if the condition is met. {@code false} if + * this executor runs out of tasks before the condition is met. */ - boolean syncTo(Flag flag); + boolean syncUntil(BooleanSupplier cond); /** - * Raise a flag indicating a key point in execution. + * Wait for running tasks, so long as the given condition is met + * ({@link BooleanSupplier#getAsBoolean()} returns {@code true}). *
- * If the flag was already raised, this method does nothing. + * This method is equivalent to {@code syncUntil(() -> !cond.getAsBoolean())}. * - * @param flag The flag to raise. + * @param cond The condition sync on. + * @return {@code true} if the condition is no longer met. {@code false} if + * this executor runs out of tasks while the condition is still met. */ - void raise(Flag flag); - - /** - * Lower a flag that may have been previously raised. - *
- * If the flag was never raised, this method does nothing. - * - * @param flag The flag to lower. - */ - void lower(Flag flag); - - /** - * Check if a flag is raised without waiting for it. - * - * @param flag The flag to check. - * @return {@code true} if the flag is raised. - */ - boolean isRaised(Flag flag); + boolean syncWhile(BooleanSupplier cond); /** * Check for the number of threads this executor uses. @@ -62,7 +50,7 @@ public interface TaskExecutor extends Executor { *
* This method may be called from any thread, but the runnable will only * be executed once somebody calls either {@link #syncPoint()} or - * {@link #syncTo(Flag)}. + * {@link #syncUntil(BooleanSupplier)}. * @param runnable The task to run. */ void scheduleForSync(Runnable runnable); diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchedStagePlan.java b/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchedStagePlan.java index 16280fdc5..2b7143ab1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchedStagePlan.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchedStagePlan.java @@ -7,8 +7,8 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import com.jozufozu.flywheel.api.event.RenderStage; -import com.jozufozu.flywheel.api.task.Flag; import com.jozufozu.flywheel.api.task.TaskExecutor; +import com.jozufozu.flywheel.lib.task.Flag; import com.jozufozu.flywheel.lib.task.SimplyComposedPlan; import com.jozufozu.flywheel.lib.task.StageFlag; import com.jozufozu.flywheel.lib.task.Synchronizer; @@ -37,14 +37,14 @@ public class BatchedStagePlan implements SimplyComposedPlan { @Override public void execute(TaskExecutor taskExecutor, BatchContext context, Runnable onCompletion) { if (isEmpty()) { - taskExecutor.raise(flag); + flag.raise(); onCompletion.run(); return; } taskExecutor.execute(() -> { var sync = new Synchronizer(bufferPlans.size(), () -> { - taskExecutor.raise(flag); + flag.raise(); onCompletion.run(); }); diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchingEngine.java index ffe9fe1dd..3c35bfa3b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchingEngine.java @@ -13,11 +13,11 @@ import com.jozufozu.flywheel.api.instance.InstanceType; import com.jozufozu.flywheel.api.instance.Instancer; import com.jozufozu.flywheel.api.model.Mesh; import com.jozufozu.flywheel.api.model.Model; -import com.jozufozu.flywheel.api.task.Flag; import com.jozufozu.flywheel.api.task.Plan; import com.jozufozu.flywheel.api.task.TaskExecutor; import com.jozufozu.flywheel.backend.engine.AbstractEngine; import com.jozufozu.flywheel.backend.engine.InstancerKey; +import com.jozufozu.flywheel.lib.task.Flag; import com.jozufozu.flywheel.lib.task.NamedFlag; import com.jozufozu.flywheel.lib.task.SimplyComposedPlan; import com.jozufozu.flywheel.lib.task.Synchronizer; @@ -57,7 +57,7 @@ public class BatchingEngine extends AbstractEngine implements SimplyComposedPlan flush(); // Now it's safe to read stage plans in #renderStage. - taskExecutor.raise(flushFlag); + flushFlag.raise(); BatchContext ctx = BatchContext.create(context, renderOrigin); @@ -76,9 +76,9 @@ public class BatchingEngine extends AbstractEngine implements SimplyComposedPlan @Override public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) { - executor.syncTo(flushFlag); + executor.syncUntil(flushFlag::isRaised); if (stage.isLast()) { - executor.lower(flushFlag); + flushFlag.lower(); } var stagePlan = stagePlans.get(stage); @@ -88,8 +88,8 @@ public class BatchingEngine extends AbstractEngine implements SimplyComposedPlan return; } - executor.syncTo(stagePlan.flag); - executor.lower(stagePlan.flag); + executor.syncUntil(stagePlan.flag::isRaised); + stagePlan.flag.lower(); drawTracker.draw(stage); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java index d7f094b84..8f7ae86a6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java @@ -8,12 +8,12 @@ import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.instance.InstanceType; import com.jozufozu.flywheel.api.instance.Instancer; import com.jozufozu.flywheel.api.model.Model; -import com.jozufozu.flywheel.api.task.Flag; import com.jozufozu.flywheel.api.task.Plan; import com.jozufozu.flywheel.api.task.TaskExecutor; import com.jozufozu.flywheel.backend.engine.AbstractEngine; import com.jozufozu.flywheel.gl.GlStateTracker; import com.jozufozu.flywheel.gl.GlTextureUnit; +import com.jozufozu.flywheel.lib.task.Flag; import com.jozufozu.flywheel.lib.task.NamedFlag; import com.jozufozu.flywheel.lib.task.RaisePlan; import com.jozufozu.flywheel.lib.task.SyncedPlan; @@ -49,7 +49,7 @@ public class IndirectEngine extends AbstractEngine { @Override public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) { if (drawManager.hasStage(stage)) { - executor.syncTo(flushFlag); + executor.syncUntil(flushFlag::isRaised); try (var restoreState = GlStateTracker.getRestoreState()) { setup(); @@ -63,8 +63,8 @@ public class IndirectEngine extends AbstractEngine { if (stage.isLast()) { // Need to sync here to ensure this frame has everything executed // in case we didn't have any stages to draw this frame. - executor.syncTo(flushFlag); - executor.lower(flushFlag); + executor.syncUntil(flushFlag::isRaised); + flushFlag.lower(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java index b473b1fda..3e4716780 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java @@ -9,7 +9,6 @@ import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.instance.InstanceType; import com.jozufozu.flywheel.api.instance.Instancer; import com.jozufozu.flywheel.api.model.Model; -import com.jozufozu.flywheel.api.task.Flag; import com.jozufozu.flywheel.api.task.Plan; import com.jozufozu.flywheel.api.task.TaskExecutor; import com.jozufozu.flywheel.backend.compile.InstancingPrograms; @@ -18,6 +17,7 @@ import com.jozufozu.flywheel.backend.engine.UniformBuffer; import com.jozufozu.flywheel.gl.GlStateTracker; import com.jozufozu.flywheel.gl.GlTextureUnit; import com.jozufozu.flywheel.lib.material.MaterialIndices; +import com.jozufozu.flywheel.lib.task.Flag; import com.jozufozu.flywheel.lib.task.NamedFlag; import com.jozufozu.flywheel.lib.task.RaisePlan; import com.jozufozu.flywheel.lib.task.SyncedPlan; @@ -58,7 +58,7 @@ public class InstancingEngine extends AbstractEngine { var drawSet = drawManager.get(stage); if (!drawSet.isEmpty()) { - executor.syncTo(flushFlag); + executor.syncUntil(flushFlag::isRaised); try (var state = GlStateTracker.getRestoreState()) { setup(); @@ -70,8 +70,8 @@ public class InstancingEngine extends AbstractEngine { if (stage.isLast()) { // Need to sync here to ensure this frame has everything executed // in case we didn't have any stages to draw this frame. - executor.syncTo(flushFlag); - executor.lower(flushFlag); + executor.syncUntil(flushFlag::isRaised); + flushFlag.lower(); } } diff --git a/src/main/java/com/jozufozu/flywheel/impl/task/ParallelTaskExecutor.java b/src/main/java/com/jozufozu/flywheel/impl/task/ParallelTaskExecutor.java index 676b1fb84..79122c8d7 100644 --- a/src/main/java/com/jozufozu/flywheel/impl/task/ParallelTaskExecutor.java +++ b/src/main/java/com/jozufozu/flywheel/impl/task/ParallelTaskExecutor.java @@ -1,25 +1,22 @@ package com.jozufozu.flywheel.impl.task; import java.util.ArrayList; -import java.util.Collections; import java.util.Deque; import java.util.List; import java.util.Queue; -import java.util.Set; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BooleanSupplier; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.api.task.Flag; import com.jozufozu.flywheel.api.task.TaskExecutor; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.logging.LogUtils; -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import net.minecraft.util.Mth; // https://github.com/CaffeineMC/sodium-fabric/blob/5d364ed5ba63f9067fcf72a078ca310bff4db3e9/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuilder.java @@ -38,9 +35,6 @@ public class ParallelTaskExecutor implements TaskExecutor { private final List threads = new ArrayList<>(); private final Deque taskQueue = new ConcurrentLinkedDeque<>(); private final Queue mainThreadQueue = new ConcurrentLinkedQueue<>(); - - private final Set flags = Collections.synchronizedSet(new ReferenceOpenHashSet<>()); - private final ThreadGroupNotifier taskNotifier = new ThreadGroupNotifier(); private final WaitGroup waitGroup = new WaitGroup(); @@ -148,18 +142,36 @@ public class ParallelTaskExecutor implements TaskExecutor { } @Override - public boolean syncTo(Flag flag) { + public boolean syncUntil(BooleanSupplier cond) { while (true) { - if (isRaised(flag)) { - // The flag is already raised! + if (cond.getAsBoolean()) { + // The condition is already true! // Early return with true to indicate. return true; } if (syncOneTask()) { // Out of tasks entirely. - // The flag may have been raised though so return the result of isRaised. - return isRaised(flag); + // The condition may have flipped though so return its result. + return cond.getAsBoolean(); + } + } + } + + + @Override + public boolean syncWhile(BooleanSupplier cond) { + while (true) { + if (!cond.getAsBoolean()) { + // The condition is already false! + // Early return with true to indicate. + return true; + } + + if (syncOneTask()) { + // Out of tasks entirely. + // The condition may have flipped though so return its result. + return !cond.getAsBoolean(); } } } @@ -187,21 +199,6 @@ public class ParallelTaskExecutor implements TaskExecutor { return false; } - @Override - public void raise(Flag flag) { - flags.add(flag); - } - - @Override - public void lower(Flag flag) { - flags.remove(flag); - } - - @Override - public boolean isRaised(Flag flag) { - return flags.contains(flag); - } - private void processTask(Runnable task) { try { task.run(); diff --git a/src/main/java/com/jozufozu/flywheel/impl/task/SerialTaskExecutor.java b/src/main/java/com/jozufozu/flywheel/impl/task/SerialTaskExecutor.java index d2888c55c..dfac16e21 100644 --- a/src/main/java/com/jozufozu/flywheel/impl/task/SerialTaskExecutor.java +++ b/src/main/java/com/jozufozu/flywheel/impl/task/SerialTaskExecutor.java @@ -1,17 +1,12 @@ package com.jozufozu.flywheel.impl.task; -import java.util.Set; +import java.util.function.BooleanSupplier; -import com.jozufozu.flywheel.api.task.Flag; import com.jozufozu.flywheel.api.task.TaskExecutor; -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; - public class SerialTaskExecutor implements TaskExecutor { public static final SerialTaskExecutor INSTANCE = new SerialTaskExecutor(); - private final Set flags = new ReferenceOpenHashSet<>(); - private SerialTaskExecutor() { } @@ -30,23 +25,13 @@ public class SerialTaskExecutor implements TaskExecutor { } @Override - public boolean syncTo(Flag flag) { - return isRaised(flag); + public boolean syncUntil(BooleanSupplier cond) { + return cond.getAsBoolean(); } @Override - public void raise(Flag flag) { - flags.add(flag); - } - - @Override - public void lower(Flag flag) { - flags.remove(flag); - } - - @Override - public boolean isRaised(Flag flag) { - return flags.contains(flag); + public boolean syncWhile(BooleanSupplier cond) { + return !cond.getAsBoolean(); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/impl/visualization/VisualizationManagerImpl.java b/src/main/java/com/jozufozu/flywheel/impl/visualization/VisualizationManagerImpl.java index be371d9b2..17639480c 100644 --- a/src/main/java/com/jozufozu/flywheel/impl/visualization/VisualizationManagerImpl.java +++ b/src/main/java/com/jozufozu/flywheel/impl/visualization/VisualizationManagerImpl.java @@ -7,7 +7,6 @@ import com.jozufozu.flywheel.api.backend.BackendManager; import com.jozufozu.flywheel.api.backend.Engine; import com.jozufozu.flywheel.api.event.RenderContext; import com.jozufozu.flywheel.api.event.RenderStage; -import com.jozufozu.flywheel.api.task.Flag; import com.jozufozu.flywheel.api.task.Plan; import com.jozufozu.flywheel.api.task.TaskExecutor; import com.jozufozu.flywheel.api.visual.DynamicVisual; @@ -22,6 +21,7 @@ import com.jozufozu.flywheel.impl.visualization.manager.BlockEntityVisualManager import com.jozufozu.flywheel.impl.visualization.manager.EffectVisualManager; import com.jozufozu.flywheel.impl.visualization.manager.EntityVisualManager; import com.jozufozu.flywheel.lib.math.MatrixUtil; +import com.jozufozu.flywheel.lib.task.Flag; import com.jozufozu.flywheel.lib.task.NamedFlag; import com.jozufozu.flywheel.lib.task.NestedPlan; import com.jozufozu.flywheel.lib.task.RaisePlan; @@ -151,11 +151,11 @@ public class VisualizationManagerImpl implements VisualizationManager { */ public void tick(double cameraX, double cameraY, double cameraZ) { // Make sure we're done with any prior frame or tick to avoid racing. - taskExecutor.syncTo(frameFlag); - taskExecutor.lower(frameFlag); + taskExecutor.syncUntil(frameFlag::isRaised); + frameFlag.lower(); - taskExecutor.syncTo(tickFlag); - taskExecutor.lower(tickFlag); + taskExecutor.syncUntil(tickFlag::isRaised); + tickFlag.lower(); tickPlan.execute(taskExecutor, new TickContext(cameraX, cameraY, cameraZ)); } @@ -171,7 +171,7 @@ public class VisualizationManagerImpl implements VisualizationManager { public void beginFrame(RenderContext context) { // Make sure we're done with the last tick. // Note we don't lower here because many frames may happen per tick. - taskExecutor.syncTo(tickFlag); + taskExecutor.syncUntil(tickFlag::isRaised); framePlan.execute(taskExecutor, context); } diff --git a/src/main/java/com/jozufozu/flywheel/lib/task/Flag.java b/src/main/java/com/jozufozu/flywheel/lib/task/Flag.java new file mode 100644 index 000000000..afa8c5aa1 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/lib/task/Flag.java @@ -0,0 +1,50 @@ +package com.jozufozu.flywheel.lib.task; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * A flag that can be raised and lowered in a thread-safe fashion. + *
+ * Useful when combined with {@link RaisePlan} and {@link com.jozufozu.flywheel.api.task.TaskExecutor#syncUntil TaskExecutor.syncUntil}. + */ +public class Flag { + + private final AtomicBoolean raised = new AtomicBoolean(false); + + + /** + * Raise this flag indicating a key point in execution. + *
+ * If the flag was already raised, this method does nothing. + */ + public void raise() { + raised.set(true); + } + + /** + * Lower this flag that may have been previously raised. + *
+ * If the flag was never raised, this method does nothing. + */ + public void lower() { + raised.set(false); + } + + /** + * Check if this flag is raised. + * + * @return {@code true} if the flag is raised. + */ + public boolean isRaised() { + return raised.get(); + } + + /** + * Check if this flag is lowered. + * + * @return {@code true} if the flag is lowered. + */ + public boolean isLowered() { + return !isRaised(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/lib/task/NamedFlag.java b/src/main/java/com/jozufozu/flywheel/lib/task/NamedFlag.java index dfa79629a..0bf8e402c 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/task/NamedFlag.java +++ b/src/main/java/com/jozufozu/flywheel/lib/task/NamedFlag.java @@ -1,11 +1,21 @@ package com.jozufozu.flywheel.lib.task; -import com.jozufozu.flywheel.api.task.Flag; - /** * A flag with an arbitrary name. - * - * @param name The name of the flag, mainly for debugging purposes. */ -public record NamedFlag(String name) implements Flag { +public final class NamedFlag extends Flag { + private final String name; + + /** + * @param name The name of the flag, mainly for debugging purposes. + */ + public NamedFlag(String name) { + this.name = name; + } + + @Override + public String toString() { + return "NamedFlag[" + "name=" + name + ']'; + } + } diff --git a/src/main/java/com/jozufozu/flywheel/lib/task/RaisePlan.java b/src/main/java/com/jozufozu/flywheel/lib/task/RaisePlan.java index 31d8c7b79..2098d1c87 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/task/RaisePlan.java +++ b/src/main/java/com/jozufozu/flywheel/lib/task/RaisePlan.java @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.lib.task; -import com.jozufozu.flywheel.api.task.Flag; import com.jozufozu.flywheel.api.task.TaskExecutor; public record RaisePlan(Flag flag) implements SimplyComposedPlan { @@ -10,7 +9,7 @@ public record RaisePlan(Flag flag) implements SimplyComposedPlan { @Override public void execute(TaskExecutor taskExecutor, C context, Runnable onCompletion) { - taskExecutor.raise(flag); + flag.raise(); onCompletion.run(); } } diff --git a/src/main/java/com/jozufozu/flywheel/lib/task/StageFlag.java b/src/main/java/com/jozufozu/flywheel/lib/task/StageFlag.java index 1a848128d..c7245c28a 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/task/StageFlag.java +++ b/src/main/java/com/jozufozu/flywheel/lib/task/StageFlag.java @@ -1,12 +1,25 @@ package com.jozufozu.flywheel.lib.task; import com.jozufozu.flywheel.api.event.RenderStage; -import com.jozufozu.flywheel.api.task.Flag; /** * A flag that is associated with a render stage. *
* Useful for synchronizing tasks for a specific render stage. */ -public record StageFlag(RenderStage stage) implements Flag { +public final class StageFlag extends Flag { + private final RenderStage stage; + + /** + * @param stage The render stage this flag is associated with. + */ + public StageFlag(RenderStage stage) { + this.stage = stage; + } + + @Override + public String toString() { + return "StageFlag[" + "stage=" + stage + ']'; + } + } diff --git a/src/test/java/com/jozufozu/flywheel/lib/task/PlanExecutionTest.java b/src/test/java/com/jozufozu/flywheel/lib/task/PlanExecutionTest.java index cf3c95b79..1e007e4df 100644 --- a/src/test/java/com/jozufozu/flywheel/lib/task/PlanExecutionTest.java +++ b/src/test/java/com/jozufozu/flywheel/lib/task/PlanExecutionTest.java @@ -156,18 +156,18 @@ class PlanExecutionTest { var first = new NamedFlag("ready right away"); var second = new NamedFlag("ready after we sync"); - var sync = new Synchronizer(2, () -> EXECUTOR.raise(second)); + var sync = new Synchronizer(2, second::raise); RaisePlan.raise(first) .execute(EXECUTOR, Unit.INSTANCE, sync); - Assertions.assertTrue(EXECUTOR.syncTo(first), "First flag should be raised since we submitted a plan that raises it"); + Assertions.assertTrue(EXECUTOR.syncUntil(first::isRaised), "First flag should be raised since we submitted a plan that raises it"); - Assertions.assertFalse(EXECUTOR.syncTo(second), "Second flag should not be raised yet."); + Assertions.assertFalse(EXECUTOR.syncUntil(second::isRaised), "Second flag should not be raised yet."); sync.decrementAndEventuallyRun(); - Assertions.assertTrue(EXECUTOR.syncTo(second), "Second flag should be raised since it was raised in sync."); + Assertions.assertTrue(EXECUTOR.syncUntil(second::isRaised), "Second flag should be raised since it was raised in sync."); } @Test @@ -186,58 +186,26 @@ class PlanExecutionTest { .then(RaisePlan.raise(second)) .execute(EXECUTOR, Unit.INSTANCE); - Assertions.assertTrue(EXECUTOR.syncTo(first), "First flag should be raised since we submitted a plan that raises it."); + Assertions.assertTrue(EXECUTOR.syncUntil(first::isRaised), "First flag should be raised since we submitted a plan that raises it."); - Assertions.assertFalse(EXECUTOR.isRaised(second), "Second flag should not be raised immediately."); + Assertions.assertFalse(second.isRaised(), "Second flag should not be raised immediately."); - Assertions.assertTrue(EXECUTOR.syncTo(second), "Second flag should be raised since we were waiting for it."); + Assertions.assertTrue(EXECUTOR.syncUntil(second::isRaised), "Second flag should be raised since we were waiting for it."); } @Test - void syncToReturnsExpected() { + void syncUntilReturnsFlagValue() { var flag = new NamedFlag("ready right away"); - Assertions.assertFalse(EXECUTOR.syncTo(flag), "Flag should not be raised yet."); + Assertions.assertFalse(EXECUTOR.syncUntil(flag::isRaised), "Flag should not be raised yet."); - EXECUTOR.raise(flag); + flag.raise(); - Assertions.assertTrue(EXECUTOR.syncTo(flag), "Flag should be raised since we raised it manually."); + Assertions.assertTrue(EXECUTOR.syncUntil(flag::isRaised), "Flag should be raised since we raised it manually."); - EXECUTOR.lower(flag); + flag.lower(); - Assertions.assertFalse(EXECUTOR.syncTo(flag), "Flag should not be raised since we lowered it."); - } - - @Test - void flagsAreReferenceEqual() { - var flagA = new NamedFlag("same"); - var flagB = new NamedFlag("same"); - - Assertions.assertNotSame(flagA, flagB, "Flags should not be the same object."); - Assertions.assertEquals(flagA, flagB, "Flags should be equal."); - - Assertions.assertFalse(EXECUTOR.isRaised(flagA), "Flag A should not be raised yet."); - Assertions.assertFalse(EXECUTOR.isRaised(flagB), "Flag B should not be raised yet."); - - EXECUTOR.raise(flagA); - - Assertions.assertTrue(EXECUTOR.isRaised(flagA), "Flag A should be raised since we raised it manually."); - Assertions.assertFalse(EXECUTOR.isRaised(flagB), "Flag B should not be raised yet."); - - EXECUTOR.raise(flagB); - - Assertions.assertTrue(EXECUTOR.isRaised(flagA), "Flag A should be raised since we raised it manually."); - Assertions.assertTrue(EXECUTOR.isRaised(flagB), "Flag B should be raised since we raised it manually."); - - EXECUTOR.lower(flagA); - - Assertions.assertFalse(EXECUTOR.isRaised(flagA), "Flag A should not be raised since we lowered it."); - Assertions.assertTrue(EXECUTOR.isRaised(flagB), "Flag B should be raised since we raised it manually."); - - EXECUTOR.lower(flagB); - - Assertions.assertFalse(EXECUTOR.isRaised(flagA), "Flag A should not be raised since we lowered it."); - Assertions.assertFalse(EXECUTOR.isRaised(flagB), "Flag B should not be raised since we lowered it."); + Assertions.assertFalse(EXECUTOR.syncUntil(flag::isRaised), "Flag should not be raised since we lowered it."); } private static void assertExpectedSequence(IntArrayList sequence, int... expected) {