mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-28 16:06:28 +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.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class Backend {
|
||||
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.
|
||||
*/
|
||||
public static boolean isFlywheelWorld(IWorld world) {
|
||||
return (world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel()) || world == Minecraft.getInstance().world;
|
||||
public static boolean isFlywheelWorld(@Nullable IWorld world) {
|
||||
if (world == null)
|
||||
return false;
|
||||
|
||||
if (world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel())
|
||||
return true;
|
||||
|
||||
return world == Minecraft.getInstance().world;
|
||||
}
|
||||
|
||||
public static boolean isGameActive() {
|
||||
|
|
|
@ -58,6 +58,7 @@ public abstract class ShaderContext<P extends GlProgram> implements IShaderConte
|
|||
@Override
|
||||
public void delete() {
|
||||
programs.values().forEach(IMultiProgram::delete);
|
||||
programs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,15 @@ import java.util.stream.Stream;
|
|||
import javax.annotation.Nonnull;
|
||||
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 com.google.common.collect.Lists;
|
||||
|
@ -89,9 +98,14 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
|||
}
|
||||
|
||||
Backend.log.info("Loaded all shader programs.");
|
||||
|
||||
|
||||
// no need to hog all that memory
|
||||
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() {
|
||||
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() {
|
||||
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);
|
||||
|
@ -40,4 +44,8 @@ public abstract class GlObject {
|
|||
}
|
||||
|
||||
protected abstract void deleteInternal(int handle);
|
||||
|
||||
protected String getDescriptor() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,4 +74,8 @@ public abstract class GlProgram extends GlObject {
|
|||
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.tickableInstances = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
materialManager.onOriginShift(this);
|
||||
materialManager.addListener(this);
|
||||
}
|
||||
|
||||
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.vector.Matrix4f;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
|
@ -124,15 +123,7 @@ public class InstancedRenderDispatcher {
|
|||
public static void onReloadRenderers(ReloadRenderersEvent event) {
|
||||
ClientWorld world = event.getWorld();
|
||||
if (Backend.getInstance().canUseInstancing() && world != null) {
|
||||
Contexts.WORLD.getMaterialManager(world).delete();
|
||||
|
||||
TileInstanceManager tiles = getTiles(world);
|
||||
tiles.invalidate();
|
||||
world.loadedTileEntityList.forEach(tiles::add);
|
||||
|
||||
EntityInstanceManager entities = getEntities(world);
|
||||
entities.invalidate();
|
||||
world.getAllEntities().forEach(entities::add);
|
||||
loadAllInWorld(world);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,4 +179,14 @@ public class InstancedRenderDispatcher {
|
|||
if (breaking != null)
|
||||
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() {
|
||||
atlasMaterials.values().forEach(InstanceMaterial::delete);
|
||||
|
||||
materials.values().stream().flatMap(m -> m.values().stream()).forEach(InstanceMaterial::delete);
|
||||
|
||||
atlasMaterials.clear();
|
||||
atlasRenderers.clear();
|
||||
materials.clear();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -131,7 +134,7 @@ public class MaterialManager<P extends WorldProgram> {
|
|||
return originCoordinate;
|
||||
}
|
||||
|
||||
public void onOriginShift(OriginShiftListener listener) {
|
||||
public void addListener(OriginShiftListener 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.ShaderTransformer;
|
||||
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.WorldProgram;
|
||||
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 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, 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) {
|
||||
return materialManager.get(world);
|
||||
return worldAttachedMMs.get(world);
|
||||
}
|
||||
|
||||
public WorldContext<P> withSpecStream(Supplier<Stream<ResourceLocation>> specStream) {
|
||||
|
@ -89,8 +88,6 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
|||
|
||||
@Override
|
||||
public void load() {
|
||||
programs.values().forEach(IMultiProgram::delete);
|
||||
programs.clear();
|
||||
|
||||
Backend.log.info("Loading context '{}'", name);
|
||||
|
||||
|
@ -130,7 +127,7 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
|||
public void delete() {
|
||||
super.delete();
|
||||
|
||||
materialManager.forEach(MaterialManager::delete);
|
||||
worldAttachedMMs.empty(MaterialManager::delete);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -49,10 +49,19 @@ public class ExtensibleGlProgram extends GlProgram {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExtensibleGlProgram{" +
|
||||
"name=" + name +
|
||||
", extensions=" + extensions +
|
||||
'}';
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("program ")
|
||||
.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.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.world.ClientWorld;
|
||||
|
@ -42,15 +40,8 @@ public class ForgeEvents {
|
|||
IWorld world = event.getWorld();
|
||||
|
||||
if (Backend.isFlywheelWorld(world)) {
|
||||
ClientWorld clientWorld = (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);
|
||||
InstancedRenderDispatcher.loadAllInWorld((ClientWorld) world);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,19 +2,21 @@ package com.jozufozu.flywheel.util;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.world.IWorld;
|
||||
|
||||
public class WorldAttached<T> {
|
||||
|
||||
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;
|
||||
attached = new HashMap<>();
|
||||
}
|
||||
|
@ -24,7 +26,7 @@ public class WorldAttached<T> {
|
|||
T t = attached.get(world);
|
||||
if (t != null)
|
||||
return t;
|
||||
T entry = factory.attach(world);
|
||||
T entry = factory.apply(world);
|
||||
put(world, entry);
|
||||
return entry;
|
||||
}
|
||||
|
@ -33,20 +35,46 @@ public class WorldAttached<T> {
|
|||
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()
|
||||
.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