diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java index 62c905e65..8b7b00af1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java @@ -161,7 +161,16 @@ public abstract class AbstractInstancer implements Instancer * Clear all instances without freeing resources. */ public void clear() { - handles.forEach(InstanceHandleImpl::clear); + for (InstanceHandleImpl handle : handles) { + // Only clear instances that belong to this instancer. + // If one of these handles was stolen by another instancer, + // clearing it here would cause significant visual artifacts and instance leaks. + // At the same time, we need to clear handles we own to prevent + // instances from changing/deleting positions in this instancer that no longer exist. + if (handle.instancer == this) { + handle.clear(); + } + } instances.clear(); handles.clear(); changed.clear(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java b/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java index 78f5aa65e..c003a9264 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java @@ -4,7 +4,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.backend.Engine; @@ -32,7 +34,7 @@ public abstract class DrawManager> { *
* All new instancers land here before having resources allocated in {@link #flush}. */ - protected final List> initializationQueue = new ArrayList<>(); + protected final Queue> initializationQueue = new ConcurrentLinkedQueue<>(); @SuppressWarnings("unchecked") public Instancer getInstancer(Environment environment, InstanceType type, Model model, RenderStage stage) { @@ -72,7 +74,8 @@ public abstract class DrawManager> { // Only queue the instancer for initialization if it has anything to render. if (checkAndWarnEmptyModel(key.model())) { // Thread safety: this method is called atomically from within computeIfAbsent, - // so we don't need extra synchronization to protect the queue. + // so you'd think we don't need extra synchronization to protect the queue, but + // somehow threads can race here and wind up never initializing an instancer. initializationQueue.add(new UninitializedInstancer<>(key, out)); } return out;