diff --git a/build.gradle b/build.gradle index 1386c43a8..93546ccc8 100644 --- a/build.gradle +++ b/build.gradle @@ -61,7 +61,8 @@ dependencies { // Fabric API modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}" - modCompileOnly 'curse.maven:starlight-521783:3667443' + // switch to implementation for debugging + modCompileOnly 'maven.modrinth:starlight:1.1.1+1.19' modCompileOnly 'maven.modrinth:iris:1.18.x-v1.2.5' modCompileOnly 'maven.modrinth:sodium:mc1.18.2-0.4.1' @@ -155,6 +156,6 @@ curseforge { changelog = file('changelog.txt') releaseType = project.curse_type mainArtifact jar - addGameVersion '1.18.2' + addGameVersion '1.19.2' } } diff --git a/gradle.properties b/gradle.properties index 07e27bfa7..fd29e4b7a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,11 +3,11 @@ org.gradle.daemon = false # mod version info mod_version = 0.6.8 -artifact_minecraft_version = 1.18.2 +artifact_minecraft_version = 1.19.2 -minecraft_version = 1.18.2 +minecraft_version = 1.19.2 loader_version = 0.14.9 -fabric_version = 0.66.0+1.18.2 +fabric_version = 0.66.0+1.19.2 # build dependency versions loom_version = 1.0-SNAPSHOT diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java index db60ee2c6..f49307a3c 100644 --- a/src/main/java/com/jozufozu/flywheel/Flywheel.java +++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java @@ -7,6 +7,7 @@ import com.jozufozu.flywheel.backend.Loader; import com.jozufozu.flywheel.backend.RenderWork; import com.jozufozu.flywheel.backend.ShadersModHandler; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; +import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.core.Contexts; import com.jozufozu.flywheel.core.PartialModel; @@ -21,6 +22,7 @@ import com.jozufozu.flywheel.vanilla.VanillaInstances; import com.mojang.logging.LogUtils; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; @@ -55,6 +57,7 @@ public class Flywheel implements ClientModInitializer { ShadersModHandler.init(); Backend.init(); + ClientCommandRegistrationCallback.EVENT.register(FlwCommands::registerClientCommands); FlywheelEvents.RELOAD_RENDERERS.register(ProgramCompiler::invalidateAll); FlywheelEvents.GATHER_CONTEXT.register(Contexts::flwInit); diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index 2ede985cb..f9b844dc6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -1,7 +1,7 @@ package com.jozufozu.flywheel.backend; +import java.io.InputStream; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -90,11 +90,11 @@ public class Loader { private void loadProgramSpecs(ResourceManager manager) { programs.clear(); - Collection programSpecs = manager.listResources(PROGRAM_DIR, s -> s.endsWith(".json")); + Map programSpecs = manager.listResources(PROGRAM_DIR, loc -> loc.getPath().endsWith(".json")); - for (ResourceLocation location : programSpecs) { - try (Resource resource = manager.getResource(location)) { - String s = StringUtil.readToString(resource.getInputStream()); + programSpecs.forEach((location, resource) -> { + try (InputStream inputStream = resource.open()) { + String s = StringUtil.readToString(inputStream); ResourceLocation specName = ResourceUtil.trim(location, PROGRAM_DIR, ".json"); @@ -114,7 +114,7 @@ public class Loader { } catch (Exception e) { Backend.LOGGER.error("Could not load program " + location, e); } - } + }); } public static class ResourceReloadListener implements ResourceManagerReloadListener, IdentifiableResourceReloadListener { diff --git a/src/main/java/com/jozufozu/flywheel/config/BackendTypeArgument.java b/src/main/java/com/jozufozu/flywheel/config/BackendTypeArgument.java deleted file mode 100644 index 7f2545a3a..000000000 --- a/src/main/java/com/jozufozu/flywheel/config/BackendTypeArgument.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.jozufozu.flywheel.config; - -import java.util.Collection; -import java.util.concurrent.CompletableFuture; - -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.arguments.ArgumentType; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; - -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.network.chat.TranslatableComponent; - -public enum BackendTypeArgument implements ArgumentType { - INSTANCE; - - private static final Dynamic2CommandExceptionType INVALID = new Dynamic2CommandExceptionType((found, constants) -> { - // TODO: don't steal lang - return new TranslatableComponent("commands.forge.arguments.enum.invalid", constants, found); - }); - - @Override - public BackendType parse(StringReader reader) throws CommandSyntaxException { - String string = reader.readUnquotedString(); - - BackendType engine = BackendType.byName(string); - - if (engine == null) { - throw INVALID.createWithContext(reader, string, BackendType.validNames()); - } - - return engine; - } - - public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { - return SharedSuggestionProvider.suggest(BackendType.validNames(), builder); - } - - @Override - public Collection getExamples() { - return BackendType.validNames(); - } - - public static BackendTypeArgument getInstance() { - return INSTANCE; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index 8fd568212..e5b8a2832 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -8,17 +8,20 @@ import org.jetbrains.annotations.NotNull; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.config.Option.EnumOption; import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import net.fabricmc.fabric.api.client.command.v1.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandBuildContext; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; -import net.minecraft.network.chat.TextComponent; public final class FlwCommands { - public static void init(FlwConfig config) { + public static void registerClientCommands(CommandDispatcher dispatcher, CommandBuildContext context) { + FlwConfig config = FlwConfig.get(); + ConfigCommandBuilder commandBuilder = new ConfigCommandBuilder("flywheel"); commandBuilder.addOption(config.backend, (builder, option) -> enumOptionCommand(builder, config, option, @@ -34,27 +37,27 @@ public final class FlwCommands { commandBuilder.addOption(config.debugNormals, (builder, option) -> booleanOptionCommand(builder, config, option, (source, value) -> { - Component text = new TextComponent("Normal debug mode is currently: ").append(boolToText(value)); + Component text = Component.literal("Normal debug mode is currently: ").append(boolToText(value)); source.sendFeedback(text); }, (source, value) -> { - Component text = boolToText(value).append(new TextComponent(" normal debug mode").withStyle(ChatFormatting.WHITE)); + Component text = boolToText(value).append(Component.literal(" normal debug mode").withStyle(ChatFormatting.WHITE)); source.sendFeedback(text); } )); commandBuilder.addOption(config.limitUpdates, (builder, option) -> booleanOptionCommand(builder, config, option, (source, value) -> { - Component text = new TextComponent("Update limiting is currently: ").append(boolToText(value)); + Component text = Component.literal("Update limiting is currently: ").append(boolToText(value)); source.sendFeedback(text); }, (source, value) -> { - Component text = boolToText(value).append(new TextComponent(" update limiting.").withStyle(ChatFormatting.WHITE)); + Component text = boolToText(value).append(Component.literal(" update limiting.").withStyle(ChatFormatting.WHITE)); source.sendFeedback(text); } )); - commandBuilder.build(); + commandBuilder.build(dispatcher); } public static void booleanOptionCommand(LiteralArgumentBuilder builder, FlwConfig config, Option option, BiConsumer displayAction, BiConsumer setAction) { @@ -96,14 +99,14 @@ public final class FlwCommands { } public static MutableComponent boolToText(boolean b) { - return b ? new TextComponent("enabled").withStyle(ChatFormatting.DARK_GREEN) : new TextComponent("disabled").withStyle(ChatFormatting.RED); + return b ? Component.literal("enabled").withStyle(ChatFormatting.DARK_GREEN) : Component.literal("disabled").withStyle(ChatFormatting.RED); } public static Component getEngineMessage(@NotNull BackendType type) { return switch (type) { - case OFF -> new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED); - case INSTANCING -> new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN); - case BATCHING -> new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN); + case OFF -> Component.literal("Disabled Flywheel").withStyle(ChatFormatting.RED); + case INSTANCING -> Component.literal("Using Instancing Engine").withStyle(ChatFormatting.GREEN); + case BATCHING -> Component.literal("Using Batching Engine").withStyle(ChatFormatting.GREEN); }; } @@ -124,8 +127,8 @@ public final class FlwCommands { command.then(builder); } - public void build() { - ClientCommandManager.DISPATCHER.register(command); + public void build(CommandDispatcher dispatcher) { + dispatcher.register(command); } } } diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java index 18f484ef5..3e010b2eb 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java @@ -51,7 +51,6 @@ public class FlwConfig { public static void init() { INSTANCE.load(); - FlwCommands.init(INSTANCE); } public BackendType getBackendType() { diff --git a/src/main/java/com/jozufozu/flywheel/core/PartialModel.java b/src/main/java/com/jozufozu/flywheel/core/PartialModel.java index a59317f8f..c49174b66 100644 --- a/src/main/java/com/jozufozu/flywheel/core/PartialModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/PartialModel.java @@ -22,10 +22,10 @@ import net.minecraft.server.packs.resources.ResourceManagerReloadListener; * Creating a PartialModel will make the associated modelLocation automatically load. * PartialModels must be initialized the mod class constructor. *
- * Once {@link ModelBakeEvent} finishes, all PartialModels (with valid modelLocations) + * Once {@link ModelEvent.BakingCompleted} finishes, all PartialModels (with valid modelLocations) * will have their bakedModel fields populated. *
- * Attempting to create a PartialModel after {@link ModelRegistryEvent} will cause an error. + * Attempting to create a PartialModel after {@link ModelEvent.RegisterAdditional} will cause an error. */ public class PartialModel { @@ -36,7 +36,7 @@ public class PartialModel { protected BakedModel bakedModel; public PartialModel(ResourceLocation modelLocation) { - if (tooLate) throw new RuntimeException("PartialModel '" + modelLocation + "' loaded after ModelRegistryEvent"); + if (tooLate) throw new RuntimeException("PartialModel '" + modelLocation + "' loaded after ModelEvent.RegisterAdditional"); this.modelLocation = modelLocation; ALL.add(this); diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BakedModelBuilder.java b/src/main/java/com/jozufozu/flywheel/core/model/BakedModelBuilder.java index 14e59af85..3eb841ccf 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/BakedModelBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/BakedModelBuilder.java @@ -1,8 +1,6 @@ package com.jozufozu.flywheel.core.model; -import java.util.Random; - import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter; import com.jozufozu.flywheel.fabric.model.DefaultLayerFilteringBakedModel; import com.mojang.blaze3d.vertex.PoseStack; @@ -12,6 +10,7 @@ import net.minecraft.client.renderer.block.ModelBlockRenderer; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; @@ -42,7 +41,7 @@ public final class BakedModelBuilder implements Bufferable { } @Override - public void bufferInto(ModelBlockRenderer blockRenderer, VertexConsumer consumer, Random random) { + public void bufferInto(ModelBlockRenderer blockRenderer, VertexConsumer consumer, RandomSource random) { BakedModel model = DefaultLayerFilteringBakedModel.wrap(this.model); if (consumer instanceof ShadeSeparatingVertexConsumer shadeSeparatingWrapper) { model = shadeSeparatingWrapper.wrapModel(model); diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java index 76e23da8f..61df69354 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java @@ -1,9 +1,25 @@ package com.jozufozu.flywheel.core.model; +import java.nio.ByteBuffer; +import java.util.function.Supplier; + +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL32; +import org.lwjgl.system.MemoryUtil; + import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; +import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage; +import com.jozufozu.flywheel.backend.model.ElementBuffer; import com.jozufozu.flywheel.core.Formats; +import com.jozufozu.flywheel.core.QuadConverter; +import com.jozufozu.flywheel.util.Pair; +import com.mojang.blaze3d.platform.MemoryTracker; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer; import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexFormat.IndexType; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.model.BakedModel; @@ -16,6 +32,7 @@ public class BlockModel implements Model { private final VertexList reader; private final String name; + private final EBOSupplier eboSupplier; public BlockModel(BlockState state) { this(Minecraft.getInstance() @@ -36,9 +53,19 @@ public class BlockModel implements Model { this(bufferable.build(), name); } - public BlockModel(ShadeSeparatedBufferBuilder bufferBuilder, String name) { + public BlockModel(Pair pair, String name) { this.name = name; - reader = Formats.BLOCK.createReader(bufferBuilder); + + RenderedBuffer renderedBuffer = pair.first(); + BufferBuilder.DrawState drawState = renderedBuffer.drawState(); + reader = Formats.BLOCK.createReader(renderedBuffer, pair.second()); + + if (!drawState.sequentialIndex()) { + eboSupplier = new BufferEBOSupplier(renderedBuffer.indexBuffer(), drawState.indexCount(), drawState.indexType()); + } else { + eboSupplier = () -> QuadConverter.getInstance() + .quads2Tris(vertexCount() / 4); + } } @Override @@ -56,6 +83,11 @@ public class BlockModel implements Model { return reader; } + @Override + public ElementBuffer createEBO() { + return eboSupplier.get(); + } + @Override public VertexType getType() { return Formats.BLOCK; @@ -70,5 +102,55 @@ public class BlockModel implements Model { // } } + eboSupplier.delete(); + } + + private interface EBOSupplier extends Supplier { + default void delete() { + } + } + + private static class BufferEBOSupplier implements EBOSupplier { + private final ByteBuffer indexBuffer; + private final int indexCount; + private final IndexType indexType; + + private int eboName = -1; + private ElementBuffer ebo; + + public BufferEBOSupplier(ByteBuffer indexBufferSrc, int indexCount, IndexType indexType) { + indexBuffer = MemoryTracker.create(indexBufferSrc.capacity()); + MemoryUtil.memCopy(indexBufferSrc, indexBuffer); + this.indexCount = indexCount; + this.indexType = indexType; + } + + @Override + public ElementBuffer get() { + if (eboName == -1) { + eboName = createEBO(); + ebo = new ElementBuffer(eboName, indexCount, indexType); + MemoryUtil.memFree(indexBuffer); + } + + return ebo; + } + + private int createEBO() { + int vbo = GL32.glGenBuffers(); + + // XXX ARRAY_BUFFER is bound and restored + var bufferType = GlBufferType.ARRAY_BUFFER; + var oldBuffer = bufferType.getBoundBuffer(); + bufferType.bind(vbo); + GL15.glBufferData(bufferType.glEnum, indexBuffer, GlBufferUsage.STATIC_DRAW.glEnum); + bufferType.bind(oldBuffer); + return vbo; + } + + @Override + public void delete() { + GL32.glDeleteBuffers(eboName); + } } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/Bufferable.java b/src/main/java/com/jozufozu/flywheel/core/model/Bufferable.java index ee78333ad..e01fecd82 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/Bufferable.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/Bufferable.java @@ -1,19 +1,20 @@ package com.jozufozu.flywheel.core.model; -import java.util.Random; - +import com.jozufozu.flywheel.util.Pair; +import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.renderer.block.ModelBlockRenderer; +import net.minecraft.util.RandomSource; /** * An interface for objects that can "rendered" into a BufferBuilder. */ public interface Bufferable { - void bufferInto(ModelBlockRenderer renderer, VertexConsumer consumer, Random random); + void bufferInto(ModelBlockRenderer renderer, VertexConsumer consumer, RandomSource random); - default ShadeSeparatedBufferBuilder build() { - return ModelUtil.getBufferBuilder(this); + default Pair build() { + return ModelUtil.getRenderedBuffer(this); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java index 550671b30..7d5519886 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java @@ -1,11 +1,13 @@ package com.jozufozu.flywheel.core.model; import java.util.Collection; -import java.util.Random; import java.util.function.Supplier; +import com.jozufozu.flywheel.backend.model.BufferBuilderExtension; +import com.jozufozu.flywheel.util.Pair; import com.jozufozu.flywheel.util.transform.TransformStack; import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexFormat; @@ -15,6 +17,7 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.ModelBlockRenderer; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; @@ -22,7 +25,18 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp public class ModelUtil { private static final ThreadLocal THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new); - public static ShadeSeparatedBufferBuilder getBufferBuilder(Bufferable bufferable) { + public static Pair endShadeSeparated(BufferBuilder shadedBuilder, BufferBuilder unshadedBuilder) { + int unshadedStartVertex = ((BufferBuilderExtension) shadedBuilder).flywheel$getVertices(); + RenderedBuffer unshadedBuffer = unshadedBuilder.endOrDiscardIfEmpty(); + if (unshadedBuffer != null) { + // FIXME: Unshaded indices + ((BufferBuilderExtension) shadedBuilder).flywheel$appendBufferUnsafe(unshadedBuffer.vertexBuffer()); + } + RenderedBuffer buffer = shadedBuilder.end(); + return Pair.of(buffer, unshadedStartVertex); + } + + public static Pair getRenderedBuffer(Bufferable bufferable) { ModelBlockRenderer blockRenderer = Minecraft.getInstance().getBlockRenderer().getModelRenderer(); ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get(); @@ -30,31 +44,29 @@ public class ModelUtil { bufferable.bufferInto(blockRenderer, objects.shadeSeparatingWrapper, objects.random); - objects.end(); - - return objects.separatedBufferBuilder; + return objects.end(); } - public static ShadeSeparatedBufferBuilder getBufferBuilder(BakedModel model, BlockState referenceState, PoseStack poseStack) { + public static Pair getBufferBuilder(BakedModel model, BlockState referenceState, PoseStack poseStack) { return new BakedModelBuilder(model).withReferenceState(referenceState) .withPoseStack(poseStack) .build(); } - public static ShadeSeparatedBufferBuilder getBufferBuilder(BlockAndTintGetter renderWorld, BakedModel model, BlockState referenceState, PoseStack poseStack) { + public static Pair getBufferBuilder(BlockAndTintGetter renderWorld, BakedModel model, BlockState referenceState, PoseStack poseStack) { return new BakedModelBuilder(model).withReferenceState(referenceState) .withPoseStack(poseStack) .withRenderWorld(renderWorld) .build(); } - public static ShadeSeparatedBufferBuilder getBufferBuilderFromTemplate(BlockAndTintGetter renderWorld, RenderType layer, Collection blocks) { + public static Pair getBufferBuilderFromTemplate(BlockAndTintGetter renderWorld, RenderType layer, Collection blocks) { return new WorldModelBuilder(layer).withRenderWorld(renderWorld) .withBlocks(blocks) .build(); } - public static ShadeSeparatedBufferBuilder getBufferBuilderFromTemplate(BlockAndTintGetter renderWorld, RenderType layer, Collection blocks, PoseStack poseStack) { + public static Pair getBufferBuilderFromTemplate(BlockAndTintGetter renderWorld, RenderType layer, Collection blocks, PoseStack poseStack) { return new WorldModelBuilder(layer).withRenderWorld(renderWorld) .withBlocks(blocks) .withPoseStack(poseStack) @@ -73,22 +85,20 @@ public class ModelUtil { } private static class ThreadLocalObjects { - public final Random random = new Random(); + public final RandomSource random = RandomSource.create(); public final ShadeSeparatingVertexConsumer shadeSeparatingWrapper = new ShadeSeparatingVertexConsumer(); - public final ShadeSeparatedBufferBuilder separatedBufferBuilder = new ShadeSeparatedBufferBuilder(512); + public final BufferBuilder shadedBuilder = new BufferBuilder(512); public final BufferBuilder unshadedBuilder = new BufferBuilder(512); private void begin() { - this.separatedBufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); + this.shadedBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); this.unshadedBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); - this.shadeSeparatingWrapper.prepare(this.separatedBufferBuilder, this.unshadedBuilder); + this.shadeSeparatingWrapper.prepare(this.shadedBuilder, this.unshadedBuilder); } - private void end() { + private Pair end() { this.shadeSeparatingWrapper.clear(); - this.unshadedBuilder.end(); - this.separatedBufferBuilder.appendUnshadedVertices(this.unshadedBuilder); - this.separatedBufferBuilder.end(); + return ModelUtil.endShadeSeparated(shadedBuilder, unshadedBuilder); } } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ShadeSeparatedBufferBuilder.java b/src/main/java/com/jozufozu/flywheel/core/model/ShadeSeparatedBufferBuilder.java deleted file mode 100644 index e7ca9f064..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/model/ShadeSeparatedBufferBuilder.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.jozufozu.flywheel.core.model; - -import java.nio.ByteBuffer; - -import com.jozufozu.flywheel.backend.model.BufferBuilderExtension; -import com.jozufozu.flywheel.fabric.helper.BufferBuilderHelper; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.datafixers.util.Pair; - -public class ShadeSeparatedBufferBuilder extends BufferBuilder { - protected int unshadedStartVertex; - - public ShadeSeparatedBufferBuilder(int capacity) { - super(capacity); - } - - public void appendUnshadedVertices(BufferBuilder unshadedBuilder) { - Pair data = unshadedBuilder.popNextBuffer(); - ByteBuffer buffer = data.getSecond(); - BufferBuilderHelper.fixByteOrder(unshadedBuilder, buffer); - unshadedStartVertex = ((BufferBuilderExtension) this).flywheel$getVertices(); - ((BufferBuilderExtension) this).flywheel$appendBufferUnsafe(buffer); - } - - public int getUnshadedStartVertex() { - return unshadedStartVertex; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ShadeSeparatingVertexConsumer.java b/src/main/java/com/jozufozu/flywheel/core/model/ShadeSeparatingVertexConsumer.java index 307ffbbd3..36b6706fc 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ShadeSeparatingVertexConsumer.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ShadeSeparatingVertexConsumer.java @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.core.model; -import java.util.Random; import java.util.function.Supplier; import com.jozufozu.flywheel.fabric.model.FabricModelUtil; @@ -13,6 +12,7 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; @@ -113,7 +113,7 @@ public class ShadeSeparatingVertexConsumer implements VertexConsumer { } @Override - public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { + public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { context.pushTransform(quadTransform); super.emitBlockQuads(blockView, state, pos, randomSupplier, context); context.popTransform(); diff --git a/src/main/java/com/jozufozu/flywheel/core/model/WorldModelBuilder.java b/src/main/java/com/jozufozu/flywheel/core/model/WorldModelBuilder.java index db7313c16..82a868b8b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/WorldModelBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/WorldModelBuilder.java @@ -2,7 +2,6 @@ package com.jozufozu.flywheel.core.model; import java.util.Collection; import java.util.Collections; -import java.util.Random; import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter; import com.jozufozu.flywheel.fabric.model.CullingBakedModel; @@ -19,6 +18,7 @@ import net.minecraft.client.renderer.block.ModelBlockRenderer; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; @@ -36,7 +36,7 @@ public final class WorldModelBuilder implements Bufferable { } @Override - public void bufferInto(ModelBlockRenderer modelRenderer, VertexConsumer consumer, Random random) { + public void bufferInto(ModelBlockRenderer modelRenderer, VertexConsumer consumer, RandomSource random) { BlockRenderDispatcher dispatcher = Minecraft.getInstance().getBlockRenderer(); ModelBlockRenderer.enableCaching(); @@ -87,6 +87,6 @@ public final class WorldModelBuilder implements Bufferable { } public BlockModel intoMesh(String name) { - return new BlockModel(ModelUtil.getBufferBuilder(this), name); + return new BlockModel(this, name); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/ShaderSources.java b/src/main/java/com/jozufozu/flywheel/core/source/ShaderSources.java index c4b42ec10..00ec7e505 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/ShaderSources.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/ShaderSources.java @@ -1,8 +1,8 @@ package com.jozufozu.flywheel.core.source; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -28,24 +28,25 @@ public class ShaderSources implements SourceFinder { public final Index index; public ShaderSources(ResourceManager manager) { - Collection allShaders = manager.listResources(SHADER_DIR, s -> { + Map allShaders = manager.listResources(SHADER_DIR, loc -> { + String path = loc.getPath(); for (String ext : EXTENSIONS) { - if (s.endsWith(ext)) return true; + if (path.endsWith(ext)) return true; } return false; }); - for (ResourceLocation location : allShaders) { - try (Resource resource = manager.getResource(location)) { - String source = StringUtil.readToString(resource.getInputStream()); + allShaders.forEach((location, resource) -> { + try (InputStream inputStream = resource.open()) { + String source = StringUtil.readToString(inputStream); ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR); shaderSources.put(name, new SourceFile(this, name, source)); - } catch (IOException e) { - // + } catch (IOException ignored) { + } - } + }); index = new Index(shaderSources); } diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java index ff218762a..f65c59627 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java @@ -6,11 +6,8 @@ import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.CommonItems; -import com.jozufozu.flywheel.core.model.ShadeSeparatedBufferBuilder; -import com.jozufozu.flywheel.fabric.helper.BufferBuilderHelper; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.datafixers.util.Pair; public class BlockVertex implements VertexType { @@ -63,21 +60,21 @@ Vertex FLWCreateVertex() { return new BlockVertexListUnsafe.Shaded(buffer, vertexCount, unshadedStartVertex); } - public VertexList createReader(BufferBuilder bufferBuilder) { + public VertexList createReader(BufferBuilder.RenderedBuffer renderedBuffer, int unshadedStartVertex) { + // TODO: try to avoid virtual model rendering - Pair pair = bufferBuilder.popNextBuffer(); - BufferBuilder.DrawState drawState = pair.getFirst(); + BufferBuilder.DrawState drawState = renderedBuffer.drawState(); if (drawState.format() != DefaultVertexFormat.BLOCK) { throw new RuntimeException("Cannot use BufferBuilder with " + drawState.format()); } + ByteBuffer vertexBuffer = renderedBuffer.vertexBuffer(); - ByteBuffer buffer = pair.getSecond(); - BufferBuilderHelper.fixByteOrder(bufferBuilder, buffer); - if (bufferBuilder instanceof ShadeSeparatedBufferBuilder separated) { - return createReader(buffer, drawState.vertexCount(), separated.getUnshadedStartVertex()); + int vertexCount = drawState.vertexCount(); + if (unshadedStartVertex >= 0 && unshadedStartVertex < vertexCount) { + return createReader(vertexBuffer, vertexCount, unshadedStartVertex); } else { - return createReader(buffer, drawState.vertexCount()); + return createReader(vertexBuffer, vertexCount); } } } diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java index 5324d15be..d37d6fd21 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java @@ -28,7 +28,7 @@ import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.UpgradeData; import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature; +import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.FluidState; @@ -194,39 +194,39 @@ public class VirtualChunk extends ChunkAccess { @Override @Nullable - public StructureStart getStartForFeature(ConfiguredStructureFeature pStructure) { + public StructureStart getStartForStructure(Structure structure) { return null; } @Override - public void setStartForFeature(ConfiguredStructureFeature pStructure, StructureStart pStart) { + public void setStartForStructure(Structure structure, StructureStart start) { } @Override - public Map, StructureStart> getAllStarts() { + public Map getAllStarts() { return Collections.emptyMap(); } @Override - public void setAllStarts(Map, StructureStart> pStructureStarts) { + public void setAllStarts(Map structureStarts) { } @Override - public LongSet getReferencesForFeature(ConfiguredStructureFeature pStructure) { + public LongSet getReferencesForStructure(Structure structure) { return LongSets.emptySet(); } @Override - public void addReferenceForFeature(ConfiguredStructureFeature pStructure, long pReference) { + public void addReferenceForStructure(Structure structure, long reference) { } @Override - public Map, LongSet> getAllReferences() { + public Map getAllReferences() { return Collections.emptyMap(); } @Override - public void setAllReferences(Map, LongSet> pStructureReferences) { + public void setAllReferences(Map structureReferences) { } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java index 9bc1871be..f256b488d 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java @@ -36,10 +36,12 @@ import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.entity.LevelEntityGetter; import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.gameevent.GameEvent.Context; import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.saveddata.maps.MapItemSavedData; import net.minecraft.world.level.storage.WritableLevelData; +import net.minecraft.world.phys.Vec3; import net.minecraft.world.scores.Scoreboard; import net.minecraft.world.ticks.LevelTickAccess; @@ -68,7 +70,7 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { public VirtualRenderWorld(Level level, Vec3i biomeOffset, int height, int minBuildHeight) { super((WritableLevelData) level.getLevelData(), level.dimension(), level.dimensionTypeRegistration(), level::getProfiler, - true, false, 0); + true, false, 0, 0); this.biomeOffset = biomeOffset; this.level = level; this.height = nextMultipleOf16(height); @@ -289,6 +291,17 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { @Override public void levelEvent(@Nullable Player player, int type, BlockPos pos, int data) {} + @Override + public void gameEvent(GameEvent p_220404_, Vec3 p_220405_, Context p_220406_) {} + + @Override + public void playSeededSound(Player p_220363_, double p_220364_, double p_220365_, double p_220366_, + SoundEvent p_220367_, SoundSource p_220368_, float p_220369_, float p_220370_, long p_220371_) {} + + @Override + public void playSeededSound(Player p_220372_, Entity p_220373_, SoundEvent p_220374_, SoundSource p_220375_, + float p_220376_, float p_220377_, long p_220378_) {} + @Override public void playSound(@Nullable Player player, double x, double y, double z, SoundEvent soundIn, SoundSource category, float volume, float pitch) {} diff --git a/src/main/java/com/jozufozu/flywheel/fabric/model/CullingBakedModel.java b/src/main/java/com/jozufozu/flywheel/fabric/model/CullingBakedModel.java index 897935401..ae5ace70f 100644 --- a/src/main/java/com/jozufozu/flywheel/fabric/model/CullingBakedModel.java +++ b/src/main/java/com/jozufozu/flywheel/fabric/model/CullingBakedModel.java @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.fabric.model; -import java.util.Random; import java.util.function.Supplier; import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; @@ -8,6 +7,7 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; @@ -33,7 +33,7 @@ public class CullingBakedModel extends ForwardingBakedModel { } @Override - public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { + public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { completionFlags = 0; resultFlags = 0; context.pushTransform(quad -> { diff --git a/src/main/java/com/jozufozu/flywheel/fabric/model/DefaultLayerFilteringBakedModel.java b/src/main/java/com/jozufozu/flywheel/fabric/model/DefaultLayerFilteringBakedModel.java index 5816bb5ad..7000fd2f1 100644 --- a/src/main/java/com/jozufozu/flywheel/fabric/model/DefaultLayerFilteringBakedModel.java +++ b/src/main/java/com/jozufozu/flywheel/fabric/model/DefaultLayerFilteringBakedModel.java @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.fabric.model; -import java.util.Random; import java.util.function.Supplier; import net.fabricmc.fabric.api.renderer.v1.material.BlendMode; @@ -10,6 +9,7 @@ import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; @@ -30,14 +30,14 @@ public class DefaultLayerFilteringBakedModel extends ForwardingBakedModel { } @Override - public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { + public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { context.pushTransform(DefaultLayerFilteringBakedModel::hasDefaultBlendMode); super.emitBlockQuads(blockView, state, pos, randomSupplier, context); context.popTransform(); } @Override - public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { + public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { context.pushTransform(DefaultLayerFilteringBakedModel::hasDefaultBlendMode); super.emitItemQuads(stack, randomSupplier, context); context.popTransform(); diff --git a/src/main/java/com/jozufozu/flywheel/fabric/model/LayerFilteringBakedModel.java b/src/main/java/com/jozufozu/flywheel/fabric/model/LayerFilteringBakedModel.java index 676cdfb1c..332054798 100644 --- a/src/main/java/com/jozufozu/flywheel/fabric/model/LayerFilteringBakedModel.java +++ b/src/main/java/com/jozufozu/flywheel/fabric/model/LayerFilteringBakedModel.java @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.fabric.model; -import java.util.Random; import java.util.function.Supplier; import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; @@ -9,6 +8,7 @@ import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; @@ -33,7 +33,7 @@ public class LayerFilteringBakedModel extends ForwardingBakedModel { } @Override - public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { + public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { RenderType defaultLayer = ItemBlockRenderTypes.getChunkRenderType(state); if (super.isVanillaAdapter()) { if (defaultLayer == targetLayer) { diff --git a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRebuildHooksMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRebuildHooksMixin.java index e6b0f404a..c407fd2ba 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRebuildHooksMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRebuildHooksMixin.java @@ -1,30 +1,78 @@ package com.jozufozu.flywheel.mixin.instancemanage; -import java.util.Set; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; +import com.jozufozu.flywheel.util.RenderChunkExtension; import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; +import net.minecraft.client.renderer.chunk.RenderChunkRegion; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; @Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$RenderChunk$RebuildTask") -public class ChunkRebuildHooksMixin { +public abstract class ChunkRebuildHooksMixin { + @Unique + private Level flywheel$level; - @Inject(method = "handleBlockEntity", at = @At("HEAD"), cancellable = true) - private void addAndFilterBEs(ChunkRenderDispatcher.CompiledChunk compiledChunk, Set set, E be, CallbackInfo ci) { - if (Backend.canUseInstancing(be.getLevel())) { - if (InstancedRenderRegistry.canInstance(be.getType())) - InstancedRenderDispatcher.getBlockEntities(be.getLevel()).queueAdd(be); + @Inject(method = "(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;DLnet/minecraft/client/renderer/chunk/RenderChunkRegion;Z)V", at = @At("RETURN")) + private void setLevel(ChunkRenderDispatcher.RenderChunk this$1, double p_194427_, RenderChunkRegion region, boolean p_194429_, CallbackInfo ci) { + flywheel$level = ((RenderChunkExtension) this$1).flywheel$getLevel(); + } - if (InstancedRenderRegistry.shouldSkipRender(be)) - ci.cancel(); + @Redirect(method = "doTask", at = @At(value = "INVOKE", target = "Ljava/util/List;addAll(Ljava/util/Collection;)Z")) + private boolean addAndFilterBEs(List self, Collection es) { + if (!Backend.canUseInstancing(flywheel$level)) { + return self.addAll(es); } + + boolean added = false; + var instanced = new ArrayList(); + for (E be : es) { + if (InstancedRenderRegistry.canInstance(be.getType())) { + instanced.add(be); + } + + if (!InstancedRenderRegistry.shouldSkipRender(be)) { + self.add(be); + added = true; + } + } + InstancedRenderDispatcher.getBlockEntities(flywheel$level).queueAddAll(instanced); + return added; + } + + @Redirect(method = "doTask", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;updateGlobalBlockEntities(Ljava/util/Collection;)V")) + private void addAndFilterBEs(ChunkRenderDispatcher.RenderChunk self, Collection bes) { + if (!Backend.canUseInstancing(flywheel$level)) { + ((RenderChunkAccessor) self).flywheel$updateGlobalBlockEntities(bes); + return; + } + + var global = new ArrayList(); + var instanced = new ArrayList(); + for (BlockEntity be : bes) { + if (InstancedRenderRegistry.canInstance(be.getType())) { + instanced.add(be); + } + + if (!InstancedRenderRegistry.shouldSkipRender(be)) { + global.add(be); + } + } + + InstancedRenderDispatcher.getBlockEntities(flywheel$level).queueAddAll(instanced); + ((RenderChunkAccessor) self).flywheel$updateGlobalBlockEntities(global); } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRenderDispatcherAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRenderDispatcherAccessor.java new file mode 100644 index 000000000..7e8e0de6d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/ChunkRenderDispatcherAccessor.java @@ -0,0 +1,14 @@ +package com.jozufozu.flywheel.mixin.instancemanage; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; + +@Mixin(ChunkRenderDispatcher.class) +public interface ChunkRenderDispatcherAccessor { + + @Accessor + ClientLevel getLevel(); +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkAccessor.java new file mode 100644 index 000000000..9bd5a2d3a --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkAccessor.java @@ -0,0 +1,19 @@ +package com.jozufozu.flywheel.mixin.instancemanage; + +import java.util.Collection; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; +import net.minecraft.world.level.block.entity.BlockEntity; + +/** + * For use in {@link ChunkRebuildHooksMixin#addAndFilterBEs(ChunkRenderDispatcher.RenderChunk, Collection)} + */ +@Mixin(ChunkRenderDispatcher.RenderChunk.class) +public interface RenderChunkAccessor { + + @Invoker("updateGlobalBlockEntities") + void flywheel$updateGlobalBlockEntities(Collection blockEntities); +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkMixin.java new file mode 100644 index 000000000..640d9a1c2 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/RenderChunkMixin.java @@ -0,0 +1,21 @@ +package com.jozufozu.flywheel.mixin.instancemanage; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; + +@Mixin(ChunkRenderDispatcher.RenderChunk.class) +public class RenderChunkMixin implements com.jozufozu.flywheel.util.RenderChunkExtension { + + @Shadow(aliases = {"this$0", "field_20833", "f_dssekupm"}) // Optifine does not use the obfuscated name so the mapped name must be included as an alias + @Final + private ChunkRenderDispatcher this$0; + + @Override + public ClientLevel flywheel$getLevel() { + return ((ChunkRenderDispatcherAccessor) this$0).getLevel(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/SodiumChunkRenderDataMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/SodiumChunkRenderDataMixin.java new file mode 100644 index 000000000..732120536 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/instancemanage/SodiumChunkRenderDataMixin.java @@ -0,0 +1,62 @@ +package com.jozufozu.flywheel.mixin.instancemanage; + +import java.util.ArrayList; +import java.util.List; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; + +import me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderData; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +@Mixin(targets = "me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderData$Builder", remap = false) +public class SodiumChunkRenderDataMixin { + + @Unique + private List flywheel$blockEntities; + @Unique + private Level flywheel$level; + + @Inject(method = "addBlockEntity", at = @At("HEAD"), cancellable = true, require = 0) + private void flywheel$onAddBlockEntity(BlockEntity be, boolean cull, CallbackInfo ci) { + if (flywheel$level == null) { + flywheel$level = be.getLevel(); + } + + if (!Backend.canUseInstancing(flywheel$level)) { + return; + } + + if (InstancedRenderRegistry.canInstance(be.getType())) { + if (flywheel$blockEntities == null) { + flywheel$blockEntities = new ArrayList<>(); + } + + // Collect BEs in a temporary list to avoid excessive synchronization in InstancedRenderDispatcher. + flywheel$blockEntities.add(be); + } + + if (InstancedRenderRegistry.shouldSkipRender(be)) { + ci.cancel(); + } + } + + @Inject(method = "build", at = @At("HEAD")) + private void flywheel$onBuild(CallbackInfoReturnable cir) { + if (flywheel$level == null || flywheel$blockEntities == null || !Backend.canUseInstancing(flywheel$level)) { + return; + } + + InstancedRenderDispatcher.getBlockEntities(flywheel$level) + .queueAddAll(flywheel$blockEntities); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/util/RenderChunkExtension.java b/src/main/java/com/jozufozu/flywheel/util/RenderChunkExtension.java new file mode 100644 index 000000000..acaa7faff --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/RenderChunkExtension.java @@ -0,0 +1,7 @@ +package com.jozufozu.flywheel.util; + +import net.minecraft.client.multiplayer.ClientLevel; + +public interface RenderChunkExtension { + ClientLevel flywheel$getLevel(); +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 3ebca6259..9063a9883 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -33,7 +33,7 @@ "depends": { "fabricloader": ">=0.11.3", "fabric": "*", - "minecraft": ">=1.18.2", + "minecraft": ">=1.19.2", "java": ">=17" } } diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index 4be4fc254..6e4f47a3c 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -22,8 +22,12 @@ "atlas.AtlasDataMixin", "atlas.SheetDataAccessor", "instancemanage.ChunkRebuildHooksMixin", + "instancemanage.ChunkRenderDispatcherAccessor", "instancemanage.InstanceAddMixin", "instancemanage.InstanceRemoveMixin", + "instancemanage.RenderChunkAccessor", + "instancemanage.RenderChunkMixin", + "instancemanage.SodiumChunkRenderDataMixin", "light.LightUpdateMixin", "light.NetworkLightUpdateMixin", "matrix.Matrix3fMixin",