mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-05 17:54:59 +01:00
Fix crash on resource reload
- Properly delete MaterialManagers and ShaderContexts - Reload renderers on resource reload - More utility methods in WorldAttached
This commit is contained in:
parent
a21377b9c6
commit
5c1f186264
13 changed files with 127 additions and 56 deletions
|
@ -24,6 +24,8 @@ import net.minecraft.util.math.vector.Matrix4f;
|
||||||
import net.minecraft.world.IWorld;
|
import net.minecraft.world.IWorld;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public class Backend {
|
public class Backend {
|
||||||
public static final Logger log = LogManager.getLogger(Backend.class);
|
public static final Logger log = LogManager.getLogger(Backend.class);
|
||||||
|
|
||||||
|
@ -175,8 +177,14 @@ public class Backend {
|
||||||
/**
|
/**
|
||||||
* Used to avoid calling Flywheel functions on (fake) worlds that don't specifically support it.
|
* Used to avoid calling Flywheel functions on (fake) worlds that don't specifically support it.
|
||||||
*/
|
*/
|
||||||
public static boolean isFlywheelWorld(IWorld world) {
|
public static boolean isFlywheelWorld(@Nullable IWorld world) {
|
||||||
return (world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel()) || world == Minecraft.getInstance().world;
|
if (world == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return world == Minecraft.getInstance().world;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isGameActive() {
|
public static boolean isGameActive() {
|
||||||
|
|
|
@ -58,6 +58,7 @@ public abstract class ShaderContext<P extends GlProgram> implements IShaderConte
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void delete() {
|
||||||
programs.values().forEach(IMultiProgram::delete);
|
programs.values().forEach(IMultiProgram::delete);
|
||||||
|
programs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,6 +19,15 @@ import java.util.stream.Stream;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||||
|
import com.jozufozu.flywheel.event.ForgeEvents;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
|
||||||
|
import net.minecraft.client.world.ClientWorld;
|
||||||
|
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
@ -89,9 +98,14 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
Backend.log.info("Loaded all shader programs.");
|
Backend.log.info("Loaded all shader programs.");
|
||||||
|
|
||||||
// no need to hog all that memory
|
// no need to hog all that memory
|
||||||
shaderSource.clear();
|
shaderSource.clear();
|
||||||
|
|
||||||
|
ClientWorld world = Minecraft.getInstance().world;
|
||||||
|
if (Backend.isFlywheelWorld(world)) {
|
||||||
|
InstancedRenderDispatcher.loadAllInWorld(world);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,9 @@ public abstract class GlObject {
|
||||||
|
|
||||||
protected final void checkHandle() {
|
protected final void checkHandle() {
|
||||||
if (!this.isHandleValid()) {
|
if (!this.isHandleValid()) {
|
||||||
throw new IllegalStateException("Handle is not valid");
|
String descriptor = getDescriptor();
|
||||||
|
String message = (descriptor == null ? "" : (descriptor + " ")) + "handle is not valid.";
|
||||||
|
throw new IllegalStateException(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +34,9 @@ public abstract class GlObject {
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
if (!isHandleValid()) {
|
if (!isHandleValid()) {
|
||||||
throw new IllegalStateException("Handle already deleted.");
|
String descriptor = getDescriptor();
|
||||||
|
String message = (descriptor == null ? "" : (descriptor + " ")) + "handle already deleted.";
|
||||||
|
throw new IllegalStateException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteInternal(handle);
|
deleteInternal(handle);
|
||||||
|
@ -40,4 +44,8 @@ public abstract class GlObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void deleteInternal(int handle);
|
protected abstract void deleteInternal(int handle);
|
||||||
|
|
||||||
|
protected String getDescriptor() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,4 +74,8 @@ public abstract class GlProgram extends GlObject {
|
||||||
glDeleteProgram(handle);
|
glDeleteProgram(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "program " + name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public abstract class InstanceManager<T> implements MaterialManager.OriginShiftL
|
||||||
this.dynamicInstances = new Object2ObjectOpenHashMap<>();
|
this.dynamicInstances = new Object2ObjectOpenHashMap<>();
|
||||||
this.tickableInstances = new Object2ObjectOpenHashMap<>();
|
this.tickableInstances = new Object2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
materialManager.onOriginShift(this);
|
materialManager.addListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tick(double cameraX, double cameraY, double cameraZ) {
|
public void tick(double cameraX, double cameraY, double cameraZ) {
|
||||||
|
|
|
@ -40,7 +40,6 @@ import net.minecraft.util.LazyValue;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.vector.Matrix4f;
|
import net.minecraft.util.math.vector.Matrix4f;
|
||||||
import net.minecraft.world.IWorld;
|
import net.minecraft.world.IWorld;
|
||||||
import net.minecraft.world.World;
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
import net.minecraftforge.event.TickEvent;
|
import net.minecraftforge.event.TickEvent;
|
||||||
|
@ -124,15 +123,7 @@ public class InstancedRenderDispatcher {
|
||||||
public static void onReloadRenderers(ReloadRenderersEvent event) {
|
public static void onReloadRenderers(ReloadRenderersEvent event) {
|
||||||
ClientWorld world = event.getWorld();
|
ClientWorld world = event.getWorld();
|
||||||
if (Backend.getInstance().canUseInstancing() && world != null) {
|
if (Backend.getInstance().canUseInstancing() && world != null) {
|
||||||
Contexts.WORLD.getMaterialManager(world).delete();
|
loadAllInWorld(world);
|
||||||
|
|
||||||
TileInstanceManager tiles = getTiles(world);
|
|
||||||
tiles.invalidate();
|
|
||||||
world.loadedTileEntityList.forEach(tiles::add);
|
|
||||||
|
|
||||||
EntityInstanceManager entities = getEntities(world);
|
|
||||||
entities.invalidate();
|
|
||||||
world.getAllEntities().forEach(entities::add);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,4 +179,14 @@ public class InstancedRenderDispatcher {
|
||||||
if (breaking != null)
|
if (breaking != null)
|
||||||
glBindTexture(GL_TEXTURE_2D, breaking.getGlTextureId());
|
glBindTexture(GL_TEXTURE_2D, breaking.getGlTextureId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void loadAllInWorld(ClientWorld world) {
|
||||||
|
Contexts.WORLD.getMaterialManager(world).delete();
|
||||||
|
|
||||||
|
TileInstanceManager tiles = tileInstanceManager.replace(world);
|
||||||
|
world.loadedTileEntityList.forEach(tiles::add);
|
||||||
|
|
||||||
|
EntityInstanceManager entities = entityInstanceManager.replace(world);
|
||||||
|
world.getAllEntities().forEach(entities::add);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,8 +91,11 @@ public class MaterialManager<P extends WorldProgram> {
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
atlasMaterials.values().forEach(InstanceMaterial::delete);
|
atlasMaterials.values().forEach(InstanceMaterial::delete);
|
||||||
|
|
||||||
materials.values().stream().flatMap(m -> m.values().stream()).forEach(InstanceMaterial::delete);
|
materials.values().stream().flatMap(m -> m.values().stream()).forEach(InstanceMaterial::delete);
|
||||||
|
|
||||||
|
atlasMaterials.clear();
|
||||||
|
atlasRenderers.clear();
|
||||||
|
materials.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -131,7 +134,7 @@ public class MaterialManager<P extends WorldProgram> {
|
||||||
return originCoordinate;
|
return originCoordinate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onOriginShift(OriginShiftListener listener) {
|
public void addListener(OriginShiftListener listener) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
@ParametersAreNonnullByDefault
|
||||||
|
@MethodsReturnNonnullByDefault
|
||||||
|
package com.jozufozu.flywheel.backend;
|
||||||
|
|
||||||
|
import mcp.MethodsReturnNonnullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.ParametersAreNonnullByDefault;
|
|
@ -21,7 +21,6 @@ import com.jozufozu.flywheel.backend.loading.Shader;
|
||||||
import com.jozufozu.flywheel.backend.loading.ShaderLoadingException;
|
import com.jozufozu.flywheel.backend.loading.ShaderLoadingException;
|
||||||
import com.jozufozu.flywheel.backend.loading.ShaderTransformer;
|
import com.jozufozu.flywheel.backend.loading.ShaderTransformer;
|
||||||
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
||||||
import com.jozufozu.flywheel.core.shader.IMultiProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.StateSensitiveMultiProgram;
|
import com.jozufozu.flywheel.core.shader.StateSensitiveMultiProgram;
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||||
import com.jozufozu.flywheel.util.WorldAttached;
|
import com.jozufozu.flywheel.util.WorldAttached;
|
||||||
|
@ -38,7 +37,7 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
||||||
protected Supplier<Stream<ResourceLocation>> specStream;
|
protected Supplier<Stream<ResourceLocation>> specStream;
|
||||||
protected TemplateFactory templateFactory;
|
protected TemplateFactory templateFactory;
|
||||||
|
|
||||||
private final WorldAttached<MaterialManager<P>> materialManager = new WorldAttached<>($ -> new MaterialManager<>(this));
|
private final WorldAttached<MaterialManager<P>> worldAttachedMMs = new WorldAttached<>($ -> new MaterialManager<>(this));
|
||||||
|
|
||||||
private final Map<ShaderType, ResourceLocation> builtins = new EnumMap<>(ShaderType.class);
|
private final Map<ShaderType, ResourceLocation> builtins = new EnumMap<>(ShaderType.class);
|
||||||
private final Map<ShaderType, String> builtinSources = new EnumMap<>(ShaderType.class);
|
private final Map<ShaderType, String> builtinSources = new EnumMap<>(ShaderType.class);
|
||||||
|
@ -71,7 +70,7 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public MaterialManager<P> getMaterialManager(IWorld world) {
|
public MaterialManager<P> getMaterialManager(IWorld world) {
|
||||||
return materialManager.get(world);
|
return worldAttachedMMs.get(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldContext<P> withSpecStream(Supplier<Stream<ResourceLocation>> specStream) {
|
public WorldContext<P> withSpecStream(Supplier<Stream<ResourceLocation>> specStream) {
|
||||||
|
@ -89,8 +88,6 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load() {
|
public void load() {
|
||||||
programs.values().forEach(IMultiProgram::delete);
|
|
||||||
programs.clear();
|
|
||||||
|
|
||||||
Backend.log.info("Loading context '{}'", name);
|
Backend.log.info("Loading context '{}'", name);
|
||||||
|
|
||||||
|
@ -130,7 +127,7 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
||||||
public void delete() {
|
public void delete() {
|
||||||
super.delete();
|
super.delete();
|
||||||
|
|
||||||
materialManager.forEach(MaterialManager::delete);
|
worldAttachedMMs.empty(MaterialManager::delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -49,10 +49,19 @@ public class ExtensibleGlProgram extends GlProgram {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ExtensibleGlProgram{" +
|
StringBuilder builder = new StringBuilder();
|
||||||
"name=" + name +
|
builder.append("program ")
|
||||||
", extensions=" + extensions +
|
.append(name)
|
||||||
'}';
|
.append('[');
|
||||||
|
|
||||||
|
for (IExtensionInstance extension : extensions) {
|
||||||
|
builder.append(extension)
|
||||||
|
.append('+');
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(']');
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,8 +4,6 @@ import java.util.ArrayList;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.world.ClientWorld;
|
import net.minecraft.client.world.ClientWorld;
|
||||||
|
@ -42,15 +40,8 @@ public class ForgeEvents {
|
||||||
IWorld world = event.getWorld();
|
IWorld world = event.getWorld();
|
||||||
|
|
||||||
if (Backend.isFlywheelWorld(world)) {
|
if (Backend.isFlywheelWorld(world)) {
|
||||||
ClientWorld clientWorld = (ClientWorld) world;
|
InstancedRenderDispatcher.loadAllInWorld((ClientWorld) world);
|
||||||
|
|
||||||
TileInstanceManager tiles = InstancedRenderDispatcher.getTiles(world);
|
|
||||||
tiles.invalidate();
|
|
||||||
clientWorld.loadedTileEntityList.forEach(tiles::add);
|
|
||||||
|
|
||||||
EntityInstanceManager entities = InstancedRenderDispatcher.getEntities(world);
|
|
||||||
entities.invalidate();
|
|
||||||
clientWorld.getAllEntities().forEach(entities::add);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,21 @@ package com.jozufozu.flywheel.util;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import net.minecraft.world.IWorld;
|
import net.minecraft.world.IWorld;
|
||||||
|
|
||||||
public class WorldAttached<T> {
|
public class WorldAttached<T> {
|
||||||
|
|
||||||
Map<IWorld, T> attached;
|
Map<IWorld, T> attached;
|
||||||
private final WorldAttacher<T> factory;
|
private final Function<IWorld, T> factory;
|
||||||
|
|
||||||
public WorldAttached(WorldAttacher<T> factory) {
|
public WorldAttached(Function<IWorld, T> factory) {
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
attached = new HashMap<>();
|
attached = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
@ -24,7 +26,7 @@ public class WorldAttached<T> {
|
||||||
T t = attached.get(world);
|
T t = attached.get(world);
|
||||||
if (t != null)
|
if (t != null)
|
||||||
return t;
|
return t;
|
||||||
T entry = factory.attach(world);
|
T entry = factory.apply(world);
|
||||||
put(world, entry);
|
put(world, entry);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
@ -33,20 +35,46 @@ public class WorldAttached<T> {
|
||||||
attached.put(world, entry);
|
attached.put(world, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forEach(Consumer<T> consumer) {
|
/**
|
||||||
|
* Replaces the entry with a new one from the factory and returns the new entry.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public T replace(IWorld world) {
|
||||||
|
attached.remove(world);
|
||||||
|
|
||||||
|
return get(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the entry with a new one from the factory and returns the new entry.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public T replace(IWorld world, Consumer<T> finalizer) {
|
||||||
|
T remove = attached.remove(world);
|
||||||
|
|
||||||
|
finalizer.accept(remove);
|
||||||
|
|
||||||
|
return get(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all entries after calling a function on them.
|
||||||
|
*
|
||||||
|
* @param finalizer Do something with all of the world-value pairs
|
||||||
|
*/
|
||||||
|
public void empty(BiConsumer<IWorld, T> finalizer) {
|
||||||
|
attached.forEach(finalizer);
|
||||||
|
attached.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all entries after calling a function on them.
|
||||||
|
*
|
||||||
|
* @param finalizer Do something with all of the values
|
||||||
|
*/
|
||||||
|
public void empty(Consumer<T> finalizer) {
|
||||||
attached.values()
|
attached.values()
|
||||||
.forEach(consumer);
|
.forEach(finalizer);
|
||||||
|
attached.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface WorldAttacher<T> extends Function<IWorld, T> {
|
|
||||||
@Nonnull
|
|
||||||
T attach(IWorld world);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default T apply(IWorld world) {
|
|
||||||
return attach(world);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue