mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-26 15:06:28 +01:00
Back-and better than ever
- Move BackendType to backend package - BackendType is now an interface whose implementors must be registered
This commit is contained in:
parent
4bb7c4bd48
commit
6436f0e9ba
10 changed files with 260 additions and 101 deletions
|
@ -12,6 +12,7 @@ import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer;
|
|||
import com.jozufozu.flywheel.config.BackendTypeArgument;
|
||||
import com.jozufozu.flywheel.config.FlwCommands;
|
||||
import com.jozufozu.flywheel.config.FlwConfig;
|
||||
import com.jozufozu.flywheel.core.BackendTypes;
|
||||
import com.jozufozu.flywheel.core.Components;
|
||||
import com.jozufozu.flywheel.core.DebugRender;
|
||||
import com.jozufozu.flywheel.core.PartialModel;
|
||||
|
@ -74,6 +75,7 @@ public class Flywheel {
|
|||
CrashReportCallables.registerCrashCallable("Flywheel Backend", Backend::getBackendDescriptor);
|
||||
|
||||
ShadersModHandler.init();
|
||||
BackendTypes.init();
|
||||
Backend.init();
|
||||
|
||||
forgeEventBus.addListener(FlwCommands::registerClientCommands);
|
||||
|
|
|
@ -4,10 +4,9 @@ import org.jetbrains.annotations.Nullable;
|
|||
import org.slf4j.Logger;
|
||||
|
||||
import com.jozufozu.flywheel.api.FlywheelWorld;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
||||
import com.jozufozu.flywheel.backend.instancing.ParallelTaskEngine;
|
||||
import com.jozufozu.flywheel.config.BackendType;
|
||||
import com.jozufozu.flywheel.config.FlwConfig;
|
||||
import com.jozufozu.flywheel.core.BackendTypes;
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
@ -58,7 +57,7 @@ public class Backend {
|
|||
}
|
||||
|
||||
public static boolean isOn() {
|
||||
return TYPE != BackendType.OFF;
|
||||
return TYPE != BackendTypes.OFF;
|
||||
}
|
||||
|
||||
public static boolean canUseInstancing(@Nullable Level world) {
|
||||
|
@ -87,18 +86,15 @@ public class Backend {
|
|||
}
|
||||
|
||||
private static BackendType chooseEngine() {
|
||||
BackendType preferredChoice = FlwConfig.get()
|
||||
var preferred = FlwConfig.get()
|
||||
.getBackendType();
|
||||
var actual = preferred.findFallback();
|
||||
|
||||
boolean usingShaders = ShadersModHandler.isShaderPackInUse();
|
||||
boolean canUseEngine = switch (preferredChoice) {
|
||||
case OFF -> true;
|
||||
case BATCHING -> !usingShaders;
|
||||
case INSTANCING -> !usingShaders && GlCompat.getInstance().instancedArraysSupported();
|
||||
case INDIRECT -> !usingShaders && GlCompat.getInstance().supportsIndirect();
|
||||
};
|
||||
if (preferred != actual) {
|
||||
LOGGER.warn("Flywheel backend fell back from '{}' to '{}'", preferred.getShortName(), actual.getShortName());
|
||||
}
|
||||
|
||||
return canUseEngine ? preferredChoice : BackendType.OFF;
|
||||
return actual;
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
|
|
20
src/main/java/com/jozufozu/flywheel/backend/BackendType.java
Normal file
20
src/main/java/com/jozufozu/flywheel/backend/BackendType.java
Normal file
|
@ -0,0 +1,20 @@
|
|||
package com.jozufozu.flywheel.backend;
|
||||
|
||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
public interface BackendType {
|
||||
|
||||
String getProperName();
|
||||
|
||||
String getShortName();
|
||||
|
||||
Component getEngineMessage();
|
||||
|
||||
Engine createEngine();
|
||||
|
||||
BackendType findFallback();
|
||||
|
||||
boolean supported();
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package com.jozufozu.flywheel.backend;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||
import com.jozufozu.flywheel.core.BackendTypes;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
public class SimpleBackendType implements BackendType {
|
||||
|
||||
|
||||
private final String properName;
|
||||
private final String shortName;
|
||||
private final Component engineMessage;
|
||||
private final Supplier<Engine> engineSupplier;
|
||||
private final Supplier<BackendType> fallback;
|
||||
private final BooleanSupplier isSupported;
|
||||
|
||||
public SimpleBackendType(String properName, String shortName, Component engineMessage, Supplier<Engine> engineSupplier, Supplier<BackendType> fallback, BooleanSupplier isSupported) {
|
||||
this.properName = properName;
|
||||
this.shortName = shortName;
|
||||
this.engineMessage = engineMessage;
|
||||
this.engineSupplier = engineSupplier;
|
||||
this.fallback = fallback;
|
||||
this.isSupported = isSupported;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperName() {
|
||||
return properName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getEngineMessage() {
|
||||
return engineMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Engine createEngine() {
|
||||
return engineSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackendType findFallback() {
|
||||
if (this.supported()) {
|
||||
return this;
|
||||
} else {
|
||||
return fallback.get()
|
||||
.findFallback();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supported() {
|
||||
return isSupported.getAsBoolean();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private String properName;
|
||||
private String shortName;
|
||||
private Component engineMessage;
|
||||
private Supplier<Engine> engineSupplier;
|
||||
private Supplier<BackendType> fallback;
|
||||
private BooleanSupplier booleanSupplier;
|
||||
|
||||
public Builder setProperName(String properName) {
|
||||
this.properName = properName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setShortName(String shortName) {
|
||||
this.shortName = shortName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setEngineMessage(Component engineMessage) {
|
||||
this.engineMessage = engineMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setEngineSupplier(Supplier<Engine> engineSupplier) {
|
||||
this.engineSupplier = engineSupplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFallback(Supplier<BackendType> fallback) {
|
||||
this.fallback = fallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder supported(BooleanSupplier booleanSupplier) {
|
||||
this.booleanSupplier = booleanSupplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BackendType register() {
|
||||
return BackendTypes.register(new SimpleBackendType(properName, shortName, engineMessage, engineSupplier, fallback, booleanSupplier));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,14 +4,10 @@ import com.jozufozu.flywheel.api.RenderStage;
|
|||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine;
|
||||
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.effect.Effect;
|
||||
import com.jozufozu.flywheel.backend.instancing.effect.EffectInstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.indirect.IndirectEngine;
|
||||
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
|
||||
import com.jozufozu.flywheel.core.Components;
|
||||
import com.jozufozu.flywheel.core.RenderContext;
|
||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
||||
import com.jozufozu.flywheel.extension.ClientLevelExtension;
|
||||
|
@ -37,12 +33,8 @@ public class InstanceWorld implements AutoCloseable {
|
|||
private final InstanceManager<Effect> effects;
|
||||
|
||||
public static InstanceWorld create(LevelAccessor level) {
|
||||
var engine = switch (Backend.getBackendType()) {
|
||||
case INDIRECT -> new IndirectEngine(Components.WORLD);
|
||||
case INSTANCING -> new InstancingEngine(Components.WORLD, 100 * 100);
|
||||
case BATCHING -> new BatchingEngine();
|
||||
case OFF -> throw new IllegalStateException("Cannot create instance world when backend is off.");
|
||||
};
|
||||
var engine = Backend.getBackendType()
|
||||
.createEngine();
|
||||
|
||||
var entities = new EntityInstanceManager(engine);
|
||||
var blockEntities = new BlockEntityInstanceManager(engine);
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
package com.jozufozu.flywheel.config;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public enum BackendType {
|
||||
OFF("Off"),
|
||||
|
||||
/**
|
||||
* Use a thread pool to buffer instances in parallel on the CPU.
|
||||
*/
|
||||
BATCHING("Parallel Batching"),
|
||||
|
||||
/**
|
||||
* Use GPU instancing to render everything.
|
||||
*/
|
||||
INSTANCING("GL33 Instanced Arrays"),
|
||||
|
||||
/**
|
||||
* Use Compute shaders to cull instances.
|
||||
*/
|
||||
INDIRECT("GL46 Compute Culling"),
|
||||
;
|
||||
|
||||
private static final Map<String, BackendType> lookup;
|
||||
|
||||
static {
|
||||
lookup = new HashMap<>();
|
||||
for (BackendType value : values()) {
|
||||
lookup.put(value.getShortName(), value);
|
||||
}
|
||||
}
|
||||
|
||||
private final String properName;
|
||||
private final String shortName;
|
||||
|
||||
BackendType(String properName) {
|
||||
this.properName = properName;
|
||||
shortName = name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public String getProperName() {
|
||||
return properName;
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static BackendType byName(String name) {
|
||||
return lookup.get(name);
|
||||
}
|
||||
|
||||
public static Collection<String> validNames() {
|
||||
return lookup.keySet();
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ package com.jozufozu.flywheel.config;
|
|||
import java.util.Collection;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import com.jozufozu.flywheel.backend.BackendType;
|
||||
import com.jozufozu.flywheel.core.BackendTypes;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
|
@ -26,22 +28,22 @@ public enum BackendTypeArgument implements ArgumentType<BackendType> {
|
|||
public BackendType parse(StringReader reader) throws CommandSyntaxException {
|
||||
String string = reader.readUnquotedString();
|
||||
|
||||
BackendType engine = BackendType.byName(string);
|
||||
BackendType engine = BackendTypes.getBackendType(string);
|
||||
|
||||
if (engine == null) {
|
||||
throw INVALID.createWithContext(reader, string, BackendType.validNames());
|
||||
throw INVALID.createWithContext(reader, string, BackendTypes.validNames());
|
||||
}
|
||||
|
||||
return engine;
|
||||
}
|
||||
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
return SharedSuggestionProvider.suggest(BackendType.validNames(), builder);
|
||||
return SharedSuggestionProvider.suggest(BackendTypes.validNames(), builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getExamples() {
|
||||
return BackendType.validNames();
|
||||
return BackendTypes.validNames();
|
||||
}
|
||||
|
||||
public static BackendTypeArgument getInstance() {
|
||||
|
|
|
@ -5,6 +5,9 @@ import java.util.function.BiConsumer;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.BackendType;
|
||||
import com.jozufozu.flywheel.backend.SimpleBackendType;
|
||||
import com.jozufozu.flywheel.core.BackendTypes;
|
||||
import com.jozufozu.flywheel.core.uniform.FrustumProvider;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
|
@ -36,7 +39,8 @@ public class FlwCommands {
|
|||
.executes(context -> {
|
||||
LocalPlayer player = Minecraft.getInstance().player;
|
||||
if (player != null) {
|
||||
player.displayClientMessage(getEngineMessage(value.get()), false);
|
||||
player.displayClientMessage(BackendTypes.getBackendType(value.get())
|
||||
.getEngineMessage(), false);
|
||||
}
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
|
@ -44,8 +48,8 @@ public class FlwCommands {
|
|||
.executes(context -> {
|
||||
LocalPlayer player = Minecraft.getInstance().player;
|
||||
if (player != null) {
|
||||
BackendType type = context.getArgument("type", BackendType.class);
|
||||
value.set(type);
|
||||
BackendType type = context.getArgument("type", SimpleBackendType.class);
|
||||
value.set(type.getShortName());
|
||||
|
||||
Component message = getEngineMessage(type);
|
||||
player.displayClientMessage(message, false);
|
||||
|
@ -155,12 +159,7 @@ public class FlwCommands {
|
|||
}
|
||||
|
||||
public static Component getEngineMessage(@NotNull BackendType type) {
|
||||
return switch (type) {
|
||||
case OFF -> new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED);
|
||||
case INSTANCING -> new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN);
|
||||
case BATCHING -> new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN);
|
||||
case INDIRECT -> new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN);
|
||||
};
|
||||
return type.getEngineMessage();
|
||||
}
|
||||
|
||||
public static class ConfigCommandBuilder {
|
||||
|
|
|
@ -2,9 +2,11 @@ package com.jozufozu.flywheel.config;
|
|||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.jozufozu.flywheel.backend.BackendType;
|
||||
import com.jozufozu.flywheel.core.BackendTypes;
|
||||
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import net.minecraftforge.common.ForgeConfigSpec.BooleanValue;
|
||||
import net.minecraftforge.common.ForgeConfigSpec.EnumValue;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
|
||||
|
@ -28,7 +30,7 @@ public class FlwConfig {
|
|||
}
|
||||
|
||||
public BackendType getBackendType() {
|
||||
return client.backend.get();
|
||||
return BackendTypes.getBackendType(client.backend.get());
|
||||
}
|
||||
|
||||
public boolean debugNormals() {
|
||||
|
@ -43,13 +45,14 @@ public class FlwConfig {
|
|||
}
|
||||
|
||||
public static class ClientConfig {
|
||||
public final EnumValue<BackendType> backend;
|
||||
public final ForgeConfigSpec.ConfigValue<String> backend;
|
||||
public final BooleanValue debugNormals;
|
||||
public final BooleanValue limitUpdates;
|
||||
|
||||
public ClientConfig(ForgeConfigSpec.Builder builder) {
|
||||
backend = builder.comment("Select the backend to use.")
|
||||
.defineEnum("backend", BackendType.INSTANCING);
|
||||
.define("backend", BackendTypes.defaultForCurrentPC()
|
||||
.getShortName());
|
||||
|
||||
debugNormals = builder.comment("Enable or disable a debug overlay that colors pixels by their normal.")
|
||||
.define("debugNormals", false);
|
||||
|
|
96
src/main/java/com/jozufozu/flywheel/core/BackendTypes.java
Normal file
96
src/main/java/com/jozufozu/flywheel/core/BackendTypes.java
Normal file
|
@ -0,0 +1,96 @@
|
|||
package com.jozufozu.flywheel.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.backend.BackendType;
|
||||
import com.jozufozu.flywheel.backend.ShadersModHandler;
|
||||
import com.jozufozu.flywheel.backend.SimpleBackendType;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
||||
import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine;
|
||||
import com.jozufozu.flywheel.backend.instancing.indirect.IndirectEngine;
|
||||
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
|
||||
public class BackendTypes {
|
||||
public static final Map<String, BackendType> BACKEND_TYPES = new HashMap<>();
|
||||
|
||||
public static BackendType register(BackendType type) {
|
||||
BACKEND_TYPES.put(type.getShortName(), type);
|
||||
return type;
|
||||
}
|
||||
|
||||
public static final BackendType OFF = SimpleBackendType.builder()
|
||||
.setProperName("Off")
|
||||
.setShortName("off")
|
||||
.setEngineMessage(new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED))
|
||||
.setEngineSupplier(() -> {
|
||||
throw new IllegalStateException("Cannot create engine when backend is off.");
|
||||
})
|
||||
.setFallback(() -> BackendTypes.OFF)
|
||||
.supported(() -> true)
|
||||
.register();
|
||||
|
||||
public static BackendType defaultForCurrentPC() {
|
||||
// TODO: Automatically select the best default config based on the user's driver
|
||||
return INDIRECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a thread pool to buffer instances in parallel on the CPU.
|
||||
*/
|
||||
public static final BackendType BATCHING = SimpleBackendType.builder()
|
||||
.setProperName("Parallel Batching")
|
||||
.setShortName("batching")
|
||||
.setEngineMessage(new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN))
|
||||
.setEngineSupplier(BatchingEngine::new)
|
||||
.setFallback(() -> BackendTypes.OFF)
|
||||
.supported(() -> !ShadersModHandler.isShaderPackInUse())
|
||||
.register();
|
||||
|
||||
@Nullable
|
||||
public static BackendType getBackendType(String name) {
|
||||
return BACKEND_TYPES.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use GPU instancing to render everything.
|
||||
*/
|
||||
public static final BackendType INSTANCING = SimpleBackendType.builder()
|
||||
.setProperName("GL33 Instanced Arrays")
|
||||
.setShortName("instancing")
|
||||
.setEngineMessage(new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN))
|
||||
.setEngineSupplier(() -> new InstancingEngine(Components.WORLD, 100 * 100))
|
||||
.setFallback(() -> BackendTypes.BATCHING)
|
||||
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
||||
.instancedArraysSupported())
|
||||
.register();
|
||||
|
||||
public static Collection<String> validNames() {
|
||||
return BACKEND_TYPES.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Compute shaders to cull instances.
|
||||
*/
|
||||
public static final BackendType INDIRECT = SimpleBackendType.builder()
|
||||
.setProperName("GL46 Compute Culling")
|
||||
.setShortName("indirect")
|
||||
.setEngineMessage(new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN))
|
||||
.setEngineSupplier(() -> new IndirectEngine(Components.WORLD))
|
||||
.setFallback(() -> BackendTypes.INSTANCING)
|
||||
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
||||
.supportsIndirect())
|
||||
.register();
|
||||
|
||||
public static void init() {
|
||||
// noop
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue