Files
Angelica/src/main/java/net/coderbot/iris/Iris.java
T
+4 992644c20f The Mergening (#13)
* ThreadedTessellator -> RenderBlocks instance Tessellator
* Plus a CME workaround until I can figure out why it's happening
* One inject with multiple methods instead of two injects
* Add AF's Tessellator#reset mixin
Fixes screen flashing when chunks are being built
* Thread Saftey
* Make each tesselator use its own ByteBuffer
* Don't make RecyclingList a static variable 🤦
* Add some additions via ITessellatorInstance
* Stop using builtin updateRenderers
* Remove a few unused things in MixinTessellator
* Remove useless casting due to implementing the interface
* Overwrite some more rendering methods to ensure they're not used
* Implement IBlockAccess on WorldSlice
* Currently just proxies to the World... need to get it to use the local cache
* Make WorldSlice use its copied values instead of using the world
* block, meta, and light.  Still need TODO biome
* Finish making WorldSlice thread safe
* Remove last unsafe this.world accesses
* Copy biomeData
* Implement getTileEntity(), and getBiomeGenForCoords()
* Remove unused 1.16+ classes - Biome, Colorizer, and ColorBlender related
* Cache worldHeight instead of asking the provider every time
* Debug Screen updates
* Disable Culling for now (until it works)
* Tessellator Updates
* Use one instance on the main thread, and individual instances on every other thread
* Prevent draw() and buffer.reset() on non singleton instance
* Misc cleanup, maybe better growBuffer?
* Fixing splash screen and GUI issues by excluding SplashProgress from GLStateManager transformer
* Update docs
* Fixing a one line in SodiumWorldRenderer that allows the game setting for the distance at which entities will render. Hope it's not too bad mitch :)
* TileEntites now render
* Less silly transparency
* Removing clamp implementation because I missed the one in mathhelper
* Initial sodium entity smooth lighting
* Fix entity smooth lighting
* Fix lerp uses and use render layers properly (#17)
* Remove some uses of blockstate + patch FluidState
* Fix lerp
* Enable Sodium fluid rendering
* Update FluidRenderer.java
* Rewind buffers when copying
* Fix multidraw
* Force cutout on all non-translucent blocks
* Let vanilla handle render pass state
* Rewrite render layer check to use Forge 1.7 hook
* Fix skylight handling
* Fix flickering
* Clone TEs correctly
* Fixing entityDistMulti (I think)
* Fix TEs rendering on the wrong pass
* Buff max render distance to 32
Pretty useless since the integrated server stalls instantly, but our renderer supports it
* Fix fog
* Fix render distance changes being ignored
* Implement entity culling
* Block face culling
* Re-enable occlusion culling
* Fix x/z coordinate mixup when indexing biome arrays
Partially fixes wrong biome colors, but at chunk edges it's still wrong because
neighboring chunks aren't accessed
* Make a copy of biome data
The old code seemed to work fine but this should be safer
* Make CompatMemoryUtil#memReallocDirect preserve buffer position
LWJGL3's MemoryUtil#memRealloc does this, see its javadoc.
Fixes screen flashing and this error getting printed when indirect buffer grows:
GL_INVALID_OPERATION error generated. Bound draw indirect buffer is not large enough.
* Use biome data of neighboring chunk sections
Fixes wrong colors at chunk edges
* Re-enable fog culling
* Tile entity culling
* Remove some unneeded Sodium code
* Massive code cleanup
* Avoids errors when usin lwjglxdebug
* Bump hodgepodge dep
* Add AngelicaConfig
* Add Mixin to disable MC's builtin checkGLError
* Add configs for iris/sodium
* Iris work
* Move some StateNotifiers to GLStateManager
* Remove some redundant mixins based on sodium/angelica work and rendering state captures
* Add Iris DebugScreenHandler
* rm archaicfix's occlusion renderer and threaded updates
* Implemented Sodium's GUI and a couple of setting fixes (#20)
* Ported Sodium's GUI
* Correct AO
* Fixed Vignette
* Re-enable DirectMemoryAccess
* Adds some code from lwjgl3 in CompatMemoryUtil.java
* Re-add *Unsafe classes
* Remove MixinWorldRenderer
* Add NEID (mixin pr) support
* Only shadow runtime module of antlr4
Reduces jar size by 11 mb
* Update NotFine
* Make LongHashMap actually perform like a hash map
* Options GUI Fixes and some option implementations (#22)
* Correct useNoErrorGLContext option
* Added translucency sorting option
* Implemented Particle Culling
gives real fps boost, however, it's not as big as i thought

* Begin implementing animateOnlyVisibleTextures
* Correct Donation page opening
* Fixed Shift + P
Now it uses NotFine's GUI, but it think that NotFine's GUI should be rewrited for using other config options
* Correct URL's for Linux
* second code cleanup in particle culling
* Fix incorrect TE map population
* Add optional assertMainThread behind `-Dangelica.assertMainThread=true`
* Add back air optimization, but only apply to vanilla air
* Implement more options (#23)
* Combine free regions in GlBufferArena
* Clear global block entity list when initializing renderer
* AF, CompileStubs, and SplashProgress
Remove most of ArchaicFix
* Move a few mixins over to angelica
* Disable if org.lwjgl.util.Debug is set
* Only enable Iris/lwjgl debug callbacks if org.lwjgl.util.Debug is set
* Rewrite Tessellator hacks - now using a ThreadLocal instead of only patching RenderBlocks
* Reenable AF mixins, switch to cached debug var, disable incompatible mixins
* Remove AF deps and a late mixin that was missed
* Add Iris Sodium Compat impl
* Fix Sodium GLProgram to use ResourceLocation instead of Identifier
* Start applying Iris -> Sodium Mixins
* Make NotFine menu open if holding shift
* Fixed Tessellators in production env
* Finish Sodium Fluid Renderer (#24)
* Init and pass Sodium LightPipelines
* Remove registry lookup and refactor WorldUtil
* Hook up lighting pipeline
* Patch fluid velocity
* It's alive! Sodium Fluid Renderer is alive!
* Fix face culling not taking third person camera offset into account
* rewrite ACTEntityRenderer to stop transforming ALL the classes
* use ClassConstantPoolParser in TessellatorTransformer to speed up search for Tessellator references
* use ClassConstantPoolParser in GLStateManagerTransformer to speed up search for class references
* rewrite GLStateManagerTransformer and TessellatorTransformer
* merge GLStateManagerTransfor and TessellatorTransformer into one Transformer
* Fixed smooth lighting rendering
* Iris block_id Mixins & BlockStateIdMap initial impl for 1.7.10
* Fix running with lwjgl debug flag & Cleanup old ShadersMod transformers
* Initialize GLStateManager via Mixins instead of all in the static block
* Clean up chunk load/unload tracking, fix chunks rendering without neighbors sometimes
* Fix occlusion culler not re-running after important uploads

Backport of https://github.com/CaffeineMC/sodium-fabric/pull/755

* Add chunks to queues without going through occlusion culler

Partially reverts the previous chunk tracking commit, because
that logic will reintroduce https://github.com/CaffeineMC/sodium-fabric/issues/605

As long as we only rebuild with neighbors present, we shouldn't
see the visual seams
* Extremely primitive synchronization approach for ISBRH
* Use factory for Spdium menu createElement outputs
This will allow NotFine to use Sodium menu elements and vice versa in a future commit.
* Splash Screen Compat
* Separate remaining NamedState enums
* NotFine but it loads Sodium pages
* Hook up NotFine optimizations
* Hook up some NotFine features
* copy over hud caching code from hodgepodge
* backport glstatemanager mixin
* Track thread that currently owns the GL Context in addition to the main thread.
* First pass over FontRenderer, 1300->1400 FPS on my GPU when showing F3
Done:
 - A batching vertex buffer builder for font output
 - Reduction in drawcalls from 1-3 per character to 1 per font atlas switch
 - Mixin to draw the entire F3 screen in one batch, instead of separate batches per line
TODO:
 - Switch drawing to a VBO
 - Cache string meshes across frames
 - Cache string width calculations used for layouts
 - Multidraw to reduce draw calls even further
* Add JVM flag to bypass GL state caching
* Push/Pop Attrib Hacks -- to be evolved later
* Fix FontRenderer+SplashScreen combo
* HUDCaching tweaks
* Add license
* Jabba Compat, and reset tesselator state even if the chunk render thread blows up
* Initial Xaero's World Map + Minimap Support
* Disable Fastcraft & Optifine if present
* Iris Progress
* Wire up more Iris mixins, informing pipeline stages.  Still not rendering anything visible.
* Don't try to transform the server
* Avoid running biome generator on client in singleplayer
* Call world biome retrieval methods instead of copying array directly (#25)
* Shaders now do something!
* Shadow Pass work
* First shadow pass renders; however it moves all around when moving the camera...
* Second shadow pass not running because it makes everything very very dark instead of just rendering translucents
* Actually use the ShadowRenderer Model View when running the shadow pass...
* Enable Sodium SmoothLightPipeline & Iris useSeparateAo
* Avoid repeatedly copying biome data on each Y level (#27)
* Celestial Rotation fixes
* Water progress
* Uniforms audit
* Lightmap workaround
* PBR compiling, stuck on Normal and Specular textures being added in
* Shaderpack selection "works"; ShaderOptions TODO
* Hud caching angelica (#28)
* Potential hud-caching fix
* Shader Options almost working
** Clicking on everything doesn't yet work, and the visuals are slightly off
* Entity shadows seem to be working
* Move glint fix to Mixin
* Rework mouse handling for ShaderPackConfig
* Stop centering shader options vertically
* Angelica pbr (#31)
* Only use Sodium light pipeline if useSeparateAo is on
Sodium pipeline is still very buggy, so isolate it to Iris-only
for now
* Fix region sorting for translucency
* Open Shaderpack now works on Linux
* RM Shadersmod
* Initial iris scrollbar clicking
* Punt unsupported ISBRHs to the main thread
* Delete the ISBRH lock, shouldn't be needed anymore
* Render GT blocks on main thread
* Require CCC 1.2.0+
* Depend on CCC 1.2
* Fix TESRs not being tracked for blocks rendered on main thread
* Cut down number of runtime dependencies, enable CoreTweaks in dev by default
* Add modern F3 background
* Pull over some hodgepodge mixins


---------

Co-authored-by: makamys <makamys@outlook.com>
Co-authored-by: embeddedt <42941056+embeddedt@users.noreply.github.com>
Co-authored-by: rjnasers <rjnasers@iastate.edu>
Co-authored-by: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com>
Co-authored-by: Ryan Nasers <ryannasersgig@gmail.com>
Co-authored-by: Rad <radoslaw.ryszka006@gmail.com>
Co-authored-by: Bogdan <64613894+Asek3@users.noreply.github.com>
Co-authored-by: Caedis <Caedis@users.noreply.github.com>
Co-authored-by: jss2a98aj <jss2a98aj@gmail.com>
Co-authored-by: Alexdoru <57050655+Alexdoru@users.noreply.github.com>
Co-authored-by: Raven Szewczyk <git@eigenraven.me>
Co-authored-by: mist475 <70655895+mist475@users.noreply.github.com>
Co-authored-by: Ryan Nasers <42074409+Cardinalstars@users.noreply.github.com>
2023-12-24 21:49:28 -08:00

684 lines
26 KiB
Java

package net.coderbot.iris;
import com.google.common.base.Throwables;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.Tags;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.mitchej123.hodgepodge.Common;
import com.mitchej123.hodgepodge.client.HodgepodgeClient;
import com.mojang.realmsclient.gui.ChatFormatting;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.InputEvent;
import lombok.Getter;
import net.coderbot.iris.config.IrisConfig;
import net.coderbot.iris.gl.GLDebug;
import net.coderbot.iris.gl.shader.StandardMacros;
import net.coderbot.iris.gui.screen.ShaderPackScreen;
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
import net.coderbot.iris.pipeline.FixedFunctionWorldRenderingPipeline;
import net.coderbot.iris.pipeline.PipelineManager;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.coderbot.iris.shaderpack.DimensionId;
import net.coderbot.iris.shaderpack.OptionalBoolean;
import net.coderbot.iris.shaderpack.ProgramSet;
import net.coderbot.iris.shaderpack.ShaderPack;
import net.coderbot.iris.shaderpack.discovery.ShaderpackDirectoryManager;
import net.coderbot.iris.shaderpack.option.OptionSet;
import net.coderbot.iris.shaderpack.option.Profile;
import net.coderbot.iris.shaderpack.option.values.MutableOptionValues;
import net.coderbot.iris.shaderpack.option.values.OptionValues;
import net.coderbot.iris.texture.pbr.PBRTextureManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.resources.I18n;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.util.ChatComponentText;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.ContextCapabilities;
import org.lwjgl.opengl.GLContext;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Stream;
import java.util.zip.ZipError;
import java.util.zip.ZipException;
public class Iris {
public final boolean isDevelopmentEnvironment;
public static ContextCapabilities capabilities;
/**
* The user-facing name of the mod. Moved into a constant to facilitate easy branding changes (for forks). You'll still need to change this separately in
* mixin plugin classes & the language files.
*/
public static final String MODNAME = "AngelicaShaders";
public static final IrisLogging logger = new IrisLogging(MODNAME);
private static Path shaderpacksDirectory;
private static ShaderpackDirectoryManager shaderpacksDirectoryManager;
private static ShaderPack currentPack;
@Getter
private static String currentPackName;
@Getter
private static boolean initialized;
private static PipelineManager pipelineManager;
@Getter
private static IrisConfig irisConfig;
private static FileSystem zipFileSystem;
@Getter
private static final Map<String, String> shaderPackOptionQueue = new HashMap<>();
// Flag variable used when reloading
// Used in favor of queueDefaultShaderPackOptionValues() for resetting as the
// behavior is more concrete and therefore is more likely to repair a user's issues
private static boolean resetShaderPackOptions = false;
private static String IRIS_VERSION;
@Getter
private static boolean fallback;
private static KeyBinding reloadKeybind;
private static KeyBinding toggleShadersKeybind;
private static KeyBinding shaderpackScreenKeybind;
public static Iris INSTANCE = new Iris();
private Iris() {
isDevelopmentEnvironment = (boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment");
}
@SubscribeEvent
public void onKeypress(InputEvent.KeyInputEvent event) {
if (reloadKeybind.isPressed()) {
final Minecraft mc = Minecraft.getMinecraft();
try {
reload();
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText("Shaders Reloaded!"));
} catch (Exception e) {
logger.error("Error while reloading Shaders for Iris!", e);
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText( "Failed tgo reload shaders! Reason: " + Throwables.getRootCause(e).getMessage()));
}
} else if (toggleShadersKeybind.isPressed()) {
final Minecraft mc = Minecraft.getMinecraft();
try {
toggleShaders(mc, !irisConfig.areShadersEnabled());
} catch (Exception e) {
logger.error("Error while toggling shaders!", e);
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText( "Failed tgo toggle shaders! Reason: " + Throwables.getRootCause(e).getMessage()));
setShadersDisabled();
fallback = true;
}
} else if (shaderpackScreenKeybind.isPressed()) {
final Minecraft mc = Minecraft.getMinecraft();
mc.displayGuiScreen(new ShaderPackScreen(null));
}
}
@SubscribeEvent
public void keyUp(InputEvent.KeyInputEvent event) {
final int key = Keyboard.getEventKey();
final boolean released = !Keyboard.getEventKeyState();
if (Minecraft.getMinecraft().gameSettings.showDebugInfo && GuiScreen.isShiftKeyDown() && GuiScreen.isCtrlKeyDown() && released) {
if (key == Keyboard.KEY_N) {
AngelicaMod.animationsMode.next();
}
}
}
/**
* Called very early on in Minecraft initialization. At this point we *cannot* safely access OpenGL, but we can do some very basic setup, config loading,
* and environment checks.
*
* <p>This is roughly equivalent to Fabric Loader's ClientModInitializer#onInitializeClient entrypoint, except
* it's entirely cross platform & we get to decide its exact semantics.</p>
*
* <p>This is called right before options are loaded, so we can add key bindings here.</p>
*/
public void onEarlyInitialize() {
try {
if (!Files.exists(getShaderpacksDirectory())) {
Files.createDirectories(getShaderpacksDirectory());
}
} catch (IOException e) {
logger.warn("Failed to create the shaderpacks directory!");
logger.warn("", e);
}
irisConfig = new IrisConfig(Minecraft.getMinecraft().mcDataDir.toPath().resolve("config").resolve("shaders.properties"));
try {
irisConfig.initialize();
} catch (IOException e) {
logger.error("Failed to initialize Angelica configuration, default values will be used instead");
logger.error("", e);
}
initialized = true;
}
public static void identifyCapabilities() {
capabilities = GLContext.getCapabilities();
}
/**
* Called once RenderSystem#initRenderer has completed. This means that we can safely access OpenGL.
*/
public static void onRenderSystemInit() {
if (!initialized) {
Iris.logger.warn("Iris::onRenderSystemInit was called, but Iris::onEarlyInitialize was not called."
+ " Trying to avoid a crash but this is an odd state.");
return;
}
setDebug(irisConfig.areDebugOptionsEnabled());
PBRTextureManager.INSTANCE.init();
// Only load the shader pack when we can access OpenGL
loadShaderpack();
}
/**
* Called when the title screen is initialized for the first time.
*/
public static void onLoadingComplete() {
if (!initialized) {
Iris.logger.warn("Iris::onLoadingComplete was called, but Iris::onEarlyInitialize was not called."
+ " Trying to avoid a crash but this is an odd state.");
return;
}
// Initialize the pipeline now so that we don't increase world loading time. Just going to guess that
// the player is in the overworld.
// See: https://github.com/IrisShaders/Iris/issues/323
lastDimension = DimensionId.OVERWORLD;
Iris.getPipelineManager().preparePipeline(DimensionId.OVERWORLD);
}
public static void toggleShaders(Minecraft minecraft, boolean enabled) throws IOException {
irisConfig.setShadersEnabled(enabled);
irisConfig.save();
reload();
if (minecraft.thePlayer != null) {
minecraft.thePlayer.addChatMessage(new ChatComponentText(enabled ? I18n.format("iris.shaders.toggled", currentPackName) : I18n.format("iris.shaders.disabled")));
}
}
public static void loadShaderpack() {
if (irisConfig == null) {
if (!initialized) {
throw new IllegalStateException("Iris::loadShaderpack was called, but Iris::onInitializeClient wasn't" + " called yet. How did this happen?");
} else {
throw new NullPointerException("Iris.irisConfig was null unexpectedly");
}
}
if (!irisConfig.areShadersEnabled()) {
logger.info("Shaders are disabled because enableShaders is set to false in shaders.properties");
setShadersDisabled();
return;
}
// Attempt to load an external shaderpack if it is available
final Optional<String> externalName = irisConfig.getShaderPackName();
if (!externalName.isPresent()) {
logger.info("Shaders are disabled because no valid shaderpack is selected");
setShadersDisabled();
return;
}
if (!loadExternalShaderpack(externalName.get())) {
logger.warn("Falling back to normal rendering without shaders because the shaderpack could not be loaded");
setShadersDisabled();
fallback = true;
}
}
private static boolean loadExternalShaderpack(String name) {
final Path shaderPackRoot;
final Path shaderPackConfigTxt;
try {
shaderPackRoot = getShaderpacksDirectory().resolve(name);
shaderPackConfigTxt = getShaderpacksDirectory().resolve(name + ".txt");
} catch (InvalidPathException e) {
logger.error("Failed to load the shaderpack \"{}\" because it contains invalid characters in its path", name);
return false;
}
final Path shaderPackPath;
if (shaderPackRoot.toString().endsWith(".zip")) {
final Optional<Path> optionalPath;
try {
optionalPath = loadExternalZipShaderpack(shaderPackRoot);
} catch (FileSystemNotFoundException | NoSuchFileException e) {
logger.error("Failed to load the shaderpack \"{}\" because it does not exist in your shaderpacks folder!", name);
return false;
} catch (ZipException e) {
logger.error("The shaderpack \"{}\" appears to be corrupted, please try downloading it again!", name);
return false;
} catch (IOException e) {
logger.error("Failed to load the shaderpack \"{}\"!", name);
logger.error("", e);
return false;
}
if (optionalPath.isPresent()) {
shaderPackPath = optionalPath.get();
} else {
logger.error("Could not load the shaderpack \"{}\" because it appears to lack a \"shaders\" directory", name);
return false;
}
} else {
if (!Files.exists(shaderPackRoot)) {
logger.error("Failed to load the shaderpack \"{}\" because it does not exist!", name);
return false;
}
// If it's a folder-based shaderpack, just use the shaders subdirectory
shaderPackPath = shaderPackRoot.resolve("shaders");
}
if (!Files.exists(shaderPackPath)) {
logger.error("Could not load the shaderpack \"{}\" because it appears to lack a \"shaders\" directory", name);
return false;
}
final Map<String, String> changedConfigs = tryReadConfigProperties(shaderPackConfigTxt).map(properties -> (Map<String, String>) (Map) properties)
.orElse(new HashMap<>());
changedConfigs.putAll(shaderPackOptionQueue);
clearShaderPackOptionQueue();
if (resetShaderPackOptions) {
changedConfigs.clear();
}
resetShaderPackOptions = false;
try {
currentPack = new ShaderPack(shaderPackPath, changedConfigs, StandardMacros.createStandardEnvironmentDefines());
final MutableOptionValues changedConfigsValues = currentPack.getShaderPackOptions().getOptionValues().mutableCopy();
// Store changed values from those currently in use by the shader pack
final Properties configsToSave = new Properties();
changedConfigsValues.getBooleanValues().forEach((k, v) -> configsToSave.setProperty(k, Boolean.toString(v)));
changedConfigsValues.getStringValues().forEach(configsToSave::setProperty);
tryUpdateConfigPropertiesFile(shaderPackConfigTxt, configsToSave);
} catch (Exception e) {
logger.error("Failed to load the shaderpack \"{}\"!", name);
logger.error("", e);
return false;
}
fallback = false;
currentPackName = name;
logger.info("Using shaderpack: " + name);
return true;
}
private static Optional<Path> loadExternalZipShaderpack(Path shaderpackPath) throws IOException {
final FileSystem zipSystem = FileSystems.newFileSystem(shaderpackPath, Iris.class.getClassLoader());
zipFileSystem = zipSystem;
// Should only be one root directory for a zip shaderpack
final Path root = zipSystem.getRootDirectories().iterator().next();
final Path potentialShaderDir = zipSystem.getPath("shaders");
// If the shaders dir was immediately found return it
// Otherwise, manually search through each directory path until it ends with "shaders"
if (Files.exists(potentialShaderDir)) {
return Optional.of(potentialShaderDir);
}
// Sometimes shaderpacks have their shaders directory within another folder in the shaderpack
// For example Sildurs-Vibrant-Shaders.zip/shaders
// While other packs have Trippy-Shaderpack-master.zip/Trippy-Shaderpack-master/shaders
// This makes it hard to determine what is the actual shaders dir
try (Stream<Path> stream = Files.walk(root)) {
return stream.filter(Files::isDirectory).filter(path -> path.endsWith("shaders")).findFirst();
}
}
private static void setShadersDisabled() {
currentPack = null;
fallback = false;
currentPackName = "(off)";
logger.info("Shaders are disabled");
}
// Temp escalation
public static void setDebug(boolean enable) {
GLStateManager.assertMainThread();
final int success;
if (enable) {
success = GLDebug.setupDebugMessageCallback();
} else {
success = GLDebug.disableDebugMessages();
}
logger.info("Debug functionality is " + (enable ? "enabled, logging will be more verbose!" : "disabled."));
if (Minecraft.getMinecraft().thePlayer != null) {
Minecraft.getMinecraft().thePlayer.sendChatMessage(I18n.format(success != 0 ? (enable
? "iris.shaders.debug.enabled"
: "iris.shaders.debug.disabled") : "iris.shaders.debug.failure"));
if (success == 2) {
Minecraft.getMinecraft().thePlayer.sendChatMessage(I18n.format("iris.shaders.debug.restart"));
}
}
if(Iris.isInitialized()) {
try {
irisConfig.setDebugEnabled(enable);
irisConfig.save();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static Optional<Properties> tryReadConfigProperties(Path path) {
final Properties properties = new Properties();
if (Files.exists(path)) {
try (InputStream is = Files.newInputStream(path)) {
// NB: config properties are specified to be encoded with ISO-8859-1 by OptiFine,
// so we don't need to do the UTF-8 workaround here.
properties.load(is);
} catch (IOException e) {
// TODO: Better error handling
return Optional.empty();
}
}
return Optional.of(properties);
}
private static void tryUpdateConfigPropertiesFile(Path path, Properties properties) {
try {
if (properties.isEmpty()) {
// Delete the file or don't create it if there are no changed configs
if (Files.exists(path)) {
Files.delete(path);
}
return;
}
try (OutputStream out = Files.newOutputStream(path)) {
properties.store(out, null);
}
} catch (IOException e) {
// TODO: Better error handling
}
}
public static boolean isValidShaderpack(Path pack) {
if (Files.isDirectory(pack)) {
// Sometimes the shaderpack directory itself can be
// identified as a shader pack due to it containing
// folders which contain "shaders" folders, this is
// necessary to check against that
if (pack.equals(getShaderpacksDirectory())) {
return false;
}
try (Stream<Path> stream = Files.walk(pack)) {
return stream.filter(Files::isDirectory)
// Prevent a pack simply named "shaders" from being
// identified as a valid pack
.filter(path -> !path.equals(pack)).anyMatch(path -> path.endsWith("shaders"));
} catch (IOException ignored) {
// ignored, not a valid shader pack.
}
}
if (pack.toString().endsWith(".zip")) {
try (FileSystem zipSystem = FileSystems.newFileSystem(pack, Iris.class.getClassLoader())) {
final Path root = zipSystem.getRootDirectories().iterator().next();
try (Stream<Path> stream = Files.walk(root)) {
return stream.filter(Files::isDirectory).anyMatch(path -> path.endsWith("shaders"));
}
} catch (ZipError zipError) {
// Java 8 seems to throw a ZipError instead of a subclass of IOException
Iris.logger.warn("The ZIP at " + pack + " is corrupt");
} catch (IOException ignored) {
// ignored, not a valid shader pack.
}
}
return false;
}
public static void queueShaderPackOptionsFromProfile(Profile profile) {
getShaderPackOptionQueue().putAll(profile.optionValues);
}
public static void queueShaderPackOptionsFromProperties(Properties properties) {
queueDefaultShaderPackOptionValues();
properties.stringPropertyNames().forEach(key -> getShaderPackOptionQueue().put(key, properties.getProperty(key)));
}
// Used in favor of resetShaderPackOptions as the aforementioned requires the pack to be reloaded
public static void queueDefaultShaderPackOptionValues() {
clearShaderPackOptionQueue();
getCurrentPack().ifPresent(pack -> {
final OptionSet options = pack.getShaderPackOptions().getOptionSet();
final OptionValues values = pack.getShaderPackOptions().getOptionValues();
options.getStringOptions().forEach((key, mOpt) -> {
if (values.getStringValue(key).isPresent()) {
getShaderPackOptionQueue().put(key, mOpt.getOption().getDefaultValue());
}
});
options.getBooleanOptions().forEach((key, mOpt) -> {
if (values.getBooleanValue(key) != OptionalBoolean.DEFAULT) {
getShaderPackOptionQueue().put(key, Boolean.toString(mOpt.getOption().getDefaultValue()));
}
});
});
}
public static void clearShaderPackOptionQueue() {
getShaderPackOptionQueue().clear();
}
public static void resetShaderPackOptionsOnNextReload() {
resetShaderPackOptions = true;
}
public static boolean shouldResetShaderPackOptionsOnNextReload() {
return resetShaderPackOptions;
}
public static void reload() throws IOException {
// allows shaderpacks to be changed at runtime
irisConfig.initialize();
// Destroy all allocated resources
destroyEverything();
// Load the new shaderpack
loadShaderpack();
// Very important - we need to re-create the pipeline straight away.
// https://github.com/IrisShaders/Iris/issues/1330
if (Minecraft.getMinecraft().theWorld != null) {
Iris.getPipelineManager().preparePipeline(Iris.getCurrentDimension());
}
// Reload Sodium/Invalidate Chunk Cache
Minecraft.getMinecraft().renderGlobal.loadRenderers();
}
/**
* Destroys and deallocates all created OpenGL resources. Useful as part of a reload.
*/
private static void destroyEverything() {
currentPack = null;
getPipelineManager().destroyPipeline();
// Close the zip filesystem that the shaderpack was loaded from
//
// This prevents a FileSystemAlreadyExistsException when reloading shaderpacks.
if (zipFileSystem != null) {
try {
zipFileSystem.close();
} catch (NoSuchFileException e) {
logger.warn("Failed to close the shaderpack zip when reloading because it was deleted, proceeding anyways.");
} catch (IOException e) {
logger.error("Failed to close zip file system?", e);
}
}
}
public static DimensionId lastDimension = null;
public static DimensionId getCurrentDimension() {
final WorldClient level = Minecraft.getMinecraft().theWorld;
if (level != null) {
if (level.provider == null) return DimensionId.OVERWORLD;
if ((level.provider.isHellWorld || level.provider.hasNoSky) || level.provider.dimensionId == -1) {
return DimensionId.NETHER;
} else if (level.provider.dimensionId == 1) {
return DimensionId.END;
} else {
return DimensionId.OVERWORLD;
}
} else {
// This prevents us from reloading the shaderpack unless we need to. Otherwise, if the player is in the
// nether and quits the game, we might end up reloading the shaders on exit and on entry to the level
// because the code thinks that the dimension changed.
return lastDimension;
}
}
private static WorldRenderingPipeline createPipeline(DimensionId dimensionId) {
if (currentPack == null) {
// Completely disables shader-based rendering
return new FixedFunctionWorldRenderingPipeline();
}
final ProgramSet programs = currentPack.getProgramSet(dimensionId);
try {
return new DeferredWorldRenderingPipeline(programs);
} catch (Exception e) {
logger.error("Failed to create shader rendering pipeline, disabling shaders!", e);
// TODO: This should be reverted if a dimension change causes shaders to compile again
fallback = true;
return new FixedFunctionWorldRenderingPipeline();
}
}
@NotNull
public static PipelineManager getPipelineManager() {
if (pipelineManager == null) {
pipelineManager = new PipelineManager(Iris::createPipeline);
}
return pipelineManager;
}
@NotNull
public static Optional<ShaderPack> getCurrentPack() {
return Optional.ofNullable(currentPack);
}
public static String getVersion() {
if (IRIS_VERSION == null) {
return "Version info unknown!";
}
return IRIS_VERSION;
}
public static String getFormattedVersion() {
final ChatFormatting color;
String version = getVersion();
if (version.endsWith("-development-environment")) {
color = ChatFormatting.GOLD;
version = version.replace("-development-environment", " (Development Environment)");
} else if (version.endsWith("-dirty") || version.contains("unknown") || version.endsWith("-nogit")) {
color = ChatFormatting.RED;
} else if (version.contains("+rev.")) {
color = ChatFormatting.LIGHT_PURPLE;
} else {
color = ChatFormatting.GREEN;
}
return color + version;
}
public static Path getShaderpacksDirectory() {
if (shaderpacksDirectory == null) {
shaderpacksDirectory = Minecraft.getMinecraft().mcDataDir.toPath().resolve("shaderpacks");
}
return shaderpacksDirectory;
}
public static ShaderpackDirectoryManager getShaderpacksDirectoryManager() {
if (shaderpacksDirectoryManager == null) {
shaderpacksDirectoryManager = new ShaderpackDirectoryManager(getShaderpacksDirectory());
}
return shaderpacksDirectoryManager;
}
public void fmlInitEvent() {
IRIS_VERSION = Tags.VERSION;
reloadKeybind = new KeyBinding("Reload Shaders", 0, "Iris Keybinds");
toggleShadersKeybind = new KeyBinding("Toggle Shaders", 0, "Iris Keybinds");
shaderpackScreenKeybind = new KeyBinding("Shaderpack Selection Screen", 0, "Iris Keybinds");
ClientRegistry.registerKeyBinding(reloadKeybind);
ClientRegistry.registerKeyBinding(toggleShadersKeybind);
ClientRegistry.registerKeyBinding(shaderpackScreenKeybind);
}
}