Re-reload

- Rename ReloadRenderersEvent to ReloadLevelRendererEvent
- Rename Engine#renderCrumblingInstances to #renderCrumbling
- Make all mixin classes package-private and abstract
- Make all event classes final and document which event bus they are
posted on
- Add EndClientResourceReloadEvent
  - Replace some usages of ReloadLevelRendererEvent with
EndClientResourceReloadEvent, including ModelHolder and ModelCache
- Always add all existing entities from world to
VisualizationManagerImpl on construction if level is instanceof Level
- Delete all VisualizationManagerImpls on resource reload
- Improve MemoryBlock utility
  - Add MemoryBlock#copyTo(MemoryBlock)
  - Remove MemoryBlock#reallocTracked and make #realloc create a tracked
block if and only if the existing block is tracked
  - Fix reallocating a debug memory block creating a regular memory
block
- Change BakedModelBufferer to only invoke the result consumer if the
data is not empty
- Improve BackendArgument
  - Fix classloading BackendArgument early causing it to return
incomplete suggestions
  - Always allow specifying namespace, allow matching only by path, and
always display suggested IDs with namespace
This commit is contained in:
PepperCode1 2023-11-29 20:03:26 -08:00
parent 7bae0f4d8c
commit f72abf8e1d
60 changed files with 501 additions and 372 deletions

View file

@ -5,10 +5,10 @@ import java.util.ArrayList;
import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.slf4j.Logger; import org.slf4j.Logger;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent; import com.jozufozu.flywheel.api.event.EndClientResourceReloadEvent;
import com.jozufozu.flywheel.api.visualization.VisualizationManager; import com.jozufozu.flywheel.api.visualization.VisualizationManager;
import com.jozufozu.flywheel.backend.Backends; import com.jozufozu.flywheel.backend.Backends;
import com.jozufozu.flywheel.backend.Loader; import com.jozufozu.flywheel.backend.compile.FlwPrograms;
import com.jozufozu.flywheel.backend.compile.Pipelines; import com.jozufozu.flywheel.backend.compile.Pipelines;
import com.jozufozu.flywheel.backend.engine.UniformBuffer; import com.jozufozu.flywheel.backend.engine.UniformBuffer;
import com.jozufozu.flywheel.backend.engine.batching.DrawBuffer; import com.jozufozu.flywheel.backend.engine.batching.DrawBuffer;
@ -70,7 +70,7 @@ public class Flywheel {
IEventBus forgeEventBus = MinecraftForge.EVENT_BUS; IEventBus forgeEventBus = MinecraftForge.EVENT_BUS;
IEventBus modEventBus = FMLJavaModLoadingContext.get() IEventBus modEventBus = FMLJavaModLoadingContext.get()
.getModEventBus(); .getModEventBus();
modEventBus.addListener(Flywheel::setup); modEventBus.addListener(Flywheel::onCommonSetup);
FlwConfig.get().registerSpecs(modLoadingContext); FlwConfig.get().registerSpecs(modLoadingContext);
@ -85,7 +85,7 @@ public class Flywheel {
private static void clientInit(IEventBus forgeEventBus, IEventBus modEventBus) { private static void clientInit(IEventBus forgeEventBus, IEventBus modEventBus) {
forgeEventBus.addListener(Flywheel::addDebugInfo); forgeEventBus.addListener(Flywheel::addDebugInfo);
forgeEventBus.addListener(BackendManagerImpl::onReloadRenderers); forgeEventBus.addListener(BackendManagerImpl::onReloadLevelRenderer);
forgeEventBus.addListener(VisualizationEventHandler::onClientTick); forgeEventBus.addListener(VisualizationEventHandler::onClientTick);
forgeEventBus.addListener(VisualizationEventHandler::onBeginFrame); forgeEventBus.addListener(VisualizationEventHandler::onBeginFrame);
@ -96,14 +96,17 @@ public class Flywheel {
forgeEventBus.addListener(FlwCommands::registerClientCommands); forgeEventBus.addListener(FlwCommands::registerClientCommands);
forgeEventBus.addListener(DrawBuffer::onReloadRenderers); forgeEventBus.addListener(DrawBuffer::onReloadRenderers);
forgeEventBus.addListener(UniformBuffer::onReloadRenderers); forgeEventBus.addListener(UniformBuffer::onReloadLevelRenderer);
forgeEventBus.addListener(LightUpdater::onClientTick); forgeEventBus.addListener(LightUpdater::onClientTick);
forgeEventBus.addListener((ReloadRenderersEvent e) -> ModelCache.onReloadRenderers(e));
forgeEventBus.addListener(ModelHolder::onReloadRenderers);
forgeEventBus.addListener((LevelEvent.Unload e) -> LevelAttached.onUnloadLevel(e)); forgeEventBus.addListener((LevelEvent.Unload e) -> LevelAttached.onUnloadLevel(e));
modEventBus.addListener(Flywheel::lateInit); modEventBus.addListener(Flywheel::onClientSetup);
modEventBus.addListener(BackendManagerImpl::onEndClientResourceReload);
modEventBus.addListener((EndClientResourceReloadEvent e) -> ModelCache.onEndClientResourceReload(e));
modEventBus.addListener(ModelHolder::onEndClientResourceReload);
modEventBus.addListener(PartialModel::onModelRegistry); modEventBus.addListener(PartialModel::onModelRegistry);
modEventBus.addListener(PartialModel::onModelBake); modEventBus.addListener(PartialModel::onModelBake);
@ -115,12 +118,12 @@ public class Flywheel {
Pipelines.init(); Pipelines.init();
Backends.init(); Backends.init();
Loader.init(); FlwPrograms.ResourceReloadListener.register();
ShadersModHandler.init(); ShadersModHandler.init();
} }
private static void lateInit(final FMLClientSetupEvent event) { private static void onClientSetup(FMLClientSetupEvent event) {
VertexTypes.init(); VertexTypes.init();
InstanceTypes.init(); InstanceTypes.init();
Materials.init(); Materials.init();
@ -134,7 +137,8 @@ public class Flywheel {
IdRegistryImpl.freezeAll(); IdRegistryImpl.freezeAll();
} }
private static void setup(final FMLCommonSetupEvent event) { private static void onCommonSetup(FMLCommonSetupEvent event) {
// FIXME: argument types also need to be registered to BuiltInRegistries.COMMAND_ARGUMENT_TYPE
ArgumentTypeInfos.registerByClass(BackendArgument.class, SingletonArgumentInfo.contextFree(() -> BackendArgument.INSTANCE)); ArgumentTypeInfos.registerByClass(BackendArgument.class, SingletonArgumentInfo.contextFree(() -> BackendArgument.INSTANCE));
} }

View file

@ -31,13 +31,13 @@ public interface Engine extends InstancerProvider {
/** /**
* Render the given instances as a crumbling overlay. * Render the given instances as a crumbling overlay.
* <br> * <br>
* This is guaranteed to be called between the first and last calls to {@link #renderStage}. * This is guaranteed to be called between the first and last calls to {@link #renderStage} for the current frame.
* *
* @param executor The task executor running the frame plan. * @param executor The task executor running the frame plan.
* @param context The render context for this frame. * @param context The render context for this frame.
* @param crumblingBlocks The instances to render. * @param crumblingBlocks The instances to render. This list is never empty.
*/ */
void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks); void renderCrumbling(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks);
/** /**
* Maintain the render origin to be within a certain distance from the camera in all directions, * Maintain the render origin to be within a certain distance from the camera in all directions,

View file

@ -2,7 +2,10 @@ package com.jozufozu.flywheel.api.event;
import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.Event;
public class BeginFrameEvent extends Event { /**
* This event is posted to the Forge event bus.
*/
public final class BeginFrameEvent extends Event {
private final RenderContext context; private final RenderContext context;
public BeginFrameEvent(RenderContext context) { public BeginFrameEvent(RenderContext context) {

View file

@ -0,0 +1,42 @@
package com.jozufozu.flywheel.api.event;
import java.util.Optional;
import net.minecraft.client.Minecraft;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.IModBusEvent;
/**
* This event is posted to mod event buses.
*/
// TODO: This should not be a mod bus event. However, currently, it cannot be a Forge bus event since that bus is not started by the time this event needs to be posted.
public final class EndClientResourceReloadEvent extends Event implements IModBusEvent {
private final Minecraft minecraft;
private final ResourceManager resourceManager;
private final boolean initialReload;
private final Optional<Throwable> error;
public EndClientResourceReloadEvent(Minecraft minecraft, ResourceManager resourceManager, boolean initialReload, Optional<Throwable> error) {
this.minecraft = minecraft;
this.resourceManager = resourceManager;
this.initialReload = initialReload;
this.error = error;
}
public Minecraft getMinecraft() {
return minecraft;
}
public ResourceManager getResourceManager() {
return resourceManager;
}
public boolean isInitialReload() {
return initialReload;
}
public Optional<Throwable> getError() {
return error;
}
}

View file

@ -5,10 +5,14 @@ import org.jetbrains.annotations.Nullable;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.Event;
public class ReloadRenderersEvent extends Event { /**
* This event is posted to the Forge event bus.
*/
public final class ReloadLevelRendererEvent extends Event {
@Nullable
private final ClientLevel level; private final ClientLevel level;
public ReloadRenderersEvent(ClientLevel level) { public ReloadLevelRendererEvent(@Nullable ClientLevel level) {
this.level = level; this.level = level;
} }

View file

@ -9,7 +9,10 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.RenderBuffers; import net.minecraft.client.renderer.RenderBuffers;
import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.Event;
public class RenderStageEvent extends Event { /**
* This event is posted to the Forge event bus.
*/
public final class RenderStageEvent extends Event {
private final RenderContext context; private final RenderContext context;
private final RenderStage stage; private final RenderStage stage;

View file

@ -1,46 +0,0 @@
package com.jozufozu.flywheel.backend;
import com.jozufozu.flywheel.backend.compile.FlwPrograms;
import com.jozufozu.flywheel.impl.BackendManagerImpl;
import net.minecraft.client.Minecraft;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
/**
* The main entity for loading shaders.
*
* <p>
* This class is responsible for invoking the loading, parsing, and compilation stages.
* </p>
*/
public class Loader implements ResourceManagerReloadListener {
public static final Loader INSTANCE = new Loader();
private Loader() {
}
@Override
public void onResourceManagerReload(ResourceManager manager) {
FlwPrograms.reload(manager);
// TODO: Move this to the impl package
// TODO: To ensure this runs after all backends are ready, inject into Minecraft after the reload and before levelRenderer.allChanged()
// Alternatively, consider adding API
// TODO: This should reset all VisualizationManagerImpls, not just the one for the static client level
BackendManagerImpl.refresh(Minecraft.getInstance().level);
}
public static void init() {
// Can be null when running data generators due to the unfortunate time we call this
Minecraft minecraft = Minecraft.getInstance();
if (minecraft == null) {
return;
}
if (minecraft.getResourceManager() instanceof ReloadableResourceManager reloadable) {
reloadable.registerReloadListener(INSTANCE);
}
}
}

View file

@ -14,7 +14,10 @@ import com.jozufozu.flywheel.glsl.generate.FnSignature;
import com.jozufozu.flywheel.glsl.generate.GlslExpr; import com.jozufozu.flywheel.glsl.generate.GlslExpr;
import com.jozufozu.flywheel.lib.material.MaterialIndices; import com.jozufozu.flywheel.lib.material.MaterialIndices;
import net.minecraft.client.Minecraft;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
public class FlwPrograms { public class FlwPrograms {
private FlwPrograms() { private FlwPrograms() {
@ -86,4 +89,28 @@ public class FlwPrograms {
} }
return builder.build(); return builder.build();
} }
public static class ResourceReloadListener implements ResourceManagerReloadListener {
public static final ResourceReloadListener INSTANCE = new ResourceReloadListener();
private ResourceReloadListener() {
}
@Override
public void onResourceManagerReload(ResourceManager manager) {
FlwPrograms.reload(manager);
}
public static void register() {
// Can be null when running data generators due to the unfortunate time we call this
Minecraft minecraft = Minecraft.getInstance();
if (minecraft == null) {
return;
}
if (minecraft.getResourceManager() instanceof ReloadableResourceManager reloadable) {
reloadable.registerReloadListener(INSTANCE);
}
}
}
} }

View file

@ -104,12 +104,11 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
deleted.clear(); deleted.clear();
} }
public void delete() {
}
@Override @Override
public String toString() { public String toString() {
return "AbstractInstancer[" + getInstanceCount() + ']'; return "AbstractInstancer[" + getInstanceCount() + ']';
} }
public void delete() {
}
} }

View file

@ -33,7 +33,6 @@ public abstract class InstancerStorage<N extends AbstractInstancer<?>> {
*/ */
private final Object creationLock = new Object(); private final Object creationLock = new Object();
/** /**
* A list of initialized instancers. * A list of initialized instancers.
* <br> * <br>

View file

@ -6,7 +6,7 @@ import java.util.Set;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent; import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.uniform.ShaderUniforms; import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
import com.jozufozu.flywheel.gl.buffer.GlBuffer; import com.jozufozu.flywheel.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.gl.shader.GlProgram; import com.jozufozu.flywheel.gl.shader.GlProgram;
@ -66,7 +66,7 @@ public class UniformBuffer {
buffer.delete(); buffer.delete();
} }
public static void onReloadRenderers(ReloadRenderersEvent event) { public static void onReloadLevelRenderer(ReloadLevelRendererEvent event) {
if (instance != null) { if (instance != null) {
instance.delete(); instance.delete();
instance = null; instance = null;

View file

@ -44,7 +44,7 @@ public class BatchingEngine extends AbstractEngine {
} }
@Override @Override
public void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) { public void renderCrumbling(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) {
executor.syncUntil(flushFlag::isRaised); executor.syncUntil(flushFlag::isRaised);
var batchContext = BatchContext.create(context, this.renderOrigin); var batchContext = BatchContext.create(context, this.renderOrigin);

View file

@ -4,7 +4,7 @@ import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent; import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.event.RenderStage; import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList; import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexListProvider; import com.jozufozu.flywheel.api.vertex.VertexListProvider;
@ -172,7 +172,7 @@ public class DrawBuffer {
buffer = null; buffer = null;
} }
public static void onReloadRenderers(ReloadRenderersEvent event) { public static void onReloadRenderers(ReloadLevelRendererEvent event) {
ALL.forEach(DrawBuffer::free); ALL.forEach(DrawBuffer::free);
} }

View file

@ -15,31 +15,6 @@ import com.jozufozu.flywheel.lib.util.Pair;
public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>> { public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>> {
public final Map<Pair<InstanceType<?>, VertexType>, IndirectCullingGroup<?>> renderLists = new HashMap<>(); public final Map<Pair<InstanceType<?>, VertexType>, IndirectCullingGroup<?>> renderLists = new HashMap<>();
public void flush() {
super.flush();
for (IndirectCullingGroup<?> value : renderLists.values()) {
value.beginFrame();
}
}
public void invalidate() {
super.invalidate();
renderLists.values()
.forEach(IndirectCullingGroup::delete);
renderLists.clear();
}
public boolean hasStage(RenderStage stage) {
for (var list : renderLists.values()) {
if (list.hasStage(stage)) {
return true;
}
}
return false;
}
@Override @Override
protected <I extends Instance> IndirectInstancer<?> create(InstanceType<I> type) { protected <I extends Instance> IndirectInstancer<?> create(InstanceType<I> type) {
return new IndirectInstancer<>(type); return new IndirectInstancer<>(type);
@ -59,4 +34,31 @@ public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>>
break; // TODO: support multiple meshes per model break; // TODO: support multiple meshes per model
} }
} }
public boolean hasStage(RenderStage stage) {
for (var list : renderLists.values()) {
if (list.hasStage(stage)) {
return true;
}
}
return false;
}
@Override
public void flush() {
super.flush();
for (IndirectCullingGroup<?> value : renderLists.values()) {
value.beginFrame();
}
}
@Override
public void invalidate() {
super.invalidate();
renderLists.values()
.forEach(IndirectCullingGroup::delete);
renderLists.clear();
}
} }

View file

@ -61,7 +61,7 @@ public class IndirectEngine extends AbstractEngine {
} }
@Override @Override
public void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) { public void renderCrumbling(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) {
// TODO: implement // TODO: implement
} }

View file

@ -21,7 +21,6 @@ import com.jozufozu.flywheel.backend.engine.InstancerKey;
import com.jozufozu.flywheel.backend.engine.InstancerStorage; import com.jozufozu.flywheel.backend.engine.InstancerStorage;
public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>> { public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>> {
/** /**
* The set of draw calls to make in each {@link RenderStage}. * The set of draw calls to make in each {@link RenderStage}.
*/ */

View file

@ -79,7 +79,7 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
changed.clear(); changed.clear();
} catch (Exception e) { } catch (Exception e) {
Flywheel.LOGGER.error("Error updating GPUInstancer:", e); Flywheel.LOGGER.error("Error updating InstancedInstancer:", e);
} }
} }
@ -111,6 +111,7 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
vao.bindAttributes(1, startAttrib, instanceFormat.attributes()); vao.bindAttributes(1, startAttrib, instanceFormat.attributes());
} }
@Override
public void delete() { public void delete() {
vbo.delete(); vbo.delete();
vbo = null; vbo = null;

View file

@ -68,11 +68,7 @@ public class InstancingEngine extends AbstractEngine {
} }
@Override @Override
public void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) { public void renderCrumbling(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) {
if (crumblingBlocks.isEmpty()) {
return;
}
// Need to wait for flush before we can inspect instancer state. // Need to wait for flush before we can inspect instancer state.
executor.syncUntil(flushFlag::isRaised); executor.syncUntil(flushFlag::isRaised);

View file

@ -2,11 +2,9 @@ package com.jozufozu.flywheel.config;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.backend.Backend; import com.jozufozu.flywheel.api.backend.Backend;
import com.jozufozu.flywheel.lib.util.ResourceUtil; import com.jozufozu.flywheel.lib.util.ResourceUtil;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
@ -14,31 +12,17 @@ import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.ResourceLocationException;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public class BackendArgument implements ArgumentType<Backend> { public class BackendArgument implements ArgumentType<Backend> {
private static final List<String> STRING_IDS = Backend.REGISTRY.getAllIds() private static final List<String> EXAMPLES = List.of("off", "flywheel:off", "instancing");
.stream()
.map(rl -> {
if (Flywheel.ID
.equals(rl.getNamespace())) {
return rl.getPath();
} else {
return rl.toString();
}
})
.toList();
private static final SimpleCommandExceptionType ERROR_INVALID = new SimpleCommandExceptionType(Component.translatable("argument.id.invalid")); private static final DynamicCommandExceptionType ERROR_UNKNOWN_BACKEND = new DynamicCommandExceptionType(arg -> {
public static final DynamicCommandExceptionType ERROR_UNKNOWN_BACKEND = new DynamicCommandExceptionType(arg -> {
return Component.literal("Unknown backend '" + arg + "'"); return Component.literal("Unknown backend '" + arg + "'");
}); });
@ -46,7 +30,7 @@ public class BackendArgument implements ArgumentType<Backend> {
@Override @Override
public Backend parse(StringReader reader) throws CommandSyntaxException { public Backend parse(StringReader reader) throws CommandSyntaxException {
ResourceLocation id = getRead(reader); ResourceLocation id = ResourceUtil.readFlywheelDefault(reader);
Backend backend = Backend.REGISTRY.get(id); Backend backend = Backend.REGISTRY.get(id);
if (backend == null) { if (backend == null) {
@ -56,34 +40,20 @@ public class BackendArgument implements ArgumentType<Backend> {
return backend; return backend;
} }
/**
* Copied from {@link ResourceLocation#read}, but defaults to flywheel namespace.
*/
@NotNull
private static ResourceLocation getRead(StringReader reader) throws CommandSyntaxException {
int i = reader.getCursor();
while(reader.canRead() && ResourceLocation.isAllowedInResourceLocation(reader.peek())) {
reader.skip();
}
String s = reader.getString().substring(i, reader.getCursor());
try {
return ResourceUtil.defaultToFlywheelNamespace(s);
} catch (ResourceLocationException resourcelocationexception) {
reader.setCursor(i);
throw ERROR_INVALID.createWithContext(reader);
}
}
@Override @Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) { public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return SharedSuggestionProvider.suggest(STRING_IDS, builder); String input = builder.getRemaining().toLowerCase(Locale.ROOT);
for (ResourceLocation id : Backend.REGISTRY.getAllIds()) {
String idStr = id.toString();
if (SharedSuggestionProvider.matchesSubStr(input, idStr) || SharedSuggestionProvider.matchesSubStr(input, id.getPath())) {
builder.suggest(idStr);
}
}
return builder.buildFuture();
} }
@Override @Override
public Collection<String> getExamples() { public Collection<String> getExamples() {
return STRING_IDS; return EXAMPLES;
} }
} }

View file

@ -23,7 +23,7 @@ public class FlwConfig {
public final ClientConfig client; public final ClientConfig client;
private final ForgeConfigSpec clientSpec; private final ForgeConfigSpec clientSpec;
public FlwConfig() { private FlwConfig() {
Pair<ClientConfig, ForgeConfigSpec> clientPair = new ForgeConfigSpec.Builder().configure(ClientConfig::new); Pair<ClientConfig, ForgeConfigSpec> clientPair = new ForgeConfigSpec.Builder().configure(ClientConfig::new);
this.client = clientPair.getLeft(); this.client = clientPair.getLeft();
clientSpec = clientPair.getRight(); clientSpec = clientPair.getRight();
@ -74,7 +74,7 @@ public class FlwConfig {
public final ConfigValue<String> backend; public final ConfigValue<String> backend;
public final BooleanValue limitUpdates; public final BooleanValue limitUpdates;
public ClientConfig(ForgeConfigSpec.Builder builder) { private ClientConfig(ForgeConfigSpec.Builder builder) {
backend = builder.comment("Select the backend to use.") backend = builder.comment("Select the backend to use.")
.define("backend", Backend.REGISTRY.getIdOrThrow(BackendManager.getDefaultBackend()).toString()); .define("backend", Backend.REGISTRY.getIdOrThrow(BackendManager.getDefaultBackend()).toString());

View file

@ -2,21 +2,21 @@ package com.jozufozu.flywheel.extension;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
public interface ClientLevelExtension { public interface LevelExtension {
/** /**
* Get an iterator over all entities in this level. * Get an iterator over all entities in this level.
* *
* <p> * <p>
* Normally, this would be accomplished by {@link ClientLevel#entitiesForRendering()}, but the output of that * Normally, this would be accomplished by {@link ClientLevel#entitiesForRendering}, but the output of that
* method is filtered of entities that are rendered by flywheel. This interface provides a workaround. * method does not include entities that are rendered by Flywheel. This interface provides a workaround.
* </p> * </p>
* @return An iterator over all entities in the level, including entities that are rendered by flywheel. * @return An iterator over all entities in the level, including entities that are rendered by Flywheel.
*/ */
Iterable<Entity> flywheel$getAllLoadedEntities(); Iterable<Entity> flywheel$getAllLoadedEntities();
static Iterable<Entity> getAllLoadedEntities(ClientLevel level) { static Iterable<Entity> getAllLoadedEntities(Level level) {
return ((ClientLevelExtension) level).flywheel$getAllLoadedEntities(); return ((LevelExtension) level).flywheel$getAllLoadedEntities();
} }
} }

View file

@ -85,7 +85,7 @@ public class SourceFile implements SourceComponent {
ResourceLocation location; ResourceLocation location;
try { try {
location = ResourceUtil.defaultToFlywheelNamespace(string); location = ResourceUtil.parseFlywheelDefault(string);
} catch (ResourceLocationException e) { } catch (ResourceLocationException e) {
failures.add(Pair.of(fileSpan, new LoadError.MalformedInclude(e))); failures.add(Pair.of(fileSpan, new LoadError.MalformedInclude(e)));
continue; continue;

View file

@ -1,11 +1,11 @@
package com.jozufozu.flywheel.impl; package com.jozufozu.flywheel.impl;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.backend.Backend; import com.jozufozu.flywheel.api.backend.Backend;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent; import com.jozufozu.flywheel.api.event.EndClientResourceReloadEvent;
import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.backend.Backends; import com.jozufozu.flywheel.backend.Backends;
import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.impl.visualization.VisualizationManagerImpl; import com.jozufozu.flywheel.impl.visualization.VisualizationManagerImpl;
@ -59,19 +59,7 @@ public final class BackendManagerImpl {
return Backends.INDIRECT; return Backends.INDIRECT;
} }
public static void onReloadRenderers(ReloadRenderersEvent event) { private static void chooseBackend() {
refresh(event.getLevel());
}
public static void refresh(@Nullable ClientLevel level) {
backend = chooseBackend();
if (level != null) {
VisualizationManagerImpl.reset(level);
}
}
private static Backend chooseBackend() {
var preferred = FlwConfig.get().getBackend(); var preferred = FlwConfig.get().getBackend();
var actual = preferred.findFallback(); var actual = preferred.findFallback();
@ -79,7 +67,7 @@ public final class BackendManagerImpl {
LOGGER.warn("Flywheel backend fell back from '{}' to '{}'", Backend.REGISTRY.getIdOrThrow(preferred), Backend.REGISTRY.getIdOrThrow(actual)); LOGGER.warn("Flywheel backend fell back from '{}' to '{}'", Backend.REGISTRY.getIdOrThrow(preferred), Backend.REGISTRY.getIdOrThrow(actual));
} }
return actual; backend = actual;
} }
public static String getBackendString() { public static String getBackendString() {
@ -93,4 +81,22 @@ public final class BackendManagerImpl {
public static void init() { public static void init() {
CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManagerImpl::getBackendString); CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManagerImpl::getBackendString);
} }
public static void onEndClientResourceReload(EndClientResourceReloadEvent event) {
if (event.getError().isPresent()) {
return;
}
chooseBackend();
VisualizationManagerImpl.resetAll();
}
public static void onReloadLevelRenderer(ReloadLevelRendererEvent event) {
chooseBackend();
ClientLevel level = event.getLevel();
if (level != null) {
VisualizationManagerImpl.reset(level);
}
}
} }

View file

@ -18,7 +18,7 @@ import com.jozufozu.flywheel.api.visual.TickableVisual;
import com.jozufozu.flywheel.api.visualization.VisualManager; import com.jozufozu.flywheel.api.visualization.VisualManager;
import com.jozufozu.flywheel.api.visualization.VisualizationLevel; import com.jozufozu.flywheel.api.visualization.VisualizationLevel;
import com.jozufozu.flywheel.api.visualization.VisualizationManager; import com.jozufozu.flywheel.api.visualization.VisualizationManager;
import com.jozufozu.flywheel.extension.ClientLevelExtension; import com.jozufozu.flywheel.extension.LevelExtension;
import com.jozufozu.flywheel.impl.task.FlwTaskExecutor; import com.jozufozu.flywheel.impl.task.FlwTaskExecutor;
import com.jozufozu.flywheel.impl.visualization.manager.BlockEntityVisualManager; import com.jozufozu.flywheel.impl.visualization.manager.BlockEntityVisualManager;
import com.jozufozu.flywheel.impl.visualization.manager.EffectVisualManager; import com.jozufozu.flywheel.impl.visualization.manager.EffectVisualManager;
@ -32,10 +32,10 @@ import com.jozufozu.flywheel.lib.util.LevelAttached;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
import net.minecraft.server.level.BlockDestructionProgress; import net.minecraft.server.level.BlockDestructionProgress;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
@ -88,6 +88,11 @@ public class VisualizationManagerImpl implements VisualizationManager {
.then(engine.createFramePlan()) .then(engine.createFramePlan())
.then(RaisePlan.raise(frameFlag)) .then(RaisePlan.raise(frameFlag))
.simplify(); .simplify();
if (level instanceof Level l) {
LevelExtension.getAllLoadedEntities(l)
.forEach(entities::queueAdd);
}
} }
public static boolean supportsVisualization(@Nullable LevelAccessor level) { public static boolean supportsVisualization(@Nullable LevelAccessor level) {
@ -127,19 +132,14 @@ public class VisualizationManagerImpl implements VisualizationManager {
return MANAGERS.get(level); return MANAGERS.get(level);
} }
// TODO: Consider making this reset action reuse the existing added game objects instead of readding them, potentially by keeping the same VisualizationManagerImpl and not fully deleting it // TODO: Consider making these reset actions reuse the existing game objects instead of re-adding them
// TODO: Consider changing parameter type to Level since it is also possible to get all entities from it // potentially by keeping the same VisualizationManagerImpl and deleting the engine and visuals but not the game objects
public static void reset(ClientLevel level) { public static void reset(LevelAccessor level) {
MANAGERS.remove(level); MANAGERS.remove(level);
VisualizationManagerImpl manager = get(level);
if (manager == null) {
return;
} }
// Block entities are loaded while chunks are baked. public static void resetAll() {
// Entities are loaded with the level, so when chunks are reloaded they need to be re-added. MANAGERS.reset();
ClientLevelExtension.getAllLoadedEntities(level)
.forEach(manager.getEntities()::queueAdd);
} }
@Override @Override
@ -239,7 +239,9 @@ public class VisualizationManagerImpl implements VisualizationManager {
crumblingBlocks.add(new Engine.CrumblingBlock(maxDestruction.getProgress(), maxDestruction.getPos(), instances)); crumblingBlocks.add(new Engine.CrumblingBlock(maxDestruction.getProgress(), maxDestruction.getPos(), instances));
} }
engine.renderCrumblingInstances(taskExecutor, context, crumblingBlocks); if (!crumblingBlocks.isEmpty()) {
engine.renderCrumbling(taskExecutor, context, crumblingBlocks);
}
} }
/** /**

View file

@ -0,0 +1,76 @@
package com.jozufozu.flywheel.lib.memory;
import java.lang.ref.Cleaner;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
non-sealed abstract class AbstractMemoryBlockImpl implements MemoryBlock {
static final Cleaner CLEANER = Cleaner.create();
final long ptr;
final long size;
boolean freed;
AbstractMemoryBlockImpl(long ptr, long size) {
this.ptr = ptr;
this.size = size;
}
@Override
public long ptr() {
return ptr;
}
@Override
public long size() {
return size;
}
@Override
public boolean isFreed() {
return freed;
}
@Override
public void copyTo(MemoryBlock block) {
long bytes = Math.min(size, block.size());
copyTo(block.ptr(), bytes);
}
@Override
public void copyTo(long ptr, long bytes) {
MemoryUtil.memCopy(this.ptr, ptr, bytes);
}
@Override
public void copyTo(long ptr) {
copyTo(ptr, size);
}
@Override
public void clear() {
MemoryUtil.memSet(ptr, 0, size);
}
@Override
public ByteBuffer asBuffer() {
int intSize = (int) size;
if (intSize != size) {
throw new UnsupportedOperationException("Cannot create buffer with long capacity!");
}
return MemoryUtil.memByteBuffer(ptr, intSize);
}
void freeInner() {
FlwMemoryTracker._freeCPUMemory(size);
freed = true;
}
@Override
public void free() {
FlwMemoryTracker.free(ptr);
freeInner();
}
}

View file

@ -5,16 +5,23 @@ import java.lang.ref.Cleaner;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.lib.util.StringUtil; import com.jozufozu.flywheel.lib.util.StringUtil;
class DebugMemoryBlockImpl extends MemoryBlockImpl { class DebugMemoryBlockImpl extends AbstractMemoryBlockImpl {
final Cleaner cleaner;
final CleaningAction cleaningAction; final CleaningAction cleaningAction;
final Cleaner.Cleanable cleanable; final Cleaner.Cleanable cleanable;
DebugMemoryBlockImpl(long ptr, long size, Cleaner cleaner) { DebugMemoryBlockImpl(long ptr, long size, Cleaner cleaner, int skipFrames) {
super(ptr, size); super(ptr, size);
cleaningAction = new CleaningAction(ptr, size, getStackTrace()); this.cleaner = cleaner;
cleaningAction = new CleaningAction(ptr, size, getStackTrace(skipFrames + 1));
cleanable = cleaner.register(this, cleaningAction); cleanable = cleaner.register(this, cleaningAction);
} }
@Override
public boolean isTracked() {
return false;
}
@Override @Override
void freeInner() { void freeInner() {
super.freeInner(); super.freeInner();
@ -22,24 +29,37 @@ class DebugMemoryBlockImpl extends MemoryBlockImpl {
cleanable.clean(); cleanable.clean();
} }
static StackWalker.StackFrame[] getStackTrace() { @Override
// skip 4 frames to get to the caller: public MemoryBlock realloc(long size) {
MemoryBlock block = new DebugMemoryBlockImpl(FlwMemoryTracker.realloc(ptr, size), size, cleaner, 1);
FlwMemoryTracker._allocCPUMemory(block.size());
freeInner();
return block;
}
static StackWalker.StackFrame[] getStackTrace(int skipFrames) {
// for DebugMemoryBlockImpl::realloc, skip 3 frames to get the allocation site:
// - this method
// - DebugMemoryBlockImpl::new
// - DebugMemoryBlockImpl::realloc
// - {caller is here}
// for DebugMemoryBlockImpl::malloc/calloc, skip 4 frames to get the allocation site:
// - this method // - this method
// - DebugMemoryBlockImpl::new // - DebugMemoryBlockImpl::new
// - DebugMemoryBlockImpl::malloc/calloc // - DebugMemoryBlockImpl::malloc/calloc
// - MemoryBlock::malloc/calloc // - MemoryBlock::malloc/calloc
// - {caller is here} // - {caller is here}
return StackWalker.getInstance().walk(s -> s.skip(4).toArray(StackWalker.StackFrame[]::new)); return StackWalker.getInstance().walk(s -> s.skip(skipFrames + 1).toArray(StackWalker.StackFrame[]::new));
} }
static MemoryBlock malloc(long size) { static MemoryBlock malloc(long size) {
MemoryBlock block = new DebugMemoryBlockImpl(FlwMemoryTracker.malloc(size), size, FlwMemoryTracker.CLEANER); MemoryBlock block = new DebugMemoryBlockImpl(FlwMemoryTracker.malloc(size), size, CLEANER, 2);
FlwMemoryTracker._allocCPUMemory(block.size()); FlwMemoryTracker._allocCPUMemory(block.size());
return block; return block;
} }
static MemoryBlock calloc(long num, long size) { static MemoryBlock calloc(long num, long size) {
MemoryBlock block = new DebugMemoryBlockImpl(FlwMemoryTracker.calloc(num, size), num * size, FlwMemoryTracker.CLEANER); MemoryBlock block = new DebugMemoryBlockImpl(FlwMemoryTracker.calloc(num, size), num * size, CLEANER, 2);
FlwMemoryTracker._allocCPUMemory(block.size()); FlwMemoryTracker._allocCPUMemory(block.size());
return block; return block;
} }

View file

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.lib.memory; package com.jozufozu.flywheel.lib.memory;
import java.lang.ref.Cleaner;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
@ -10,8 +9,6 @@ import com.jozufozu.flywheel.lib.util.StringUtil;
public final class FlwMemoryTracker { public final class FlwMemoryTracker {
public static final boolean DEBUG_MEMORY_SAFETY = System.getProperty("flw.debugMemorySafety") != null; public static final boolean DEBUG_MEMORY_SAFETY = System.getProperty("flw.debugMemorySafety") != null;
static final Cleaner CLEANER = Cleaner.create();
private static final AtomicLong CPU_MEMORY = new AtomicLong(0); private static final AtomicLong CPU_MEMORY = new AtomicLong(0);
private static final AtomicLong GPU_MEMORY = new AtomicLong(0); private static final AtomicLong GPU_MEMORY = new AtomicLong(0);

View file

@ -2,7 +2,7 @@ package com.jozufozu.flywheel.lib.memory;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
public sealed interface MemoryBlock permits MemoryBlockImpl { public sealed interface MemoryBlock permits AbstractMemoryBlockImpl {
long ptr(); long ptr();
long size(); long size();
@ -11,6 +11,8 @@ public sealed interface MemoryBlock permits MemoryBlockImpl {
boolean isTracked(); boolean isTracked();
void copyTo(MemoryBlock block);
void copyTo(long ptr, long bytes); void copyTo(long ptr, long bytes);
void copyTo(long ptr); void copyTo(long ptr);
@ -21,8 +23,6 @@ public sealed interface MemoryBlock permits MemoryBlockImpl {
MemoryBlock realloc(long size); MemoryBlock realloc(long size);
MemoryBlock reallocTracked(long size);
void free(); void free();
static MemoryBlock malloc(long size) { static MemoryBlock malloc(long size) {

View file

@ -1,33 +1,8 @@
package com.jozufozu.flywheel.lib.memory; package com.jozufozu.flywheel.lib.memory;
import java.nio.ByteBuffer; class MemoryBlockImpl extends AbstractMemoryBlockImpl {
import org.lwjgl.system.MemoryUtil;
non-sealed class MemoryBlockImpl implements MemoryBlock {
final long ptr;
final long size;
boolean freed;
MemoryBlockImpl(long ptr, long size) { MemoryBlockImpl(long ptr, long size) {
this.ptr = ptr; super(ptr, size);
this.size = size;
}
@Override
public long ptr() {
return ptr;
}
@Override
public long size() {
return size;
}
@Override
public boolean isFreed() {
return freed;
} }
@Override @Override
@ -35,35 +10,6 @@ non-sealed class MemoryBlockImpl implements MemoryBlock {
return false; return false;
} }
@Override
public void copyTo(long ptr, long bytes) {
MemoryUtil.memCopy(this.ptr, ptr, bytes);
}
@Override
public void copyTo(long ptr) {
copyTo(ptr, size);
}
@Override
public void clear() {
MemoryUtil.memSet(ptr, 0, size);
}
@Override
public ByteBuffer asBuffer() {
int intSize = (int) size;
if (intSize != size) {
throw new UnsupportedOperationException("Cannot create buffer with long capacity!");
}
return MemoryUtil.memByteBuffer(ptr, intSize);
}
void freeInner() {
FlwMemoryTracker._freeCPUMemory(size);
freed = true;
}
@Override @Override
public MemoryBlock realloc(long size) { public MemoryBlock realloc(long size) {
MemoryBlock block = new MemoryBlockImpl(FlwMemoryTracker.realloc(ptr, size), size); MemoryBlock block = new MemoryBlockImpl(FlwMemoryTracker.realloc(ptr, size), size);
@ -72,20 +18,6 @@ non-sealed class MemoryBlockImpl implements MemoryBlock {
return block; return block;
} }
@Override
public MemoryBlock reallocTracked(long size) {
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.realloc(ptr, size), size, FlwMemoryTracker.CLEANER);
FlwMemoryTracker._allocCPUMemory(block.size());
freeInner();
return block;
}
@Override
public void free() {
FlwMemoryTracker.free(ptr);
freeInner();
}
static MemoryBlock malloc(long size) { static MemoryBlock malloc(long size) {
MemoryBlock block = new MemoryBlockImpl(FlwMemoryTracker.malloc(size), size); MemoryBlock block = new MemoryBlockImpl(FlwMemoryTracker.malloc(size), size);
FlwMemoryTracker._allocCPUMemory(block.size()); FlwMemoryTracker._allocCPUMemory(block.size());

View file

@ -2,12 +2,14 @@ package com.jozufozu.flywheel.lib.memory;
import java.lang.ref.Cleaner; import java.lang.ref.Cleaner;
class TrackedMemoryBlockImpl extends MemoryBlockImpl { class TrackedMemoryBlockImpl extends AbstractMemoryBlockImpl {
final Cleaner cleaner;
final CleaningAction cleaningAction; final CleaningAction cleaningAction;
final Cleaner.Cleanable cleanable; final Cleaner.Cleanable cleanable;
TrackedMemoryBlockImpl(long ptr, long size, Cleaner cleaner) { TrackedMemoryBlockImpl(long ptr, long size, Cleaner cleaner) {
super(ptr, size); super(ptr, size);
this.cleaner = cleaner;
cleaningAction = new CleaningAction(ptr, size); cleaningAction = new CleaningAction(ptr, size);
cleanable = cleaner.register(this, cleaningAction); cleanable = cleaner.register(this, cleaningAction);
} }
@ -24,14 +26,22 @@ class TrackedMemoryBlockImpl extends MemoryBlockImpl {
cleanable.clean(); cleanable.clean();
} }
@Override
public MemoryBlock realloc(long size) {
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.realloc(ptr, size), size, cleaner);
FlwMemoryTracker._allocCPUMemory(block.size());
freeInner();
return block;
}
static MemoryBlock malloc(long size) { static MemoryBlock malloc(long size) {
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.malloc(size), size, FlwMemoryTracker.CLEANER); MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.malloc(size), size, CLEANER);
FlwMemoryTracker._allocCPUMemory(block.size()); FlwMemoryTracker._allocCPUMemory(block.size());
return block; return block;
} }
static MemoryBlock calloc(long num, long size) { static MemoryBlock calloc(long num, long size) {
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.calloc(num, size), num * size, FlwMemoryTracker.CLEANER); MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.calloc(num, size), num * size, CLEANER);
FlwMemoryTracker._allocCPUMemory(block.size()); FlwMemoryTracker._allocCPUMemory(block.size());
return block; return block;
} }

View file

@ -8,7 +8,7 @@ import java.util.function.Function;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent; import com.jozufozu.flywheel.api.event.EndClientResourceReloadEvent;
import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.api.model.Model;
public class ModelCache<T> { public class ModelCache<T> {
@ -31,7 +31,7 @@ public class ModelCache<T> {
} }
@ApiStatus.Internal @ApiStatus.Internal
public static void onReloadRenderers(ReloadRenderersEvent event) { public static void onEndClientResourceReload(EndClientResourceReloadEvent event) {
for (ModelCache<?> cache : ALL) { for (ModelCache<?> cache : ALL) {
cache.clear(); cache.clear();
} }

View file

@ -7,7 +7,7 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent; import com.jozufozu.flywheel.api.event.EndClientResourceReloadEvent;
import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.api.model.Model;
public class ModelHolder { public class ModelHolder {
@ -51,7 +51,7 @@ public class ModelHolder {
} }
@ApiStatus.Internal @ApiStatus.Internal
public static void onReloadRenderers(ReloadRenderersEvent event) { public static void onEndClientResourceReload(EndClientResourceReloadEvent event) {
for (ModelHolder holder : ALL) { for (ModelHolder holder : ALL) {
holder.clear(); holder.clear();
} }

View file

@ -56,11 +56,13 @@ public final class BakedModelBufferer {
blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, buffer, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType); blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, buffer, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType);
poseStack.popPose(); poseStack.popPose();
RenderedBuffer data = buffer.end(); RenderedBuffer data = buffer.endOrDiscardIfEmpty();
if (data != null) {
resultConsumer.accept(renderType, data); resultConsumer.accept(renderType, data);
data.release(); data.release();
} }
} }
}
public static void bufferSingleShadeSeparated(ModelBlockRenderer blockRenderer, BlockAndTintGetter renderWorld, BakedModel model, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ShadeSeparatedResultConsumer resultConsumer) { public static void bufferSingleShadeSeparated(ModelBlockRenderer blockRenderer, BlockAndTintGetter renderWorld, BakedModel model, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ShadeSeparatedResultConsumer resultConsumer) {
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get(); ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
@ -89,13 +91,17 @@ public final class BakedModelBufferer {
blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, shadeSeparatingWrapper, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType); blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, shadeSeparatingWrapper, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType);
poseStack.popPose(); poseStack.popPose();
RenderedBuffer shadedData = shadedBuffer.end(); RenderedBuffer shadedData = shadedBuffer.endOrDiscardIfEmpty();
RenderedBuffer unshadedData = unshadedBuffer.end(); if (shadedData != null) {
resultConsumer.accept(renderType, true, shadedData); resultConsumer.accept(renderType, true, shadedData);
shadedData.release(); shadedData.release();
}
RenderedBuffer unshadedData = unshadedBuffer.endOrDiscardIfEmpty();
if (unshadedData != null) {
resultConsumer.accept(renderType, false, unshadedData); resultConsumer.accept(renderType, false, unshadedData);
unshadedData.release(); unshadedData.release();
} }
}
shadeSeparatingWrapper.clear(); shadeSeparatingWrapper.clear();
} }
@ -163,11 +169,13 @@ public final class BakedModelBufferer {
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) { for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
RenderType renderType = CHUNK_LAYERS[layerIndex]; RenderType renderType = CHUNK_LAYERS[layerIndex];
BufferBuilder buffer = buffers[layerIndex]; BufferBuilder buffer = buffers[layerIndex];
RenderedBuffer data = buffer.end(); RenderedBuffer data = buffer.endOrDiscardIfEmpty();
if (data != null) {
resultConsumer.accept(renderType, data); resultConsumer.accept(renderType, data);
data.release(); data.release();
} }
} }
}
public static void bufferMultiBlockShadeSeparated(Collection<StructureTemplate.StructureBlockInfo> blocks, BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Map<BlockPos, ModelData> modelDataMap, ShadeSeparatedResultConsumer resultConsumer) { public static void bufferMultiBlockShadeSeparated(Collection<StructureTemplate.StructureBlockInfo> blocks, BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Map<BlockPos, ModelData> modelDataMap, ShadeSeparatedResultConsumer resultConsumer) {
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get(); ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
@ -224,14 +232,18 @@ public final class BakedModelBufferer {
RenderType renderType = CHUNK_LAYERS[layerIndex]; RenderType renderType = CHUNK_LAYERS[layerIndex];
BufferBuilder shadedBuffer = shadedBuffers[layerIndex]; BufferBuilder shadedBuffer = shadedBuffers[layerIndex];
BufferBuilder unshadedBuffer = unshadedBuffers[layerIndex]; BufferBuilder unshadedBuffer = unshadedBuffers[layerIndex];
RenderedBuffer shadedData = shadedBuffer.end(); RenderedBuffer shadedData = shadedBuffer.endOrDiscardIfEmpty();
RenderedBuffer unshadedData = unshadedBuffer.end(); if (shadedData != null) {
resultConsumer.accept(renderType, true, shadedData); resultConsumer.accept(renderType, true, shadedData);
shadedData.release(); shadedData.release();
}
RenderedBuffer unshadedData = unshadedBuffer.endOrDiscardIfEmpty();
if (unshadedBuffer != null) {
resultConsumer.accept(renderType, false, unshadedData); resultConsumer.accept(renderType, false, unshadedData);
unshadedData.release(); unshadedData.release();
} }
} }
}
public interface ResultConsumer { public interface ResultConsumer {
void accept(RenderType renderType, RenderedBuffer data); void accept(RenderType renderType, RenderedBuffer data);

View file

@ -81,24 +81,20 @@ public class BakedModelBuilder {
if (shadeSeparated) { if (shadeSeparated) {
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> { ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, shaded); Material material = materialFunc.apply(renderType, shaded);
if (material != null) { if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK); MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType + ",shaded=" + shaded)); meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType + ",shaded=" + shaded));
} }
}
}; };
BakedModelBufferer.bufferSingleShadeSeparated(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer); BakedModelBufferer.bufferSingleShadeSeparated(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);
} else { } else {
ResultConsumer resultConsumer = (renderType, data) -> { ResultConsumer resultConsumer = (renderType, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, true); Material material = materialFunc.apply(renderType, true);
if (material != null) { if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK); MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType)); meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType));
} }
}
}; };
BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer); BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);
} }

View file

@ -70,24 +70,20 @@ public class BlockModelBuilder {
if (shadeSeparated) { if (shadeSeparated) {
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> { ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, shaded); Material material = materialFunc.apply(renderType, shaded);
if (material != null) { if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK); MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType + ",shaded=" + shaded)); meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType + ",shaded=" + shaded));
} }
}
}; };
BakedModelBufferer.bufferBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer); BakedModelBufferer.bufferBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);
} else { } else {
ResultConsumer resultConsumer = (renderType, data) -> { ResultConsumer resultConsumer = (renderType, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, true); Material material = materialFunc.apply(renderType, true);
if (material != null) { if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK); MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType)); meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType));
} }
}
}; };
BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer); BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);
} }

View file

@ -74,24 +74,20 @@ public class MultiBlockModelBuilder {
if (shadeSeparated) { if (shadeSeparated) {
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> { ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, shaded); Material material = materialFunc.apply(renderType, shaded);
if (material != null) { if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK); MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType + ",shaded=" + shaded)); meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType + ",shaded=" + shaded));
} }
}
}; };
BakedModelBufferer.bufferMultiBlockShadeSeparated(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer); BakedModelBufferer.bufferMultiBlockShadeSeparated(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);
} else { } else {
ResultConsumer resultConsumer = (renderType, data) -> { ResultConsumer resultConsumer = (renderType, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, true); Material material = materialFunc.apply(renderType, true);
if (material != null) { if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK); MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType)); meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType));
} }
}
}; };
BakedModelBufferer.bufferMultiBlock(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer); BakedModelBufferer.bufferMultiBlock(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);
} }

View file

@ -126,7 +126,7 @@ class VertexWriter implements VertexConsumer {
public MemoryBlock copyDataAndReset() { public MemoryBlock copyDataAndReset() {
MemoryBlock dataCopy = MemoryBlock.malloc(vertexCount * STRIDE); MemoryBlock dataCopy = MemoryBlock.malloc(vertexCount * STRIDE);
data.copyTo(dataCopy.ptr(), dataCopy.size()); data.copyTo(dataCopy);
vertexCount = 0; vertexCount = 0;
filledPosition = false; filledPosition = false;

View file

@ -1,11 +1,24 @@
package com.jozufozu.flywheel.lib.util; package com.jozufozu.flywheel.lib.util;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import net.minecraft.ResourceLocationException;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public class ResourceUtil { public final class ResourceUtil {
public static ResourceLocation defaultToFlywheelNamespace(String location) { private static final SimpleCommandExceptionType ERROR_INVALID = new SimpleCommandExceptionType(Component.translatable("argument.id.invalid"));
private ResourceUtil() {
}
/**
* Same as {@link ResourceLocation#ResourceLocation(String)}, but defaults to Flywheel namespace.
*/
public static ResourceLocation parseFlywheelDefault(String location) {
String namespace = Flywheel.ID; String namespace = Flywheel.ID;
String path = location; String path = location;
@ -19,4 +32,24 @@ public class ResourceUtil {
return new ResourceLocation(namespace, path); return new ResourceLocation(namespace, path);
} }
/**
* Same as {@link ResourceLocation#read(StringReader)}, but defaults to Flywheel namespace.
*/
public static ResourceLocation readFlywheelDefault(StringReader reader) throws CommandSyntaxException {
int i = reader.getCursor();
while (reader.canRead() && ResourceLocation.isAllowedInResourceLocation(reader.peek())) {
reader.skip();
}
String s = reader.getString().substring(i, reader.getCursor());
try {
return parseFlywheelDefault(s);
} catch (ResourceLocationException resourcelocationexception) {
reader.setCursor(i);
throw ERROR_INVALID.createWithContext(reader);
}
}
} }

View file

@ -11,7 +11,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
@Mixin(BlockEntityType.class) @Mixin(BlockEntityType.class)
public class BlockEntityTypeMixin<T extends BlockEntity> implements BlockEntityTypeExtension<T> { abstract class BlockEntityTypeMixin<T extends BlockEntity> implements BlockEntityTypeExtension<T> {
@Unique @Unique
private BlockEntityVisualizer<? super T> flywheel$visualizer; private BlockEntityVisualizer<? super T> flywheel$visualizer;

View file

@ -14,7 +14,7 @@ import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormatElement; import com.mojang.blaze3d.vertex.VertexFormatElement;
@Mixin(BufferBuilder.class) @Mixin(BufferBuilder.class)
public abstract class BufferBuilderMixin implements BufferBuilderExtension { abstract class BufferBuilderMixin implements BufferBuilderExtension {
@Shadow @Shadow
private ByteBuffer buffer; private ByteBuffer buffer;

View file

@ -3,30 +3,19 @@ package com.jozufozu.flywheel.mixin;
import java.util.ArrayList; import java.util.ArrayList;
import org.spongepowered.asm.mixin.Mixin; 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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.jozufozu.flywheel.api.visualization.VisualizationManager; import com.jozufozu.flywheel.api.visualization.VisualizationManager;
import com.jozufozu.flywheel.extension.ClientLevelExtension;
import com.jozufozu.flywheel.impl.visualization.VisualizationHelper; import com.jozufozu.flywheel.impl.visualization.VisualizationHelper;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.entity.LevelEntityGetter;
@Mixin(ClientLevel.class) @Mixin(ClientLevel.class)
public abstract class ClientLevelMixin implements ClientLevelExtension { abstract class ClientLevelMixin {
@Shadow
protected abstract LevelEntityGetter<Entity> getEntities();
@Override
public Iterable<Entity> flywheel$getAllLoadedEntities() {
return getEntities().getAll();
}
@Inject(method = "entitiesForRendering()Ljava/lang/Iterable;", at = @At("RETURN"), cancellable = true) @Inject(method = "entitiesForRendering()Ljava/lang/Iterable;", at = @At("RETURN"), cancellable = true)
private void flywheel$filterEntities(CallbackInfoReturnable<Iterable<Entity>> cir) { private void flywheel$filterEntities(CallbackInfoReturnable<Iterable<Entity>> cir) {
if (!VisualizationManager.supportsVisualization((ClientLevel) (Object) this)) { if (!VisualizationManager.supportsVisualization((ClientLevel) (Object) this)) {

View file

@ -11,7 +11,7 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
@Mixin(EntityType.class) @Mixin(EntityType.class)
public class EntityTypeMixin<T extends Entity> implements EntityTypeExtension<T> { abstract class EntityTypeMixin<T extends Entity> implements EntityTypeExtension<T> {
@Unique @Unique
private EntityVisualizer<? super T> flywheel$visualizer; private EntityVisualizer<? super T> flywheel$visualizer;

View file

@ -11,7 +11,7 @@ import com.jozufozu.flywheel.lib.uniform.FlwShaderUniforms;
import net.minecraft.client.renderer.FogRenderer; import net.minecraft.client.renderer.FogRenderer;
@Mixin(FogRenderer.class) @Mixin(FogRenderer.class)
public class FogUpdateMixin { abstract class FogUpdateMixin {
@Unique @Unique
private static void flywheel$updateFog() { private static void flywheel$updateFog() {
FlwShaderUniforms.FOG_UPDATE = true; FlwShaderUniforms.FOG_UPDATE = true;

View file

@ -10,7 +10,7 @@ import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
@Mixin(GlStateManager.class) @Mixin(GlStateManager.class)
public class GlStateManagerMixin { abstract class GlStateManagerMixin {
@Inject(method = "_glBindBuffer(II)V", at = @At("RETURN")) @Inject(method = "_glBindBuffer(II)V", at = @At("RETURN"))
private static void flywheel$onBindBuffer(int target, int buffer, CallbackInfo ci) { private static void flywheel$onBindBuffer(int target, int buffer, CallbackInfo ci) {
GlStateTracker._setBuffer(GlBufferType.fromTarget(target), buffer); GlStateTracker._setBuffer(GlBufferType.fromTarget(target), buffer);

View file

@ -0,0 +1,21 @@
package com.jozufozu.flywheel.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import com.jozufozu.flywheel.extension.LevelExtension;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.entity.LevelEntityGetter;
@Mixin(Level.class)
abstract class LevelMixin implements LevelExtension {
@Shadow
protected abstract LevelEntityGetter<Entity> getEntities();
@Override
public Iterable<Entity> flywheel$getAllLoadedEntities() {
return getEntities().getAll();
}
}

View file

@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.api.event.BeginFrameEvent; import com.jozufozu.flywheel.api.event.BeginFrameEvent;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent; import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.event.RenderContext; import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.event.RenderStage; import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.event.RenderStageEvent; import com.jozufozu.flywheel.api.event.RenderStageEvent;
@ -32,7 +32,7 @@ import net.minecraft.server.level.BlockDestructionProgress;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
@Mixin(value = LevelRenderer.class, priority = 1001) // Higher priority to go after Sodium @Mixin(value = LevelRenderer.class, priority = 1001) // Higher priority to go after Sodium
public class LevelRendererMixin { abstract class LevelRendererMixin {
@Shadow @Shadow
private ClientLevel level; private ClientLevel level;
@ -61,7 +61,7 @@ public class LevelRendererMixin {
@Inject(method = "allChanged", at = @At("RETURN")) @Inject(method = "allChanged", at = @At("RETURN"))
private void flywheel$refresh(CallbackInfo ci) { private void flywheel$refresh(CallbackInfo ci) {
MinecraftForge.EVENT_BUS.post(new ReloadRenderersEvent(level)); MinecraftForge.EVENT_BUS.post(new ReloadLevelRendererEvent(level));
} }
@Inject(method = "renderLevel", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", args = "ldc=destroyProgress")) @Inject(method = "renderLevel", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", args = "ldc=destroyProgress"))

View file

@ -16,7 +16,7 @@ import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.ChunkSource;
@Mixin(ClientChunkCache.class) @Mixin(ClientChunkCache.class)
public abstract class LightUpdateMixin extends ChunkSource { abstract class LightUpdateMixin extends ChunkSource {
@Shadow @Shadow
@Final @Final
ClientLevel level; ClientLevel level;

View file

@ -0,0 +1,37 @@
package com.jozufozu.flywheel.mixin;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.spongepowered.asm.mixin.Final;
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.api.event.EndClientResourceReloadEvent;
import com.mojang.realmsclient.client.RealmsClient;
import net.minecraft.client.Minecraft;
import net.minecraft.client.main.GameConfig;
import net.minecraft.server.packs.resources.ReloadInstance;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraftforge.fml.ModLoader;
@Mixin(Minecraft.class)
abstract class MinecraftMixin {
@Shadow
@Final
private ReloadableResourceManager resourceManager;
@Inject(method = "lambda$new$5", at = @At("HEAD"))
private void flywheel$onEndInitialResourceReload(RealmsClient realmsClient, ReloadInstance reloadInstance, GameConfig gameConfig, Optional<Throwable> error, CallbackInfo ci) {
ModLoader.get().postEvent(new EndClientResourceReloadEvent((Minecraft) (Object) this, resourceManager, true, error));
}
@Inject(method = "lambda$reloadResourcePacks$28", at = @At("HEAD"))
private void flywheel$onEndManualResourceReload(boolean recovery, CompletableFuture<Void> future, Optional<Throwable> error, CallbackInfo ci) {
ModLoader.get().postEvent(new EndClientResourceReloadEvent((Minecraft) (Object) this, resourceManager, false, error));
}
}

View file

@ -7,7 +7,7 @@ import com.jozufozu.flywheel.lib.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
@Mixin(PoseStack.class) @Mixin(PoseStack.class)
public abstract class PoseStackMixin implements TransformStack { abstract class PoseStackMixin implements TransformStack {
@Override @Override
public TransformStack pushPose() { public TransformStack pushPose() {
((PoseStack) (Object) this).pushPose(); ((PoseStack) (Object) this).pushPose();

View file

@ -12,10 +12,11 @@ import com.jozufozu.flywheel.extension.RenderTypeExtension;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
@Mixin(RenderType.class) @Mixin(RenderType.class)
public class RenderTypeMixin implements RenderTypeExtension { abstract class RenderTypeMixin implements RenderTypeExtension {
@Shadow @Shadow
@Final @Final
private boolean sortOnUpload; private boolean sortOnUpload;
@Unique @Unique
private DrawBufferSet flywheel$drawBufferSet; private DrawBufferSet flywheel$drawBufferSet;

View file

@ -8,7 +8,7 @@ import com.jozufozu.flywheel.extension.VertexFormatExtension;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
@Mixin(VertexFormat.class) @Mixin(VertexFormat.class)
public class VertexFormatMixin implements VertexFormatExtension { abstract class VertexFormatMixin implements VertexFormatExtension {
@Unique @Unique
private VertexListProvider flywheel$vertexListProvider; private VertexListProvider flywheel$vertexListProvider;

View file

@ -10,7 +10,7 @@ import com.mojang.blaze3d.platform.GlStateManager;
import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LevelRenderer;
@Mixin(LevelRenderer.class) @Mixin(LevelRenderer.class)
public class FixFabulousDepthMixin { abstract class FixFabulousDepthMixin {
@Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/PostChain;process(F)V", ordinal = 1)) @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/PostChain;process(F)V", ordinal = 1))
private void flywheel$disableTransparencyShaderDepth(CallbackInfo ci) { private void flywheel$disableTransparencyShaderDepth(CallbackInfo ci) {
GlStateManager._depthMask(false); GlStateManager._depthMask(false);

View file

@ -10,7 +10,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
@Mixin(PoseStack.class) @Mixin(PoseStack.class)
public class FixNormalScalingMixin { abstract class FixNormalScalingMixin {
/** /**
* Minecraft negates the normal matrix if all scales are equal and negative, but * Minecraft negates the normal matrix if all scales are equal and negative, but
* does not return afterward. This allows the rest of the method's logic to be * does not return afterward. This allows the rest of the method's logic to be

View file

@ -12,7 +12,7 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
@Mixin(value = ChunkBuilderMeshingTask.class, remap = false) @Mixin(value = ChunkBuilderMeshingTask.class, remap = false)
public class ChunkBuilderMeshingTaskMixin { abstract class ChunkBuilderMeshingTaskMixin {
@Redirect(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderDispatcher;getRenderer(Lnet/minecraft/world/level/block/entity/BlockEntity;)Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderer;", remap = true)) @Redirect(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderDispatcher;getRenderer(Lnet/minecraft/world/level/block/entity/BlockEntity;)Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderer;", remap = true))
private BlockEntityRenderer<?> flywheel$redirectGetRenderer(BlockEntityRenderDispatcher dispatcher, BlockEntity blockEntity) { private BlockEntityRenderer<?> flywheel$redirectGetRenderer(BlockEntityRenderDispatcher dispatcher, BlockEntity blockEntity) {
if (VisualizationHelper.tryAddBlockEntity(blockEntity)) { if (VisualizationHelper.tryAddBlockEntity(blockEntity)) {

View file

@ -11,7 +11,7 @@ import com.jozufozu.flywheel.impl.visualization.VisualizationHelper;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
@Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$RenderChunk$RebuildTask") @Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$RenderChunk$RebuildTask")
public class ChunkRebuildHooksMixin { abstract class ChunkRebuildHooksMixin {
@Inject(method = "handleBlockEntity(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk$RebuildTask$CompileResults;Lnet/minecraft/world/level/block/entity/BlockEntity;)V", at = @At("HEAD"), cancellable = true) @Inject(method = "handleBlockEntity(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk$RebuildTask$CompileResults;Lnet/minecraft/world/level/block/entity/BlockEntity;)V", at = @At("HEAD"), cancellable = true)
private void flywheel$tryAddBlockEntity(@Coerce Object compileResults, BlockEntity blockEntity, CallbackInfo ci) { private void flywheel$tryAddBlockEntity(@Coerce Object compileResults, BlockEntity blockEntity, CallbackInfo ci) {
if (VisualizationHelper.tryAddBlockEntity(blockEntity)) { if (VisualizationHelper.tryAddBlockEntity(blockEntity)) {

View file

@ -14,7 +14,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
@Mixin(LevelChunk.class) @Mixin(LevelChunk.class)
public class VisualAddMixin { abstract class VisualAddMixin {
@Shadow @Shadow
@Final @Final
Level level; Level level;

View file

@ -13,7 +13,7 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
@Mixin(BlockEntity.class) @Mixin(BlockEntity.class)
public class VisualRemoveMixin { abstract class VisualRemoveMixin {
@Shadow @Shadow
@Nullable @Nullable
protected Level level; protected Level level;

View file

@ -15,7 +15,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
@Mixin(LevelRenderer.class) @Mixin(LevelRenderer.class)
public class VisualUpdateMixin { abstract class VisualUpdateMixin {
@Shadow @Shadow
private ClientLevel level; private ClientLevel level;

View file

@ -5,7 +5,7 @@ import java.util.List;
import org.joml.Vector3f; import org.joml.Vector3f;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent; import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.event.RenderStage; import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.task.Plan; import com.jozufozu.flywheel.api.task.Plan;
import com.jozufozu.flywheel.api.visual.Effect; import com.jozufozu.flywheel.api.visual.Effect;
@ -65,7 +65,7 @@ public class ExampleEffect implements Effect {
trySpawnNewEffect(); trySpawnNewEffect();
} }
public static void onReload(ReloadRenderersEvent event) { public static void onReloadLevelRenderer(ReloadLevelRendererEvent event) {
ALL_EFFECTS.clear(); ALL_EFFECTS.clear();
} }

View file

@ -11,8 +11,10 @@
"EntityTypeMixin", "EntityTypeMixin",
"FogUpdateMixin", "FogUpdateMixin",
"GlStateManagerMixin", "GlStateManagerMixin",
"LevelMixin",
"LevelRendererMixin", "LevelRendererMixin",
"LightUpdateMixin", "LightUpdateMixin",
"MinecraftMixin",
"PoseStackMixin", "PoseStackMixin",
"RenderTypeMixin", "RenderTypeMixin",
"VertexFormatMixin", "VertexFormatMixin",