diff --git a/common/src/api/java/dev/engine_room/flywheel/api/backend/Backend.java b/common/src/api/java/dev/engine_room/flywheel/api/backend/Backend.java index ca63659c7..6cc6228f9 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/backend/Backend.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/backend/Backend.java @@ -14,9 +14,15 @@ public interface Backend { Engine createEngine(LevelAccessor level); /** - * Get a fallback backend in case this backend is not supported. + * The priority of this backend. + *

The backend with the highest priority upon first launch will be chosen as the default backend. + * + *

If the selected backend becomes unavailable for whatever reason, the next supported backend + * with a LOWER priority than the selected one will be chosen. + * + * @return The priority of this backend. */ - Backend findFallback(); + int priority(); /** * Check if this backend is supported. diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java b/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java index 59e255cd5..81cdf2408 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java @@ -17,6 +17,7 @@ public final class Backends { */ public static final Backend INSTANCING = SimpleBackend.builder() .engineFactory(level -> new EngineImpl(level, new InstancedDrawManager(InstancingPrograms.get()), 256)) + .priority(500) .supported(() -> GlCompat.SUPPORTS_INSTANCING && InstancingPrograms.allLoaded() && !ShadersModHandler.isShaderPackInUse()) .register(Flywheel.rl("instancing")); @@ -25,7 +26,7 @@ public final class Backends { */ public static final Backend INDIRECT = SimpleBackend.builder() .engineFactory(level -> new EngineImpl(level, new IndirectDrawManager(IndirectPrograms.get()), 256)) - .fallback(() -> Backends.INSTANCING) + .priority(1000) .supported(() -> GlCompat.SUPPORTS_INDIRECT && IndirectPrograms.allLoaded() && !ShadersModHandler.isShaderPackInUse()) .register(Flywheel.rl("indirect")); diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java b/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java index cdec1433f..bc09227c1 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java @@ -3,22 +3,20 @@ package dev.engine_room.flywheel.lib.backend; import java.util.Objects; import java.util.function.BooleanSupplier; import java.util.function.Function; -import java.util.function.Supplier; import dev.engine_room.flywheel.api.backend.Backend; -import dev.engine_room.flywheel.api.backend.BackendManager; import dev.engine_room.flywheel.api.backend.Engine; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.LevelAccessor; public final class SimpleBackend implements Backend { private final Function engineFactory; - private final Supplier fallback; + private final int priority; private final BooleanSupplier isSupported; - public SimpleBackend(Function engineFactory, Supplier fallback, BooleanSupplier isSupported) { + public SimpleBackend(int priority, Function engineFactory, BooleanSupplier isSupported) { + this.priority = priority; this.engineFactory = engineFactory; - this.fallback = fallback; this.isSupported = isSupported; } @@ -32,13 +30,8 @@ public final class SimpleBackend implements Backend { } @Override - public Backend findFallback() { - if (isSupported()) { - return this; - } else { - return fallback.get() - .findFallback(); - } + public int priority() { + return priority; } @Override @@ -48,7 +41,7 @@ public final class SimpleBackend implements Backend { public static final class Builder { private Function engineFactory; - private Supplier fallback = BackendManager::offBackend; + private int priority = 0; private BooleanSupplier isSupported; public Builder engineFactory(Function engineFactory) { @@ -56,8 +49,8 @@ public final class SimpleBackend implements Backend { return this; } - public Builder fallback(Supplier fallback) { - this.fallback = fallback; + public Builder priority(int priority) { + this.priority = priority; return this; } @@ -68,10 +61,9 @@ public final class SimpleBackend implements Backend { public Backend register(ResourceLocation id) { Objects.requireNonNull(engineFactory); - Objects.requireNonNull(fallback); Objects.requireNonNull(isSupported); - return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(engineFactory, fallback, isSupported)); + return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(priority, engineFactory, isSupported)); } } } diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/BackendManagerImpl.java b/common/src/main/java/dev/engine_room/flywheel/impl/BackendManagerImpl.java index bd2ae4a78..75133adeb 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/BackendManagerImpl.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/BackendManagerImpl.java @@ -1,8 +1,9 @@ package dev.engine_room.flywheel.impl; +import java.util.ArrayList; + import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.backend.Backend; -import dev.engine_room.flywheel.backend.Backends; import dev.engine_room.flywheel.impl.visualization.VisualizationManagerImpl; import dev.engine_room.flywheel.lib.backend.SimpleBackend; import net.minecraft.client.multiplayer.ClientLevel; @@ -31,21 +32,48 @@ public final class BackendManagerImpl { return backend != OFF_BACKEND; } + // Don't store this statically because backends can theoretically change their priorities at runtime. + private static ArrayList backendsByPriority() { + var backends = new ArrayList<>(Backend.REGISTRY.getAll()); + + // Sort with keys backwards so that the highest priority is first. + backends.sort((a, b) -> Integer.compare(b.priority(), a.priority())); + return backends; + } + private static Backend findDefaultBackend() { - // TODO: Automatically select the best default config based on the user's driver - // TODO: Figure out how this will work if custom backends are registered and without hardcoding the default backends - return Backends.INDIRECT; + var backendsByPriority = backendsByPriority(); + if (backendsByPriority.isEmpty()) { + // This probably shouldn't happen, but fail gracefully. + FlwImpl.LOGGER.warn("No backends registered, defaulting to 'flywheel:off'"); + return OFF_BACKEND; + } + + return backendsByPriority.get(0); } private static void chooseBackend() { var preferred = FlwConfig.INSTANCE.backend(); - var actual = preferred.findFallback(); - - if (preferred != actual) { - FlwImpl.LOGGER.warn("Flywheel backend fell back from '{}' to '{}'", Backend.REGISTRY.getIdOrThrow(preferred), Backend.REGISTRY.getIdOrThrow(actual)); + if (preferred.isSupported()) { + backend = preferred; + return; } - backend = actual; + var backendsByPriority = backendsByPriority(); + + var startIndex = backendsByPriority.indexOf(preferred) + 1; + + // For safety in case we don't find anything + backend = OFF_BACKEND; + for (int i = startIndex; i < backendsByPriority.size(); i++) { + var candidate = backendsByPriority.get(i); + if (candidate.isSupported()) { + backend = candidate; + break; + } + } + + FlwImpl.LOGGER.warn("Flywheel backend fell back from '{}' to '{}'", Backend.REGISTRY.getIdOrThrow(preferred), Backend.REGISTRY.getIdOrThrow(backend)); } public static String getBackendString() {