Add Iris compatibility

- Use the Iris API to detect shader rendering state
- Remove CommandNameProviderEnum and use a Function argument instead
This commit is contained in:
PepperCode1 2022-01-06 15:25:52 -08:00
parent 05c6d9d685
commit ec97b82ed6
10 changed files with 70 additions and 174 deletions

View file

@ -57,6 +57,8 @@ dependencies {
modCompileOnly 'curse.maven:starlight-521783:3554912'
modCompileOnly 'maven.modrinth:iris:1.18.x-v1.1.4'
implementation 'com.google.code.findbugs:jsr305:3.0.2'
modCompileOnly 'maven.modrinth:indium:1.0.2-alpha1+mc1.18'
modCompileOnly 'io.vram:frex-fabric-mc118:6.0.229'

View file

@ -1,6 +1,10 @@
package com.jozufozu.flywheel.backend;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
@ -42,8 +46,6 @@ public class Backend {
protected Backend() {
loader = new Loader(this);
OptifineHandler.init();
}
/**
@ -97,8 +99,6 @@ public class Backend {
}
public void refresh() {
OptifineHandler.refresh();
capabilities = GL.createCapabilities();
compat = new GlCompat(capabilities);
@ -165,7 +165,7 @@ public class Backend {
FlwEngine preferredChoice = FlwConfig.get()
.getEngine();
boolean usingShaders = OptifineHandler.usingShaders();
boolean usingShaders = IrisShaderHandler.isShaderPackInUse();
boolean canUseEngine = switch (preferredChoice) {
case OFF -> true;
case BATCHING -> !usingShaders;

View file

@ -0,0 +1,51 @@
package com.jozufozu.flywheel.backend;
import net.fabricmc.loader.api.FabricLoader;
import net.irisshaders.iris.api.v0.IrisApi;
public class IrisShaderHandler {
public static final boolean IRIS_LOADED = FabricLoader.getInstance().isModLoaded("iris");
private static final InternalHandler HANDLER;
static {
if (IRIS_LOADED) {
HANDLER = new InternalHandlerImpl();
} else {
HANDLER = new InternalHandler() {};
}
}
private IrisShaderHandler() {
}
public static boolean isShaderPackInUse() {
return HANDLER.isShaderPackInUse();
}
public static boolean isRenderingShadowPass() {
return HANDLER.isRenderingShadowPass();
}
private interface InternalHandler {
default boolean isShaderPackInUse() {
return false;
};
default boolean isRenderingShadowPass() {
return false;
};
}
private static class InternalHandlerImpl implements InternalHandler {
@Override
public boolean isShaderPackInUse() {
return IrisApi.getInstance().isShaderPackInUse();
};
@Override
public boolean isRenderingShadowPass() {
return IrisApi.getInstance().isRenderingShadowPass();
};
}
}

View file

@ -1,115 +0,0 @@
package com.jozufozu.flywheel.backend;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import com.jozufozu.flywheel.util.Lazy;
import net.minecraft.client.Minecraft;
public class OptifineHandler {
public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine";
public static final String SHADER_PACKAGE = "net.optifine.shaders";
private static Package optifine;
private static OptifineHandler handler;
private static final Lazy<BooleanSupplier> isShadowPass = Lazy.of(() -> {
try {
Class<?> ofShaders = Class.forName("net.optifine.shaders.Shaders");
Field field = ofShaders.getDeclaredField("isShadowPass");
field.setAccessible(true);
return () -> {
try {
return field.getBoolean(null);
} catch (IllegalAccessException ignored) {
return false;
}
};
} catch (Exception ignored) {
return () -> false;
}
});
public final boolean usingShaders;
public OptifineHandler(boolean usingShaders) {
this.usingShaders = usingShaders;
}
/**
* Get information about the current Optifine configuration.
*
* @return {@link Optional#empty()} if Optifine is not installed.
*/
public static Optional<OptifineHandler> get() {
return Optional.ofNullable(handler);
}
public static boolean optifineInstalled() {
return optifine != null;
}
public static boolean usingShaders() {
return OptifineHandler.get()
.map(OptifineHandler::isUsingShaders)
.orElse(false);
}
public static boolean isShadowPass() {
return isShadowPass.get().getAsBoolean();
}
public static void init() {
optifine = Package.getPackage(OPTIFINE_ROOT_PACKAGE);
if (optifine == null) {
Backend.LOGGER.info("Optifine not detected.");
} else {
Backend.LOGGER.info("Optifine detected.");
refresh();
}
}
public static void refresh() {
if (optifine == null) return;
boolean shadersOff = areShadersDisabledInOptifineConfigFile();
handler = new OptifineHandler(!shadersOff);
}
private static boolean areShadersDisabledInOptifineConfigFile() {
File dir = Minecraft.getInstance().gameDirectory;
File shaderOptions = new File(dir, "optionsshaders.txt");
boolean shadersOff = true;
try (BufferedReader reader = new BufferedReader(new FileReader(shaderOptions))) {
shadersOff = reader.lines()
.anyMatch(it -> {
String line = it.replaceAll("\\s", "");
if (line.startsWith("shaderPack=")) {
String setting = line.substring("shaderPack=".length());
return setting.equals("OFF") || setting.equals("(internal)");
}
return false;
});
} catch (IOException e) {
Backend.LOGGER.info("No shader config found.");
}
return shadersOff;
}
public boolean isUsingShaders() {
return usingShaders;
}
}

View file

@ -1,6 +1,7 @@
package com.jozufozu.flywheel.config;
import java.util.function.BiConsumer;
import java.util.function.Function;
import org.jetbrains.annotations.NotNull;
@ -22,6 +23,7 @@ public final class ConfigCommands {
ConfigCommandBuilder commandBuilder = new ConfigCommandBuilder("flywheel");
commandBuilder.addOption(config.engine, "backend", (builder, option) -> enumOptionCommand(builder, config, option,
FlwEngine::getShortName,
(source, value) -> {
source.sendFeedback(getEngineMessage(value));
},
@ -67,19 +69,13 @@ public final class ConfigCommands {
}));
}
public static <E extends Enum<E>> void enumOptionCommand(LiteralArgumentBuilder<FabricClientCommandSource> builder, FlwConfig config, EnumOption<E> option, BiConsumer<FabricClientCommandSource, E> displayAction, BiConsumer<FabricClientCommandSource, E> setAction) {
public static <E extends Enum<E>> void enumOptionCommand(LiteralArgumentBuilder<FabricClientCommandSource> builder, FlwConfig config, EnumOption<E> option, Function<E, String> nameFunc, BiConsumer<FabricClientCommandSource, E> displayAction, BiConsumer<FabricClientCommandSource, E> setAction) {
builder.executes(context -> {
displayAction.accept(context.getSource(), option.get());
return Command.SINGLE_SUCCESS;
});
for (E constant : option.getEnumType().getEnumConstants()) {
String name;
if (constant instanceof CommandNameProviderEnum nameProvider) {
name = nameProvider.getCommandName();
} else {
name = constant.name();
}
builder.then(ClientCommandManager.literal(name)
builder.then(ClientCommandManager.literal(nameFunc.apply(constant))
.executes(context -> {
option.set(constant);
setAction.accept(context.getSource(), option.get());
@ -132,8 +128,4 @@ public final class ConfigCommands {
ClientCommandManager.DISPATCHER.register(command);
}
}
public interface CommandNameProviderEnum {
String getCommandName();
}
}

View file

@ -6,7 +6,7 @@ import java.util.Map;
import javax.annotation.Nullable;
public enum FlwEngine implements ConfigCommands.CommandNameProviderEnum {
public enum FlwEngine {
OFF("off", "Off"),
BATCHING("batching", "Parallel Batching"),
INSTANCING("instancing", "GL33 Instanced Arrays"),
@ -29,8 +29,7 @@ public enum FlwEngine implements ConfigCommands.CommandNameProviderEnum {
this.properName = properName;
}
@Override
public String getCommandName() {
public String getShortName() {
return shortName;
}

View file

@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.OptifineHandler;
import com.jozufozu.flywheel.backend.IrisShaderHandler;
import com.jozufozu.flywheel.core.LastActiveCamera;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.fabric.event.FlywheelEvents;
@ -18,7 +18,7 @@ public class FrustumMixin {
@Inject(method = "prepare", at = @At("TAIL"))
private void onPrepare(double x, double y, double z, CallbackInfo ci) {
if (OptifineHandler.isShadowPass()) {
if (IrisShaderHandler.isRenderingShadowPass()) {
FlywheelEvents.BEGIN_FRAME.invoker().handleEvent(new BeginFrameEvent(Minecraft.getInstance().level, LastActiveCamera.getActiveCamera(), (Frustum) (Object) this));
}
}

View file

@ -1,35 +0,0 @@
package com.jozufozu.flywheel.mixin;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.OptifineHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.VideoSettingsScreen;
@Mixin(Minecraft.class)
public class ShaderCloseMixin {
@Shadow
@Nullable
public Screen screen;
@Inject(at = @At("HEAD"), method = "setScreen")
private void whenScreenChanges(Screen screen, CallbackInfo info) {
if (OptifineHandler.optifineInstalled() && screen instanceof VideoSettingsScreen) {
Screen old = this.screen;
if (old != null && old.getClass()
.getName()
.startsWith(OptifineHandler.SHADER_PACKAGE)) {
OptifineHandler.refresh();
}
}
}
}

View file

@ -34,5 +34,8 @@
"fabric": "*",
"minecraft": "1.18.x",
"java": ">=17"
},
"breaks": {
"iris": "<1.1.4"
}
}

View file

@ -19,7 +19,6 @@
"LevelRendererMixin",
"PausedPartialTickAccessor",
"RenderTexturesMixin",
"ShaderCloseMixin",
"ShaderInstanceAccessor",
"atlas.AtlasDataMixin",
"atlas.SheetDataAccessor",