Keeping our priorities straight

- Implement backend priority system
- Give indirect priority 1000 and instancing 500
- Generate the sorted list of backends on demand in case one changes
  priority at runtime
This commit is contained in:
Jozufozu 2024-08-15 21:45:14 -07:00
parent a5f49c6738
commit 1a8ed8db28
4 changed files with 56 additions and 29 deletions

View File

@ -14,9 +14,15 @@ public interface Backend {
Engine createEngine(LevelAccessor level); Engine createEngine(LevelAccessor level);
/** /**
* Get a fallback backend in case this backend is not supported. * The priority of this backend.
* <p>The backend with the highest priority upon first launch will be chosen as the default backend.
*
* <p>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. * Check if this backend is supported.

View File

@ -17,6 +17,7 @@ public final class Backends {
*/ */
public static final Backend INSTANCING = SimpleBackend.builder() public static final Backend INSTANCING = SimpleBackend.builder()
.engineFactory(level -> new EngineImpl(level, new InstancedDrawManager(InstancingPrograms.get()), 256)) .engineFactory(level -> new EngineImpl(level, new InstancedDrawManager(InstancingPrograms.get()), 256))
.priority(500)
.supported(() -> GlCompat.SUPPORTS_INSTANCING && InstancingPrograms.allLoaded() && !ShadersModHandler.isShaderPackInUse()) .supported(() -> GlCompat.SUPPORTS_INSTANCING && InstancingPrograms.allLoaded() && !ShadersModHandler.isShaderPackInUse())
.register(Flywheel.rl("instancing")); .register(Flywheel.rl("instancing"));
@ -25,7 +26,7 @@ public final class Backends {
*/ */
public static final Backend INDIRECT = SimpleBackend.builder() public static final Backend INDIRECT = SimpleBackend.builder()
.engineFactory(level -> new EngineImpl(level, new IndirectDrawManager(IndirectPrograms.get()), 256)) .engineFactory(level -> new EngineImpl(level, new IndirectDrawManager(IndirectPrograms.get()), 256))
.fallback(() -> Backends.INSTANCING) .priority(1000)
.supported(() -> GlCompat.SUPPORTS_INDIRECT && IndirectPrograms.allLoaded() && !ShadersModHandler.isShaderPackInUse()) .supported(() -> GlCompat.SUPPORTS_INDIRECT && IndirectPrograms.allLoaded() && !ShadersModHandler.isShaderPackInUse())
.register(Flywheel.rl("indirect")); .register(Flywheel.rl("indirect"));

View File

@ -3,22 +3,20 @@ package dev.engine_room.flywheel.lib.backend;
import java.util.Objects; import java.util.Objects;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Function; 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.Backend;
import dev.engine_room.flywheel.api.backend.BackendManager;
import dev.engine_room.flywheel.api.backend.Engine; import dev.engine_room.flywheel.api.backend.Engine;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
public final class SimpleBackend implements Backend { public final class SimpleBackend implements Backend {
private final Function<LevelAccessor, Engine> engineFactory; private final Function<LevelAccessor, Engine> engineFactory;
private final Supplier<Backend> fallback; private final int priority;
private final BooleanSupplier isSupported; private final BooleanSupplier isSupported;
public SimpleBackend(Function<LevelAccessor, Engine> engineFactory, Supplier<Backend> fallback, BooleanSupplier isSupported) { public SimpleBackend(int priority, Function<LevelAccessor, Engine> engineFactory, BooleanSupplier isSupported) {
this.priority = priority;
this.engineFactory = engineFactory; this.engineFactory = engineFactory;
this.fallback = fallback;
this.isSupported = isSupported; this.isSupported = isSupported;
} }
@ -32,13 +30,8 @@ public final class SimpleBackend implements Backend {
} }
@Override @Override
public Backend findFallback() { public int priority() {
if (isSupported()) { return priority;
return this;
} else {
return fallback.get()
.findFallback();
}
} }
@Override @Override
@ -48,7 +41,7 @@ public final class SimpleBackend implements Backend {
public static final class Builder { public static final class Builder {
private Function<LevelAccessor, Engine> engineFactory; private Function<LevelAccessor, Engine> engineFactory;
private Supplier<Backend> fallback = BackendManager::offBackend; private int priority = 0;
private BooleanSupplier isSupported; private BooleanSupplier isSupported;
public Builder engineFactory(Function<LevelAccessor, Engine> engineFactory) { public Builder engineFactory(Function<LevelAccessor, Engine> engineFactory) {
@ -56,8 +49,8 @@ public final class SimpleBackend implements Backend {
return this; return this;
} }
public Builder fallback(Supplier<Backend> fallback) { public Builder priority(int priority) {
this.fallback = fallback; this.priority = priority;
return this; return this;
} }
@ -68,10 +61,9 @@ public final class SimpleBackend implements Backend {
public Backend register(ResourceLocation id) { public Backend register(ResourceLocation id) {
Objects.requireNonNull(engineFactory); Objects.requireNonNull(engineFactory);
Objects.requireNonNull(fallback);
Objects.requireNonNull(isSupported); Objects.requireNonNull(isSupported);
return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(engineFactory, fallback, isSupported)); return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(priority, engineFactory, isSupported));
} }
} }
} }

View File

@ -1,8 +1,9 @@
package dev.engine_room.flywheel.impl; package dev.engine_room.flywheel.impl;
import java.util.ArrayList;
import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.Flywheel;
import dev.engine_room.flywheel.api.backend.Backend; 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.impl.visualization.VisualizationManagerImpl;
import dev.engine_room.flywheel.lib.backend.SimpleBackend; import dev.engine_room.flywheel.lib.backend.SimpleBackend;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
@ -31,21 +32,48 @@ public final class BackendManagerImpl {
return backend != OFF_BACKEND; return backend != OFF_BACKEND;
} }
// Don't store this statically because backends can theoretically change their priorities at runtime.
private static ArrayList<Backend> 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() { private static Backend findDefaultBackend() {
// TODO: Automatically select the best default config based on the user's driver var backendsByPriority = backendsByPriority();
// TODO: Figure out how this will work if custom backends are registered and without hardcoding the default backends if (backendsByPriority.isEmpty()) {
return Backends.INDIRECT; // 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() { private static void chooseBackend() {
var preferred = FlwConfig.INSTANCE.backend(); var preferred = FlwConfig.INSTANCE.backend();
var actual = preferred.findFallback(); if (preferred.isSupported()) {
backend = preferred;
if (preferred != actual) { return;
FlwImpl.LOGGER.warn("Flywheel backend fell back from '{}' to '{}'", Backend.REGISTRY.getIdOrThrow(preferred), Backend.REGISTRY.getIdOrThrow(actual));
} }
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() { public static String getBackendString() {