From 4f407d8659a553779731765308091b318d81c4dc Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 26 Jul 2022 19:12:18 -0700 Subject: [PATCH 01/13] Culling experiments - Cull updates based on the view frustum. - Instances check themselves against a FrustumIntersection object - Make GlProgram not abstract - Leave in small debug rendering experiment --- .../java/com/jozufozu/flywheel/Flywheel.java | 7 +- .../flywheel/api/instance/Instance.java | 10 + .../com/jozufozu/flywheel/backend/Loader.java | 6 +- .../flywheel/backend/gl/shader/GlProgram.java | 15 +- .../backend/instancing/InstanceManager.java | 31 +-- .../backend/instancing/InstanceWorld.java | 5 +- .../blockentity/BlockEntityInstance.java | 14 +- .../instancing/entity/EntityInstance.java | 8 + .../instancing/InstancingEngine.java | 6 +- .../jozufozu/flywheel/core/DebugRender.java | 104 ++++++++ .../jozufozu/flywheel/core/RenderContext.java | 56 ++-- .../flywheel/core/compile/DebugCompiler.java | 89 +++++++ .../core/compile/FragmentCompiler.java | 72 ----- .../core/compile/InstancedArraysCompiler.java | 248 ++++++++++++++++++ .../core/compile/ProgramCompiler.java | 88 ------- .../flywheel/core/compile/VertexCompiler.java | 99 ------- .../crumbling/CrumblingInstanceManager.java | 17 -- .../core/crumbling/CrumblingRenderer.java | 4 +- .../model/TransformedWriterUnsafe.java | 6 +- .../flywheel/core/uniform/ViewProvider.java | 4 +- .../flywheel/mixin/ClientLevelMixin.java | 2 +- .../flywheel/mixin/LevelRendererMixin.java | 4 +- .../flywheel/mixin/RenderTypeMixin.java | 2 +- .../flywheel/mixin/matrix/Matrix3fMixin.java | 10 +- .../flywheel/mixin/matrix/Matrix4fMixin.java | 14 +- .../java/com/jozufozu/flywheel/util/Lazy.java | 4 + .../{ => extension}/ClientLevelExtension.java | 2 +- .../util/extension/Matrix3fExtension.java | 16 ++ .../util/extension/Matrix4fExtension.java | 16 ++ .../MatrixExtension.java} | 8 +- .../{ => extension}/RenderTypeExtension.java | 2 +- .../util/joml/FrustumIntersection.java | 39 +++ .../flywheel/util/joml/Quaternionf.java | 9 + .../vanilla/effect/ExampleEffect.java | 6 + .../assets/flywheel/flywheel/debug/debug.frag | 5 + .../assets/flywheel/flywheel/debug/debug.vert | 7 + 36 files changed, 648 insertions(+), 387 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/core/DebugRender.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/compile/DebugCompiler.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/compile/InstancedArraysCompiler.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java rename src/main/java/com/jozufozu/flywheel/util/{ => extension}/ClientLevelExtension.java (94%) create mode 100644 src/main/java/com/jozufozu/flywheel/util/extension/Matrix3fExtension.java create mode 100644 src/main/java/com/jozufozu/flywheel/util/extension/Matrix4fExtension.java rename src/main/java/com/jozufozu/flywheel/util/{MatrixWrite.java => extension/MatrixExtension.java} (70%) rename src/main/java/com/jozufozu/flywheel/util/{ => extension}/RenderTypeExtension.java (93%) create mode 100644 src/main/resources/assets/flywheel/flywheel/debug/debug.frag create mode 100644 src/main/resources/assets/flywheel/flywheel/debug/debug.vert diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java index 7aaa6a7f6..afcffa837 100644 --- a/src/main/java/com/jozufozu/flywheel/Flywheel.java +++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java @@ -12,15 +12,15 @@ import com.jozufozu.flywheel.config.BackendTypeArgument; import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.DebugRender; import com.jozufozu.flywheel.core.PartialModel; import com.jozufozu.flywheel.core.QuadConverter; import com.jozufozu.flywheel.core.StitchedSprite; -import com.jozufozu.flywheel.core.compile.ProgramCompiler; +import com.jozufozu.flywheel.core.compile.InstancedArraysCompiler; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.model.Models; import com.jozufozu.flywheel.event.EntityWorldHandler; import com.jozufozu.flywheel.event.ForgeEvents; -import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor; import com.jozufozu.flywheel.vanilla.VanillaInstances; import com.mojang.logging.LogUtils; @@ -80,7 +80,7 @@ public class Flywheel { forgeEventBus.addListener(FlwCommands::registerClientCommands); forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onRendererReload); - forgeEventBus.addListener(ProgramCompiler::invalidateAll); + forgeEventBus.addListener(InstancedArraysCompiler::invalidateAll); forgeEventBus.addListener(Models::onReload); forgeEventBus.addListener(MeshPool::reset); forgeEventBus.addListener(CrumblingRenderer::onReloadRenderers); @@ -108,6 +108,7 @@ public class Flywheel { // forgeEventBus.addListener(ExampleEffect::onReload); Components.init(); + DebugRender.init(); VanillaInstances.init(); diff --git a/src/main/java/com/jozufozu/flywheel/api/instance/Instance.java b/src/main/java/com/jozufozu/flywheel/api/instance/Instance.java index 6cc6e89d8..c52837e45 100644 --- a/src/main/java/com/jozufozu/flywheel/api/instance/Instance.java +++ b/src/main/java/com/jozufozu/flywheel/api/instance/Instance.java @@ -1,9 +1,19 @@ package com.jozufozu.flywheel.api.instance; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; + import net.minecraft.core.BlockPos; public interface Instance { BlockPos getWorldPosition(); + /** + * Check this instance against a frustum.

+ * An implementor may choose to return a constant to skip the frustum check. + * @param frustum A frustum intersection tester for the current frame. + * @return {@code true} if this instance should be considered for updates. + */ + boolean checkFrustum(FrustumIntersection frustum); + boolean isRemoved(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index 3519ace3e..dfef2748b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -6,7 +6,7 @@ import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.core.ComponentRegistry; import com.jozufozu.flywheel.core.compile.ContextShader; -import com.jozufozu.flywheel.core.compile.ProgramCompiler; +import com.jozufozu.flywheel.core.compile.InstancedArraysCompiler; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.ShaderLoadingException; @@ -70,8 +70,8 @@ public class Loader implements ResourceManagerReloadListener { for (StructType structType : ComponentRegistry.structTypes) { for (VertexType vertexType : ComponentRegistry.vertexTypes) { for (ContextShader contextShader : ComponentRegistry.contextShaders) { - var ctx = new ProgramCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader); - ProgramCompiler.INSTANCE.getProgram(ctx); + var ctx = new InstancedArraysCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader); + InstancedArraysCompiler.INSTANCE.getProgram(ctx); } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java index 84650d9b2..ea80d4cf5 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java @@ -5,25 +5,19 @@ import static org.lwjgl.opengl.GL20.glGetUniformLocation; import static org.lwjgl.opengl.GL20.glUniform1i; import static org.lwjgl.opengl.GL20.glUniformMatrix4fv; -import java.nio.FloatBuffer; - import org.jetbrains.annotations.NotNull; -import org.lwjgl.system.MemoryStack; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlObject; import com.mojang.blaze3d.shaders.ProgramManager; -import com.mojang.math.Matrix4f; import net.minecraft.resources.ResourceLocation; -public abstract class GlProgram extends GlObject { - private static final FloatBuffer floatBuffer = MemoryStack.stackGet() - .mallocFloat(16); +public class GlProgram extends GlObject { public final ResourceLocation name; - protected GlProgram(ResourceLocation name, int handle) { + public GlProgram(ResourceLocation name, int handle) { this.name = name; setHandle(handle); } @@ -70,11 +64,6 @@ public abstract class GlProgram extends GlObject { return samplerUniform; } - protected static void uploadMatrixUniform(int uniform, Matrix4f mat) { - mat.store(floatBuffer); - glUniformMatrix4fv(uniform, false, floatBuffer); - } - @Override protected void deleteInternal(int handle) { glDeleteProgram(handle); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 297d9d0e9..319fb9607 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -15,9 +15,8 @@ import com.jozufozu.flywheel.backend.instancing.ratelimit.NonLimiter; import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.light.LightUpdater; -import com.mojang.math.Vector3f; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; -import net.minecraft.client.Camera; import net.minecraft.core.BlockPos; public abstract class InstanceManager { @@ -100,27 +99,26 @@ public abstract class InstanceManager { int dY = pos.getY() - cY; int dZ = pos.getZ() - cZ; - if (tick.shouldUpdate(dX, dY, dZ)) instance.tick(); + if (!tick.shouldUpdate(dX, dY, dZ)) { + return; + } + + instance.tick(); } public void beginFrame(TaskEngine taskEngine, RenderContext context) { frame.tick(); processQueuedAdditions(); - Camera camera = context.camera(); - Vector3f look = camera.getLookVector(); - float lookX = look.x(); - float lookY = look.y(); - float lookZ = look.z(); - // integer camera pos - BlockPos cameraIntPos = camera.getBlockPosition(); + BlockPos cameraIntPos = context.camera().getBlockPosition(); int cX = cameraIntPos.getX(); int cY = cameraIntPos.getY(); int cZ = cameraIntPos.getZ(); + FrustumIntersection culler = context.culler(); var instances = getStorage().getInstancesForUpdate(); - distributeWork(taskEngine, instances, instance -> updateInstance(instance, lookX, lookY, lookZ, cX, cY, cZ)); + distributeWork(taskEngine, instances, instance -> updateInstance(instance, culler, cX, cY, cZ)); } private static void distributeWork(TaskEngine taskEngine, List instances, Consumer action) { @@ -140,7 +138,7 @@ public abstract class InstanceManager { } } - protected void updateInstance(DynamicInstance dyn, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) { + protected void updateInstance(DynamicInstance dyn, FrustumIntersection test, int cX, int cY, int cZ) { if (!dyn.decreaseFramerateWithDistance()) { dyn.beginFrame(); return; @@ -151,15 +149,14 @@ public abstract class InstanceManager { int dY = worldPos.getY() - cY; int dZ = worldPos.getZ() - cZ; - // is it more than 2 blocks behind the camera? - int dist = 2; - float dot = (dX + lookX * dist) * lookX + (dY + lookY * dist) * lookY + (dZ + lookZ * dist) * lookZ; - if (dot < 0) { + if (!frame.shouldUpdate(dX, dY, dZ)) { return; } - if (frame.shouldUpdate(dX, dY, dZ)) + if (dyn.checkFrustum(test)) { dyn.beginFrame(); + } + } public void add(T obj) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index 3962d4d1a..ccc177125 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -13,7 +13,7 @@ import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.event.BeginFrameEvent; -import com.jozufozu.flywheel.util.ClientLevelExtension; +import com.jozufozu.flywheel.util.extension.ClientLevelExtension; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; @@ -133,10 +133,7 @@ public class InstanceWorld { */ public void renderStage(RenderContext context, RenderStage stage) { taskEngine.syncPoint(); - context.pushPose(); - context.translateBack(context.camera().getPosition()); engine.renderStage(taskEngine, context, stage); - context.popPose(); } /** diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/blockentity/BlockEntityInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/blockentity/BlockEntityInstance.java index 39a53e444..a46ce4f7c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/blockentity/BlockEntityInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/blockentity/BlockEntityInstance.java @@ -14,6 +14,7 @@ import com.jozufozu.flywheel.core.structs.model.TransformedPart; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart; import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.ImmutableBox; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.entity.BlockEntity; @@ -90,16 +91,13 @@ public abstract class BlockEntityInstance extends Abstrac return pos; } - protected InstancerFactory getTransformFactory() { - return instancerManager.factory(StructTypes.TRANSFORMED); - } - - protected InstancerFactory getOrientedFactory() { - return instancerManager.factory(StructTypes.ORIENTED); - } - @Override public ImmutableBox getVolume() { return GridAlignedBB.from(pos); } + + @Override + public boolean checkFrustum(FrustumIntersection frustum) { + return frustum.testAab(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java index 4e6926317..352aa832b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java @@ -8,6 +8,7 @@ import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceM import com.jozufozu.flywheel.light.LightListener; import com.jozufozu.flywheel.light.TickingLightListener; import com.jozufozu.flywheel.util.box.GridAlignedBB; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; import com.mojang.math.Vector3f; import net.minecraft.core.BlockPos; @@ -96,4 +97,11 @@ public abstract class EntityInstance extends AbstractInstance public BlockPos getWorldPosition() { return entity.blockPosition(); } + + @Override + public boolean checkFrustum(FrustumIntersection frustum) { + AABB aabb = entity.getBoundingBox(); + return frustum.testAab((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ, + (float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index 43d1acd41..45c14e724 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -20,7 +20,7 @@ import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.compile.ContextShader; -import com.jozufozu.flywheel.core.compile.ProgramCompiler; +import com.jozufozu.flywheel.core.compile.InstancedArraysCompiler; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.uniform.UniformBuffer; import com.jozufozu.flywheel.util.WeakHashSet; @@ -137,9 +137,9 @@ public class InstancingEngine implements Engine { .getInstanceShader(); Material material = desc.material(); - var ctx = new ProgramCompiler.Context(vertexType, material, instanceShader, context); + var ctx = new InstancedArraysCompiler.Context(vertexType, material, instanceShader, context); - ProgramCompiler.INSTANCE.getProgram(ctx) + InstancedArraysCompiler.INSTANCE.getProgram(ctx) .bind(); UniformBuffer.getInstance().sync(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/DebugRender.java b/src/main/java/com/jozufozu/flywheel/core/DebugRender.java new file mode 100644 index 000000000..70a0c4d1c --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/DebugRender.java @@ -0,0 +1,104 @@ +package com.jozufozu.flywheel.core; + +import org.lwjgl.opengl.GL46; +import org.lwjgl.system.MemoryStack; + +import com.jozufozu.flywheel.Flywheel; +import com.jozufozu.flywheel.backend.gl.GlStateTracker; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.core.compile.DebugCompiler; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.util.Lazy; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; +import com.mojang.blaze3d.systems.RenderSystem; + +public class DebugRender { + + private static final Lazy SHADER = Lazy.of(() -> DebugCompiler.INSTANCE.get(new DebugCompiler.Context(Files.VERTEX, Files.FRAGMENT))); + + private static final Lazy FRUSTUM_VBO = Lazy.of(Frustum::new); + + public static void init() { + Files.init(); + } + + public static void updateFrustum(FrustumIntersection culler) { + FRUSTUM_VBO.get() + .upload(culler); + } + + public static void drawFrustum() { + if (!FRUSTUM_VBO.isInitialized()) { + return; + } + + RenderSystem.disableCull(); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + + try (var ignored = GlStateTracker.getRestoreState()) { + SHADER.get() + .bind(); + FRUSTUM_VBO.get() + .draw(); + } + } + + public static class Files { + public static final FileResolution VERTEX = FileResolution.get(Flywheel.rl("debug/debug.vert")); + public static final FileResolution FRAGMENT = FileResolution.get(Flywheel.rl("debug/debug.frag")); + + public static void init() { + + } + } + + // FIXME: This never worked (and the thing it was meant to debug is already fixed), + // but it should be a quick turnaround + private static class Frustum { + private static final int[] indices = new int[]{ + 0, 2, 3, 0, 3, 1, + 2, 6, 7, 2, 7, 3, + 6, 4, 5, 6, 5, 7, + 4, 0, 1, 4, 1, 5, + 0, 4, 6, 0, 6, 2, + 1, 5, 7, 1, 7, 3, + }; + + private static final int elementCount = indices.length; + private static final int indicesSize = elementCount * 4; + private static final int verticesSize = 3 * 8 * 4; + private final int buffer; + private final int vao; + + public Frustum() { + // holy moly DSA is nice + buffer = GL46.glCreateBuffers(); + GL46.glNamedBufferStorage(buffer, verticesSize + indicesSize, GL46.GL_DYNAMIC_STORAGE_BIT); + GL46.glNamedBufferSubData(buffer, 0, indices); + + vao = GL46.glCreateVertexArrays(); + GL46.glEnableVertexArrayAttrib(vao, 0); + GL46.glVertexArrayElementBuffer(vao, buffer); + GL46.glVertexArrayVertexBuffer(vao, 0, buffer, indicesSize, 3 * 4); + GL46.glVertexArrayAttribFormat(vao, 0, 3, GL46.GL_FLOAT, false, 0); + } + + public void upload(FrustumIntersection culler) { + try (var stack = MemoryStack.stackPush()) { + var buf = stack.malloc(3 * 8 * 4); + + culler.bufferPlanes(buf); + + GL46.glNamedBufferSubData(buffer, indicesSize, buf); + } + } + + public void draw() { + GL46.glEnableVertexArrayAttrib(vao, 0); + GL46.glVertexArrayElementBuffer(vao, buffer); + GL46.glBindVertexArray(vao); + GL46.glDrawElements(GL46.GL_TRIANGLES, elementCount, GL46.GL_UNSIGNED_INT, 0); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/RenderContext.java b/src/main/java/com/jozufozu/flywheel/core/RenderContext.java index 6fd3effbb..121e2d93a 100644 --- a/src/main/java/com/jozufozu/flywheel/core/RenderContext.java +++ b/src/main/java/com/jozufozu/flywheel/core/RenderContext.java @@ -2,56 +2,19 @@ package com.jozufozu.flywheel.core; import org.jetbrains.annotations.NotNull; -import com.jozufozu.flywheel.util.transform.TransformStack; +import com.jozufozu.flywheel.util.extension.Matrix4fExtension; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; -import com.mojang.math.Quaternion; import net.minecraft.client.Camera; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.RenderBuffers; +import net.minecraft.world.phys.Vec3; public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack stack, Matrix4f viewProjection, - Matrix4f projection, RenderBuffers buffers, Camera camera) implements TransformStack { - - @Override - public TransformStack multiply(Quaternion quaternion) { - return TransformStack.cast(stack).multiply(quaternion); - } - - @Override - public TransformStack scale(float factorX, float factorY, float factorZ) { - return TransformStack.cast(stack).scale(factorX, factorY, factorZ); - } - - @Override - public TransformStack pushPose() { - stack.pushPose(); - return TransformStack.cast(stack); - } - - @Override - public TransformStack popPose() { - stack.popPose(); - return TransformStack.cast(stack); - } - - @Override - public TransformStack mulPose(Matrix4f pose) { - return TransformStack.cast(stack).mulPose(pose); - } - - @Override - public TransformStack mulNormal(Matrix3f normal) { - return TransformStack.cast(stack).mulNormal(normal); - } - - @Override - public TransformStack translate(double x, double y, double z) { - return TransformStack.cast(stack).translate(x, y, z); - } + Matrix4f projection, RenderBuffers buffers, Camera camera, FrustumIntersection culler) { @NotNull public static Matrix4f createViewProjection(PoseStack view, Matrix4f projection) { @@ -59,4 +22,15 @@ public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack viewProjection.multiply(view.last().pose()); return viewProjection; } + + public static FrustumIntersection createCuller(Camera camera, Matrix4f viewProjection) { + com.jozufozu.flywheel.util.joml.Matrix4f proj = Matrix4fExtension.clone(viewProjection); + + Vec3 cam = camera + .getPosition(); + + proj.translate((float) -cam.x, (float) -cam.y, (float) -cam.z); + + return new FrustumIntersection(proj); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/DebugCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/DebugCompiler.java new file mode 100644 index 000000000..319d2dc11 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/DebugCompiler.java @@ -0,0 +1,89 @@ +package com.jozufozu.flywheel.core.compile; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.source.CompilationContext; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.event.ReloadRenderersEvent; + +/** + * Simple shader compiler that pulls no excessive tricks.

+ * Useful for writing experimental shaders or + */ +public class DebugCompiler extends Memoizer { + + public static final DebugCompiler INSTANCE = new DebugCompiler(); + + private final ShaderCompiler shaderCompiler; + + private DebugCompiler() { + this.shaderCompiler = new ShaderCompiler(); + } + + @Override + public void invalidate() { + super.invalidate(); + shaderCompiler.invalidate(); + } + + @Override + protected GlProgram _create(DebugCompiler.Context ctx) { + + return new ProgramAssembler(ctx.vertex.getFileLoc()) + .attachShader(shaderCompiler.vertex(ctx.vertex)) + .attachShader(shaderCompiler.fragment(ctx.fragment)) + .link() + .build(GlProgram::new); + } + + @Override + protected void _destroy(GlProgram value) { + value.delete(); + } + + public static void invalidateAll(ReloadRenderersEvent ignored) { + INSTANCE.invalidate(); + } + + public record Context(FileResolution vertex, FileResolution fragment) { + } + + /** + * Handles compilation and deletion of vertex shaders. + */ + private static class ShaderCompiler extends Memoizer { + + public GlShader vertex(FileResolution source) { + return get(new Context(source, ShaderType.VERTEX)); + } + + public GlShader fragment(FileResolution source) { + return get(new Context(source, ShaderType.FRAGMENT)); + } + + @Override + protected GlShader _create(Context ctx) { + var index = new CompilationContext(); + + String source = CompileUtil.generateHeader(GLSLVersion.V420, ctx.type) + ctx.source.getFile() + .generateFinalSource(index); + + try { + return new GlShader(source, ctx.type, ImmutableList.of(ctx.source.getFileLoc())); + } catch (ShaderCompilationException e) { + throw e.withErrorLog(index); + } + } + + @Override + protected void _destroy(GlShader value) { + value.delete(); + } + + public record Context(FileResolution source, ShaderType type) { + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java deleted file mode 100644 index 2923de834..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.jozufozu.flywheel.core.compile; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.gl.GLSLVersion; -import com.jozufozu.flywheel.backend.gl.shader.GlShader; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.core.source.CompilationContext; -import com.jozufozu.flywheel.core.source.SourceFile; - -/** - * Handles compilation and deletion of fragment shaders. - */ -public class FragmentCompiler extends Memoizer { - - public FragmentCompiler() { - } - - @Override - protected GlShader _create(Context key) { - StringBuilder finalSource = new StringBuilder(); - - finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.FRAGMENT)); - - var ctx = new CompilationContext(); - - // MATERIAL - - SourceFile materialShader = key.materialShader; - finalSource.append(materialShader.generateFinalSource(ctx)); - - // CONTEXT - - SourceFile contextShaderSource = key.contextShader; - finalSource.append(contextShaderSource.generateFinalSource(ctx)); - - // MAIN - - finalSource.append(generateFooter()); - - try { - return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name)); - } catch (ShaderCompilationException e) { - throw e.withErrorLog(ctx); - } - } - - protected String generateFooter() { - return """ - void main() { - flw_initFragment(); - - flw_materialFragment(); - - flw_contextFragment(); - } - """; - } - - @Override - protected void _destroy(GlShader value) { - value.delete(); - } - - /** - * Represents the conditions under which a shader is compiled. - * - * @param materialShader The fragment material shader source. - */ - public record Context(SourceFile materialShader, SourceFile contextShader) { - - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/InstancedArraysCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/InstancedArraysCompiler.java new file mode 100644 index 000000000..0c5625db8 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/InstancedArraysCompiler.java @@ -0,0 +1,248 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.ArrayList; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.source.CompilationContext; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.core.source.parse.ShaderField; +import com.jozufozu.flywheel.core.source.span.Span; +import com.jozufozu.flywheel.event.ReloadRenderersEvent; +import com.jozufozu.flywheel.util.Pair; + +/** + * A caching compiler. + * + *

+ * This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of + * compiled programs, and will only compile a program if it is not already in the cache. + *

+ *

+ * A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload. + *

+ */ +public class InstancedArraysCompiler extends Memoizer { + + public static final InstancedArraysCompiler INSTANCE = new InstancedArraysCompiler(); + + private final VertexCompiler vertexCompiler; + private final FragmentCompiler fragmentCompiler; + + private InstancedArraysCompiler() { + this.vertexCompiler = new VertexCompiler(); + this.fragmentCompiler = new FragmentCompiler(); + } + + /** + * Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec. + * + * @param ctx The context of compilation. + * @return A compiled GlProgram. + */ + public GlProgram getProgram(InstancedArraysCompiler.Context ctx) { + return super.get(ctx); + } + + @Override + public void invalidate() { + super.invalidate(); + vertexCompiler.invalidate(); + fragmentCompiler.invalidate(); + } + + @Override + protected GlProgram _create(InstancedArraysCompiler.Context ctx) { + // TODO: try-catch here to prevent crashing if shaders failed to compile + Material material = ctx.material; + FileResolution instanceShader = ctx.instanceShader(); + ContextShader contextShader = ctx.contextShader; + + var vertex = new VertexCompiler.Context(ctx.vertexType(), instanceShader.getFile(), material.getVertexShader().getFile(), + contextShader.getVertexShader()); + + var fragment = new FragmentCompiler.Context(material.getFragmentShader().getFile(), contextShader.getFragmentShader()); + + return new ProgramAssembler(instanceShader.getFileLoc()) + .attachShader(vertexCompiler.get(vertex)) + .attachShader(fragmentCompiler.get(fragment)) + .link() + .build(contextShader.factory()); + } + + @Override + protected void _destroy(GlProgram value) { + value.delete(); + } + + public static void invalidateAll(ReloadRenderersEvent ignored) { + INSTANCE.invalidate(); + } + + /** + * Represents the entire context of a program's usage. + * + * @param vertexType The vertexType the program should be adapted for. + * @param material The material shader to use. + * @param instanceShader The instance shader to use. + * @param contextShader The context shader to use. + */ + public record Context(VertexType vertexType, Material material, FileResolution instanceShader, + ContextShader contextShader) { + } + + /** + * Handles compilation and deletion of vertex shaders. + */ + public static class VertexCompiler extends Memoizer { + + public VertexCompiler() { + } + + @Override + protected GlShader _create(Context key) { + StringBuilder finalSource = new StringBuilder(); + + finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX)); + + var index = new CompilationContext(); + + // LAYOUT + + var layoutShader = key.vertexType.getLayoutShader().getFile(); + finalSource.append(layoutShader.generateFinalSource(index)); + + // INSTANCE + + int attributeBaseIndex = key.vertexType.getLayout() + .getAttributeCount(); + + var instanceShader = key.instanceShader; + var replacements = new ArrayList>(); + for (ShaderField field : instanceShader.fields.values()) { + if (field.decoration != ShaderField.Decoration.IN) { + continue; + } + + int location = Integer.parseInt(field.location.get()); + int newLocation = location + attributeBaseIndex; + replacements.add(Pair.of(field.location, Integer.toString(newLocation))); + } + finalSource.append(instanceShader.generateFinalSource(index, replacements)); + + // MATERIAL + + var materialShader = key.materialShader; + finalSource.append(materialShader.generateFinalSource(index)); + + // CONTEXT + + var contextShaderSource = key.contextShader; + finalSource.append(contextShaderSource.generateFinalSource(index)); + + // MAIN + + finalSource.append(""" + void main() { + flw_layoutVertex(); + + flw_instanceVertex(); + + flw_materialVertex(); + + flw_contextVertex(); + } + """); + + try { + return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name)); + } catch (ShaderCompilationException e) { + throw e.withErrorLog(index); + } + } + + @Override + protected void _destroy(GlShader value) { + value.delete(); + } + + /** + * @param vertexType The vertex type to use. + * @param instanceShader The instance shader source. + * @param materialShader The vertex material shader source. + * @param contextShader The context shader source. + */ + public record Context(VertexType vertexType, SourceFile instanceShader, SourceFile materialShader, SourceFile contextShader) { + } + } + + /** + * Handles compilation and deletion of fragment shaders. + */ + public static class FragmentCompiler extends Memoizer { + + public FragmentCompiler() { + } + + @Override + protected GlShader _create(Context key) { + StringBuilder finalSource = new StringBuilder(); + + finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.FRAGMENT)); + + var ctx = new CompilationContext(); + + // MATERIAL + + SourceFile materialShader = key.materialShader; + finalSource.append(materialShader.generateFinalSource(ctx)); + + // CONTEXT + + SourceFile contextShaderSource = key.contextShader; + finalSource.append(contextShaderSource.generateFinalSource(ctx)); + + // MAIN + + finalSource.append(generateFooter()); + + try { + return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name)); + } catch (ShaderCompilationException e) { + throw e.withErrorLog(ctx); + } + } + + protected String generateFooter() { + return """ + void main() { + flw_initFragment(); + + flw_materialFragment(); + + flw_contextFragment(); + } + """; + } + + @Override + protected void _destroy(GlShader value) { + value.delete(); + } + + /** + * Represents the conditions under which a shader is compiled. + * + * @param materialShader The fragment material shader source. + */ + public record Context(SourceFile materialShader, SourceFile contextShader) { + + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java deleted file mode 100644 index 4b54c2218..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.jozufozu.flywheel.core.compile; - -import com.jozufozu.flywheel.api.material.Material; -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.core.source.FileResolution; -import com.jozufozu.flywheel.event.ReloadRenderersEvent; - -/** - * A caching compiler. - * - *

- * This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of - * compiled programs, and will only compile a program if it is not already in the cache. - *

- *

- * A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload. - *

- */ -public class ProgramCompiler extends Memoizer { - - public static final ProgramCompiler INSTANCE = new ProgramCompiler(); - - private final VertexCompiler vertexCompiler; - private final FragmentCompiler fragmentCompiler; - - private ProgramCompiler() { - this.vertexCompiler = new VertexCompiler(); - this.fragmentCompiler = new FragmentCompiler(); - } - - /** - * Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec. - * - * @param ctx The context of compilation. - * @return A compiled GlProgram. - */ - public GlProgram getProgram(ProgramCompiler.Context ctx) { - return super.get(ctx); - } - - @Override - public void invalidate() { - super.invalidate(); - vertexCompiler.invalidate(); - fragmentCompiler.invalidate(); - } - - @Override - protected GlProgram _create(ProgramCompiler.Context ctx) { - // TODO: try-catch here to prevent crashing if shaders failed to compile - Material material = ctx.material; - FileResolution instanceShader = ctx.instanceShader(); - ContextShader contextShader = ctx.contextShader; - - var vertex = new VertexCompiler.Context(ctx.vertexType(), instanceShader.getFile(), material.getVertexShader().getFile(), - contextShader.getVertexShader()); - - var fragment = new FragmentCompiler.Context(material.getFragmentShader().getFile(), contextShader.getFragmentShader()); - - return new ProgramAssembler(instanceShader.getFileLoc()) - .attachShader(vertexCompiler.get(vertex)) - .attachShader(fragmentCompiler.get(fragment)) - .link() - .build(contextShader.factory()); - } - - @Override - protected void _destroy(GlProgram value) { - value.delete(); - } - - public static void invalidateAll(ReloadRenderersEvent ignored) { - INSTANCE.invalidate(); - } - - /** - * Represents the entire context of a program's usage. - * - * @param vertexType The vertexType the program should be adapted for. - * @param material The material shader to use. - * @param instanceShader The instance shader to use. - * @param contextShader The context shader to use. - */ - public record Context(VertexType vertexType, Material material, FileResolution instanceShader, - ContextShader contextShader) { - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java deleted file mode 100644 index 50aba6150..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.jozufozu.flywheel.core.compile; - -import java.util.ArrayList; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.gl.GLSLVersion; -import com.jozufozu.flywheel.backend.gl.shader.GlShader; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.core.source.CompilationContext; -import com.jozufozu.flywheel.core.source.SourceFile; -import com.jozufozu.flywheel.core.source.parse.ShaderField; -import com.jozufozu.flywheel.core.source.span.Span; -import com.jozufozu.flywheel.util.Pair; - -/** - * Handles compilation and deletion of vertex shaders. - */ -public class VertexCompiler extends Memoizer { - - public VertexCompiler() { - } - - @Override - protected GlShader _create(Context key) { - StringBuilder finalSource = new StringBuilder(); - - finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX)); - - var index = new CompilationContext(); - - // LAYOUT - - var layoutShader = key.vertexType.getLayoutShader().getFile(); - finalSource.append(layoutShader.generateFinalSource(index)); - - // INSTANCE - - int attributeBaseIndex = key.vertexType.getLayout() - .getAttributeCount(); - - var instanceShader = key.instanceShader; - var replacements = new ArrayList>(); - for (ShaderField field : instanceShader.fields.values()) { - if (field.decoration != ShaderField.Decoration.IN) { - continue; - } - - int location = Integer.parseInt(field.location.get()); - int newLocation = location + attributeBaseIndex; - replacements.add(Pair.of(field.location, Integer.toString(newLocation))); - } - finalSource.append(instanceShader.generateFinalSource(index, replacements)); - - // MATERIAL - - var materialShader = key.materialShader; - finalSource.append(materialShader.generateFinalSource(index)); - - // CONTEXT - - var contextShaderSource = key.contextShader; - finalSource.append(contextShaderSource.generateFinalSource(index)); - - // MAIN - - finalSource.append(""" - void main() { - flw_layoutVertex(); - - flw_instanceVertex(); - - flw_materialVertex(); - - flw_contextVertex(); - } - """); - - try { - return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name)); - } catch (ShaderCompilationException e) { - throw e.withErrorLog(index); - } - } - - @Override - protected void _destroy(GlShader value) { - value.delete(); - } - - /** - * @param vertexType The vertex type to use. - * @param instanceShader The instance shader source. - * @param materialShader The vertex material shader source. - * @param contextShader The context shader source. - */ - public record Context(VertexType vertexType, SourceFile instanceShader, SourceFile materialShader, SourceFile contextShader) { - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java deleted file mode 100644 index c58b5984d..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.jozufozu.flywheel.core.crumbling; - -import com.jozufozu.flywheel.api.instance.DynamicInstance; -import com.jozufozu.flywheel.api.instancer.InstancerManager; -import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager; - -public class CrumblingInstanceManager extends BlockEntityInstanceManager { - - public CrumblingInstanceManager(InstancerManager instancerManager) { - super(instancerManager); - } - - @Override - protected void updateInstance(DynamicInstance dyn, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) { - dyn.beginFrame(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java index 7459758f5..595179b9f 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java @@ -8,6 +8,8 @@ import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine; +import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager; +import com.jozufozu.flywheel.backend.instancing.instancing.DrawCall; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.RenderContext; @@ -128,7 +130,7 @@ public class CrumblingRenderer { private State() { instancerManager = new CrumblingEngine(); - instanceManager = new CrumblingInstanceManager(instancerManager); + instanceManager = new BlockEntityInstanceManager(instancerManager); instancerManager.attachManagers(instanceManager); } diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedWriterUnsafe.java index 8634e0db7..9aab8c48b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedWriterUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedWriterUnsafe.java @@ -4,7 +4,7 @@ import java.nio.ByteBuffer; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.core.structs.ColoredLitWriterUnsafe; -import com.jozufozu.flywheel.util.MatrixWrite; +import com.jozufozu.flywheel.util.extension.MatrixExtension; public class TransformedWriterUnsafe extends ColoredLitWriterUnsafe { @@ -17,7 +17,7 @@ public class TransformedWriterUnsafe extends ColoredLitWriterUnsafe implements Supplier { return value; } + public boolean isInitialized() { + return value != null; + } + public Lazy lazyMap(Function func) { return new Lazy<>(() -> func.apply(get())); } diff --git a/src/main/java/com/jozufozu/flywheel/util/ClientLevelExtension.java b/src/main/java/com/jozufozu/flywheel/util/extension/ClientLevelExtension.java similarity index 94% rename from src/main/java/com/jozufozu/flywheel/util/ClientLevelExtension.java rename to src/main/java/com/jozufozu/flywheel/util/extension/ClientLevelExtension.java index 197e24a27..d6e6e8bd5 100644 --- a/src/main/java/com/jozufozu/flywheel/util/ClientLevelExtension.java +++ b/src/main/java/com/jozufozu/flywheel/util/extension/ClientLevelExtension.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.util; +package com.jozufozu.flywheel.util.extension; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.entity.Entity; diff --git a/src/main/java/com/jozufozu/flywheel/util/extension/Matrix3fExtension.java b/src/main/java/com/jozufozu/flywheel/util/extension/Matrix3fExtension.java new file mode 100644 index 000000000..a587ec537 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/extension/Matrix3fExtension.java @@ -0,0 +1,16 @@ +package com.jozufozu.flywheel.util.extension; + +import com.jozufozu.flywheel.util.joml.Matrix3f; + +public interface Matrix3fExtension { + + Matrix3f flywheel$store(Matrix3f matrix); + + static Matrix3f clone(com.mojang.math.Matrix3f moj) { + return ((Matrix3fExtension)(Object) moj).flywheel$store(new Matrix3f()); + } + + static void store(com.mojang.math.Matrix3f moj, Matrix3f joml) { + ((Matrix3fExtension)(Object) moj).flywheel$store(joml); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/util/extension/Matrix4fExtension.java b/src/main/java/com/jozufozu/flywheel/util/extension/Matrix4fExtension.java new file mode 100644 index 000000000..7651cf622 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/extension/Matrix4fExtension.java @@ -0,0 +1,16 @@ +package com.jozufozu.flywheel.util.extension; + +import com.jozufozu.flywheel.util.joml.Matrix4f; + +public interface Matrix4fExtension { + + Matrix4f flywheel$store(Matrix4f matrix); + + static Matrix4f clone(com.mojang.math.Matrix4f moj) { + return ((Matrix4fExtension)(Object) moj).flywheel$store(new Matrix4f()); + } + + static void store(com.mojang.math.Matrix4f moj, Matrix4f joml) { + ((Matrix4fExtension)(Object) moj).flywheel$store(joml); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/util/MatrixWrite.java b/src/main/java/com/jozufozu/flywheel/util/extension/MatrixExtension.java similarity index 70% rename from src/main/java/com/jozufozu/flywheel/util/MatrixWrite.java rename to src/main/java/com/jozufozu/flywheel/util/extension/MatrixExtension.java index e5455dad5..4dd0ac116 100644 --- a/src/main/java/com/jozufozu/flywheel/util/MatrixWrite.java +++ b/src/main/java/com/jozufozu/flywheel/util/extension/MatrixExtension.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.util; +package com.jozufozu.flywheel.util.extension; import java.nio.ByteBuffer; @@ -8,7 +8,7 @@ import com.mojang.math.Matrix4f; * @see com.jozufozu.flywheel.mixin.matrix.Matrix3fMixin * @see com.jozufozu.flywheel.mixin.matrix.Matrix4fMixin */ -public interface MatrixWrite { +public interface MatrixExtension { /** * Write the contents of this object into sequential memory starting at the given address. @@ -18,10 +18,10 @@ public interface MatrixWrite { void flywheel$write(ByteBuffer buf); static void write(Matrix4f matrix, ByteBuffer buf) { - ((MatrixWrite) (Object) matrix).flywheel$write(buf); + ((MatrixExtension) (Object) matrix).flywheel$write(buf); } static void writeUnsafe(Matrix4f matrix, long ptr) { - ((MatrixWrite) (Object) matrix).flywheel$writeUnsafe(ptr); + ((MatrixExtension) (Object) matrix).flywheel$writeUnsafe(ptr); } } diff --git a/src/main/java/com/jozufozu/flywheel/util/RenderTypeExtension.java b/src/main/java/com/jozufozu/flywheel/util/extension/RenderTypeExtension.java similarity index 93% rename from src/main/java/com/jozufozu/flywheel/util/RenderTypeExtension.java rename to src/main/java/com/jozufozu/flywheel/util/extension/RenderTypeExtension.java index 5beefc1f2..6398bb479 100644 --- a/src/main/java/com/jozufozu/flywheel/util/RenderTypeExtension.java +++ b/src/main/java/com/jozufozu/flywheel/util/extension/RenderTypeExtension.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.util; +package com.jozufozu.flywheel.util.extension; import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer; diff --git a/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java b/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java index a85b90676..3bb149f9c 100644 --- a/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java +++ b/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java @@ -23,6 +23,9 @@ */ package com.jozufozu.flywheel.util.joml; +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; /** * Efficiently performs frustum intersection tests by caching the frustum planes of an arbitrary transformation {@link Matrix4fc matrix}. *

@@ -950,4 +953,40 @@ public class FrustumIntersection { return da >= 0.0f || db >= 0.0f; } + public void bufferPlanes(ByteBuffer buffer) { + + Vector3f scratch = new Vector3f(); + Vector3f result = new Vector3f(); + + long addr = MemoryUtil.memAddress(buffer); + planeIntersect(planes[0], planes[2], planes[4], result, scratch); result.getToAddress(addr); + planeIntersect(planes[0], planes[2], planes[5], result, scratch); result.getToAddress(addr + 12); + planeIntersect(planes[0], planes[3], planes[4], result, scratch); result.getToAddress(addr + 24); + planeIntersect(planes[0], planes[3], planes[5], result, scratch); result.getToAddress(addr + 36); + planeIntersect(planes[1], planes[2], planes[4], result, scratch); result.getToAddress(addr + 48); + planeIntersect(planes[1], planes[2], planes[5], result, scratch); result.getToAddress(addr + 60); + planeIntersect(planes[1], planes[3], planes[4], result, scratch); result.getToAddress(addr + 72); + planeIntersect(planes[1], planes[3], planes[5], result, scratch); result.getToAddress(addr + 84); + } + + private Vector3f planeIntersect(Vector4f a, Vector4f b, Vector4f c, Vector3f result, Vector3f scratch) { + // Formula used + // d1 ( N2 * N3 ) + d2 ( N3 * N1 ) + d3 ( N1 * N2 ) + //P = --------------------------------------------------------------------- + // N1 . ( N2 * N3 ) + // + // Note: N refers to the normal, d refers to the displacement. '.' means dot product. '*' means cross product + + float f = result.set(b.x, b.y, b.z).cross(c.x, c.y, c.z).dot(a.x, a.y, a.z); + + result.set(0); + scratch.set(b.x, b.y, b.z).cross(c.x, c.y, c.z).mul(a.z); + result.add(scratch); + scratch.set(c.x, c.y, c.z).cross(a.x, a.y, a.z).mul(b.z); + result.add(scratch); + scratch.set(a.x, a.y, a.z).cross(b.x, b.y, b.z).mul(c.z); + result.add(scratch); + + return result.div(f); + } } diff --git a/src/main/java/com/jozufozu/flywheel/util/joml/Quaternionf.java b/src/main/java/com/jozufozu/flywheel/util/joml/Quaternionf.java index f783ca20e..ad2ef13cd 100644 --- a/src/main/java/com/jozufozu/flywheel/util/joml/Quaternionf.java +++ b/src/main/java/com/jozufozu/flywheel/util/joml/Quaternionf.java @@ -32,6 +32,8 @@ import java.nio.FloatBuffer; import java.text.DecimalFormat; import java.text.NumberFormat; +import com.mojang.math.Quaternion; + /** * Quaternion of 4 single-precision floats which can represent rotation and uniform scaling. * @@ -130,6 +132,13 @@ public class Quaternionf implements Externalizable, Cloneable, Quaternionfc { w = cos; } + public Quaternionf(Quaternion moj) { + x = moj.i(); + y = moj.j(); + z = moj.k(); + w = moj.r(); + } + /** * @return the first component of the vector part */ diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/effect/ExampleEffect.java b/src/main/java/com/jozufozu/flywheel/vanilla/effect/ExampleEffect.java index 5c583534c..d7f362424 100644 --- a/src/main/java/com/jozufozu/flywheel/vanilla/effect/ExampleEffect.java +++ b/src/main/java/com/jozufozu/flywheel/vanilla/effect/ExampleEffect.java @@ -18,6 +18,7 @@ import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.ImmutableBox; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; import com.jozufozu.flywheel.util.joml.Vector3f; import net.minecraft.client.Minecraft; @@ -297,5 +298,10 @@ public class ExampleEffect implements Effect { public boolean decreaseFramerateWithDistance() { return false; } + + @Override + public boolean checkFrustum(FrustumIntersection frustum) { + return true; + } } } diff --git a/src/main/resources/assets/flywheel/flywheel/debug/debug.frag b/src/main/resources/assets/flywheel/flywheel/debug/debug.frag new file mode 100644 index 000000000..ab41dc91f --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/debug/debug.frag @@ -0,0 +1,5 @@ +out vec4 fragColor; + +void main() { + fragColor = vec4(1.0, 1.0, 1.0, 0.2); +} diff --git a/src/main/resources/assets/flywheel/flywheel/debug/debug.vert b/src/main/resources/assets/flywheel/flywheel/debug/debug.vert new file mode 100644 index 000000000..6a5d60ca1 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/debug/debug.vert @@ -0,0 +1,7 @@ +#use "flywheel:uniform/view.glsl" + +layout(location = 0) in vec3 worldPos; + +void main() { + gl_Position = flw_viewProjection * vec4(worldPos, 1.0); +} From e42657cd8aa693b9abaacbc2d8ce67e6c2f3fb6c Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 29 Jul 2022 16:06:25 -0700 Subject: [PATCH 02/13] Culling experiments 2: not testing the GPU - Skeleton for compute shader culling/indirect rendering --- build.gradle | 10 ++ .../java/com/jozufozu/flywheel/Flywheel.java | 2 +- .../context}/ContextShader.java | 2 +- .../flywheel/api/instancer/InstancedPart.java | 9 +- .../jozufozu/flywheel/backend/Backend.java | 1 + .../com/jozufozu/flywheel/backend/Loader.java | 4 +- .../backend/gl/shader/ShaderType.java | 2 + .../backend/gl/versioned/GlCompat.java | 7 + .../backend/instancing/AbstractInstancer.java | 19 +- .../backend/instancing/InstanceWorld.java | 2 + .../batching/BatchDrawingTracker.java | 2 +- .../instancing/indirect/ComputeCompiler.java | 42 +++++ .../instancing/indirect/DSABuffer.java | 10 ++ .../instancing/indirect/IndirectEngine.java | 170 ++++++++++++++++++ .../instancing/indirect/IndirectFactory.java | 73 ++++++++ .../indirect/IndirectInstancer.java | 72 ++++++++ .../instancing/indirect/IndirectList.java | 121 +++++++++++++ .../instancing/indirect/InstancedModel.java | 51 ++++++ .../instancing/indirect/RenderLists.java | 39 ++++ .../instancing/indirect/ShaderState.java | 8 + .../instancing/indirect/package-info.java | 6 + .../instancing/instancing/GPUInstancer.java | 18 +- .../instancing}/InstancedArraysCompiler.java | 4 +- .../instancing/InstancingEngine.java | 3 +- .../instancing/instancing/MeshPool.java | 60 +++++-- .../jozufozu/flywheel/config/BackendType.java | 5 + .../jozufozu/flywheel/config/FlwCommands.java | 1 + .../flywheel/core/ComponentRegistry.java | 2 +- .../jozufozu/flywheel/core/Components.java | 7 +- .../jozufozu/flywheel/core/DebugRender.java | 2 +- .../flywheel/core/compile/CompileUtil.java | 2 +- .../flywheel/core/model/BlockMesh.java | 0 .../flywheel/core/model/ModelUtil.java | 1 + .../util/joml/FrustumIntersection.java | 58 +++++- .../flywheel/compute/cull_instances.glsl | 69 +++++++ .../flywheel/compute/draw_instances.vert | 28 +++ .../flywheel/flywheel/compute/objects.glsl | 17 ++ .../sphere_cull_frustum_experiment.glsl | 72 ++++++++ 38 files changed, 951 insertions(+), 50 deletions(-) rename src/main/java/com/jozufozu/flywheel/{core/compile => api/context}/ContextShader.java (91%) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCompiler.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/DSABuffer.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectFactory.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ShaderState.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/package-info.java rename src/main/java/com/jozufozu/flywheel/{core/compile => backend/instancing/instancing}/InstancedArraysCompiler.java (97%) create mode 100644 src/main/java/com/jozufozu/flywheel/core/model/BlockMesh.java create mode 100644 src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl create mode 100644 src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert create mode 100644 src/main/resources/assets/flywheel/flywheel/compute/objects.glsl create mode 100644 src/main/resources/assets/flywheel/flywheel/compute/sphere_cull_frustum_experiment.glsl diff --git a/build.gradle b/build.gradle index 10988a0ef..5f87840a4 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,8 @@ version = mod_version + (dev && buildNumber != null ? "-${buildNumber}" : '') java.toolchain.languageVersion = JavaLanguageVersion.of(17) +jarJar.enable() + println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) minecraft { mappings channel: 'parchment', version: "${parchment_version}-${minecraft_version}" @@ -111,6 +113,11 @@ repositories { dependencies { minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" + implementation "com.dreizak:miniball:1.0.3" + jarJar(group: 'com.dreizak', name: 'miniball', version: '[1.0,2.0)') { + jarJar.pin(it, "[1.0,2.0)") + } + // switch to implementation for debugging compileOnly fg.deobf("maven.modrinth:starlight-forge:1.0.2+1.18.2") @@ -171,7 +178,9 @@ void addLicense(jarTask) { } jar.finalizedBy('reobfJar') +tasks.jarJar.finalizedBy('reobfJarJar') addLicense(jar) +addLicense(tasks.jarJar) publishing { publications { @@ -180,6 +189,7 @@ publishing { from components.java fg.component(it) + jarJar.component(it) } } diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java index afcffa837..925dd3eb4 100644 --- a/src/main/java/com/jozufozu/flywheel/Flywheel.java +++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java @@ -16,7 +16,7 @@ import com.jozufozu.flywheel.core.DebugRender; import com.jozufozu.flywheel.core.PartialModel; import com.jozufozu.flywheel.core.QuadConverter; import com.jozufozu.flywheel.core.StitchedSprite; -import com.jozufozu.flywheel.core.compile.InstancedArraysCompiler; +import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysCompiler; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.model.Models; import com.jozufozu.flywheel.event.EntityWorldHandler; diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ContextShader.java b/src/main/java/com/jozufozu/flywheel/api/context/ContextShader.java similarity index 91% rename from src/main/java/com/jozufozu/flywheel/core/compile/ContextShader.java rename to src/main/java/com/jozufozu/flywheel/api/context/ContextShader.java index 66d8c850f..37e9320ed 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/ContextShader.java +++ b/src/main/java/com/jozufozu/flywheel/api/context/ContextShader.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.core.compile; +package com.jozufozu.flywheel.api.context; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.core.source.FileResolution; diff --git a/src/main/java/com/jozufozu/flywheel/api/instancer/InstancedPart.java b/src/main/java/com/jozufozu/flywheel/api/instancer/InstancedPart.java index 50379c54e..e06799ba1 100644 --- a/src/main/java/com/jozufozu/flywheel/api/instancer/InstancedPart.java +++ b/src/main/java/com/jozufozu/flywheel/api/instancer/InstancedPart.java @@ -25,12 +25,9 @@ public abstract class InstancedPart { } public final boolean checkDirtyAndClear() { - if (dirty) { - dirty = false; - return true; - } else { - return false; - } + boolean wasDirty = dirty; + dirty = false; + return wasDirty; } public final boolean isRemoved() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index b24df2eb6..b48077615 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -95,6 +95,7 @@ public class Backend { case OFF -> true; case BATCHING -> !usingShaders; case INSTANCING -> !usingShaders && GlCompat.getInstance().instancedArraysSupported(); + case INDIRECT -> !usingShaders && GlCompat.getInstance().supportsIndirect(); }; return canUseEngine ? preferredChoice : BackendType.OFF; diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index dfef2748b..7f923e5a3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -5,8 +5,8 @@ import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.core.ComponentRegistry; -import com.jozufozu.flywheel.core.compile.ContextShader; -import com.jozufozu.flywheel.core.compile.InstancedArraysCompiler; +import com.jozufozu.flywheel.api.context.ContextShader; +import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysCompiler; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.ShaderLoadingException; diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java index ca42adc46..f5b106232 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java @@ -1,10 +1,12 @@ package com.jozufozu.flywheel.backend.gl.shader; import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL43; public enum ShaderType { VERTEX("vertex", "VERTEX_SHADER", "vert", GL20.GL_VERTEX_SHADER), FRAGMENT("fragment", "FRAGMENT_SHADER", "frag", GL20.GL_FRAGMENT_SHADER), + COMPUTE("compute", "COMPUTE_SHADER", "glsl", GL43.GL_COMPUTE_SHADER), ; public final String name; diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java index 059dd15b1..3ae01a15d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java @@ -32,12 +32,15 @@ public class GlCompat { public final InstancedArrays instancedArrays; public final BufferStorage bufferStorage; public final boolean amd; + public final boolean supportsIndirect; private GlCompat() { GLCapabilities caps = GL.createCapabilities(); instancedArrays = getLatest(InstancedArrays.class, caps); bufferStorage = getLatest(BufferStorage.class, caps); + supportsIndirect = caps.OpenGL46; + amd = _isAmdWindows(); } @@ -116,5 +119,9 @@ public class GlCompat { // vendor string I got was "ATI Technologies Inc." return vendor.contains("ATI") || vendor.contains("AMD"); } + + public boolean supportsIndirect() { + return supportsIndirect; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java index 78bf26d6a..77cad5920 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java @@ -6,6 +6,7 @@ import java.util.BitSet; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.Instancer; import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.struct.StructWriter; public abstract class AbstractInstancer implements Instancer { @@ -28,7 +29,7 @@ public abstract class AbstractInstancer implements Inst /** * Copy a data from another Instancer to this. - * + *

* This has the effect of swapping out one model for another. * @param inOther the data associated with a different model. */ @@ -104,6 +105,22 @@ public abstract class AbstractInstancer implements Inst return instanceData; } + protected void writeChangedUnchecked(StructWriter writer) { + boolean sequential = true; + for (int i = 0; i < data.size(); i++) { + final D element = data.get(i); + if (element.checkDirtyAndClear()) { + if (!sequential) { + writer.seek(i); + } + writer.write(element); + sequential = true; + } else { + sequential = false; + } + } + } + public abstract void delete(); @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index ccc177125..8c5317223 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -9,6 +9,7 @@ import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceM import com.jozufozu.flywheel.backend.instancing.effect.Effect; import com.jozufozu.flywheel.backend.instancing.effect.EffectInstanceManager; import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager; +import com.jozufozu.flywheel.backend.instancing.indirect.IndirectEngine; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.RenderContext; @@ -37,6 +38,7 @@ public class InstanceWorld { public static InstanceWorld create(LevelAccessor level) { var engine = switch (Backend.getBackendType()) { + case INDIRECT -> new IndirectEngine(Components.WORLD); case INSTANCING -> new InstancingEngine(Components.WORLD); case BATCHING -> new BatchingEngine(); case OFF -> throw new IllegalStateException("Cannot create instance world when backend is off."); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchDrawingTracker.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchDrawingTracker.java index 25ce1b86a..ef7cea552 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchDrawingTracker.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchDrawingTracker.java @@ -3,7 +3,7 @@ package com.jozufozu.flywheel.backend.instancing.batching; import java.util.HashSet; import java.util.Set; -import com.jozufozu.flywheel.util.RenderTypeExtension; +import com.jozufozu.flywheel.util.extension.RenderTypeExtension; import com.mojang.blaze3d.vertex.BufferBuilder; import net.minecraft.client.renderer.RenderType; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCompiler.java new file mode 100644 index 000000000..00f02c6d3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCompiler.java @@ -0,0 +1,42 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.compile.Memoizer; +import com.jozufozu.flywheel.core.compile.ProgramAssembler; +import com.jozufozu.flywheel.core.source.CompilationContext; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.event.ReloadRenderersEvent; + +public class ComputeCompiler extends Memoizer { + + public static final ComputeCompiler INSTANCE = new ComputeCompiler(); + + private ComputeCompiler() { + } + + @Override + protected GlProgram _create(FileResolution file) { + + String source = file.getFile() + .generateFinalSource(new CompilationContext()); + + var shader = new GlShader(source, ShaderType.COMPUTE, ImmutableList.of(file.getFileLoc())); + + return new ProgramAssembler(file.getFileLoc()) + .attachShader(shader) + .link() + .build(GlProgram::new); + } + + @Override + protected void _destroy(GlProgram value) { + value.delete(); + } + + public static void invalidateAll(ReloadRenderersEvent ignored) { + INSTANCE.invalidate(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/DSABuffer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/DSABuffer.java new file mode 100644 index 000000000..43b1710e3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/DSABuffer.java @@ -0,0 +1,10 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +public class DSABuffer { + int id; + int byteSize; + + public DSABuffer(int id) { + this.id = id; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java new file mode 100644 index 000000000..3294ff928 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -0,0 +1,170 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jetbrains.annotations.NotNull; +import org.lwjgl.opengl.GL32; + +import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.gl.GlTextureUnit; +import com.jozufozu.flywheel.backend.instancing.Engine; +import com.jozufozu.flywheel.backend.instancing.InstanceManager; +import com.jozufozu.flywheel.backend.instancing.TaskEngine; +import com.jozufozu.flywheel.backend.instancing.instancing.MeshPool; +import com.jozufozu.flywheel.core.RenderContext; +import com.jozufozu.flywheel.api.context.ContextShader; +import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysCompiler; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.core.uniform.UniformBuffer; +import com.jozufozu.flywheel.util.WeakHashSet; +import com.mojang.blaze3d.systems.RenderSystem; + +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; + +public class IndirectEngine implements Engine { + + public static int MAX_ORIGIN_DISTANCE = 100; + + protected BlockPos originCoordinate = BlockPos.ZERO; + + protected final ContextShader context; + + protected final Map, IndirectFactory> factories = new HashMap<>(); + + protected final List> uninitializedModels = new ArrayList<>(); + protected final RenderLists renderLists = new RenderLists(); + + /** + * The set of instance managers that are attached to this engine. + */ + private final WeakHashSet> instanceManagers; + + public IndirectEngine(ContextShader context) { + this.context = context; + + this.instanceManagers = new WeakHashSet<>(); + } + + @SuppressWarnings("unchecked") + @NotNull + @Override + public IndirectFactory factory(StructType type) { + return (IndirectFactory) factories.computeIfAbsent(type, this::createFactory); + } + + @NotNull + private IndirectFactory createFactory(StructType type) { + return new IndirectFactory<>(type, uninitializedModels::add); + } + + @Override + public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) { + var groups = renderLists.get(stage); + + setup(); + + for (var group : groups) { + group.submit(); + } + + } + + private void setup() { + GlTextureUnit.T2.makeActive(); + Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer(); + + RenderSystem.depthMask(true); + RenderSystem.colorMask(true, true, true, true); + RenderSystem.enableDepthTest(); + RenderSystem.depthFunc(GL32.GL_LEQUAL); + RenderSystem.enableCull(); + } + + protected void setup(ShaderState desc) { + + VertexType vertexType = desc.vertex(); + FileResolution instanceShader = desc.instance() + .getInstanceShader(); + Material material = desc.material(); + + var ctx = new InstancedArraysCompiler.Context(vertexType, material, instanceShader, context); + + InstancedArraysCompiler.INSTANCE.getProgram(ctx) + .bind(); + UniformBuffer.getInstance().sync(); + } + + public void clearAll() { + factories.values().forEach(IndirectFactory::clear); + } + + @Override + public void delete() { + factories.values() + .forEach(IndirectFactory::delete); + + factories.clear(); + } + + @Override + public Vec3i getOriginCoordinate() { + return originCoordinate; + } + + @Override + public void attachManagers(InstanceManager... listener) { + instanceManagers.addAll(List.of(listener)); + } + + @Override + public boolean maintainOriginCoordinate(Camera camera) { + Vec3 cameraPos = camera.getPosition(); + + double distanceSqr = Vec3.atLowerCornerOf(originCoordinate) + .subtract(cameraPos) + .lengthSqr(); + + if (distanceSqr > MAX_ORIGIN_DISTANCE * MAX_ORIGIN_DISTANCE) { + shiftListeners(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y), Mth.floor(cameraPos.z)); + return true; + } + return false; + } + + @Override + public void beginFrame(TaskEngine taskEngine, RenderContext context) { + for (var model : uninitializedModels) { + model.init(renderLists); + } + uninitializedModels.clear(); + + MeshPool.getInstance() + .flush(); + } + + private void shiftListeners(int cX, int cY, int cZ) { + originCoordinate = new BlockPos(cX, cY, cZ); + + factories.values().forEach(IndirectFactory::clear); + + instanceManagers.forEach(InstanceManager::onOriginShift); + } + + @Override + public void addDebugInfo(List info) { + info.add("GL33 Instanced Arrays"); + info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ()); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectFactory.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectFactory.java new file mode 100644 index 000000000..cf2f64912 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectFactory.java @@ -0,0 +1,73 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.instancer.Instancer; +import com.jozufozu.flywheel.api.instancer.InstancerFactory; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; +import com.jozufozu.flywheel.core.model.Model; + +public class IndirectFactory implements InstancerFactory { + + protected final Map> models = new HashMap<>(); + protected final StructType type; + private final Consumer> creationListener; + + public IndirectFactory(StructType type, Consumer> creationListener) { + this.type = type; + this.creationListener = creationListener; + } + + @Override + public Instancer model(Model modelKey) { + return models.computeIfAbsent(modelKey, this::createInstancer).getInstancer(); + } + + public int getInstanceCount() { + return models.values() + .stream() + .map(InstancedModel::getInstancer) + .mapToInt(AbstractInstancer::getInstanceCount) + .sum(); + } + + public int getVertexCount() { + return models.values() + .stream() + .mapToInt(InstancedModel::getVertexCount) + .sum(); + } + + public void delete() { + models.values().forEach(InstancedModel::delete); + models.clear(); + } + + /** + * Clear all instance data without freeing resources. + */ + public void clear() { + models.values() + .stream() + .map(InstancedModel::getInstancer) + .forEach(AbstractInstancer::clear); + } + + private InstancedModel createInstancer(Model model) { + var instancer = new InstancedModel<>(type, model); + this.creationListener.accept(instancer); + return instancer; + } + +// private void bindInstanceAttributes(GlVertexArray vao) { +// vao.bindAttributes(this.vbo, this.attributeBaseIndex, this.instanceFormat, 0L); +// +// for (int i = 0; i < this.instanceFormat.getAttributeCount(); i++) { +// vao.setAttributeDivisor(this.attributeBaseIndex + i, 1); +// } +// } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java new file mode 100644 index 000000000..ff6ee8e51 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java @@ -0,0 +1,72 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.struct.StructWriter; +import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; +import com.jozufozu.flywheel.core.layout.BufferLayout; + +public class IndirectInstancer extends AbstractInstancer { + + public final BufferLayout instanceFormat; + public final StructType structType; + public final InstancedModel parent; + int maxInstanceCount = 0; + + boolean anyToUpdate; + + public IndirectInstancer(InstancedModel parent, StructType type) { + super(type); + this.parent = parent; + this.instanceFormat = type.getLayout(); + this.structType = type; + } + + @Override + public void notifyDirty() { + anyToUpdate = true; + } + + public boolean isEmpty() { + return !anyToUpdate && !anyToRemove && maxInstanceCount == 0; + } + + void update() { + if (anyToRemove) { + removeDeletedInstances(); + } + + maxInstanceCount = data.size(); + + anyToRemove = false; + } + + void writeAll(final StructWriter writer) { + anyToUpdate = false; + + for (var instance : data) { + writer.write(instance); + } + } + + void writeChanged(final StructWriter writer) { + if (!anyToUpdate) { + return; + } + + anyToUpdate = false; + + final int size = data.size(); + + if (size == 0) { + return; + } + + writeChangedUnchecked(writer); + } + + @Override + public void delete() { + // noop + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java new file mode 100644 index 000000000..b68aec224 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java @@ -0,0 +1,121 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.lwjgl.opengl.GL46; +import org.lwjgl.opengl.GL46C; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.instancing.instancing.MeshPool; +import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.model.Mesh; + +public class IndirectList { + + final GlProgram compute; + final GlProgram draw; + + /** + * Stores raw instance data per-object. + */ + DSABuffer objectBuffer; + + /** + * Stores bounding spheres + */ + DSABuffer boundingSphereBuffer; + + /** + * Stores drawIndirect structs. + */ + DSABuffer drawBuffer; + DSABuffer targetBuffer; + + final int[] buffers = new int[4]; + + final List> batches = new ArrayList<>(); + + IndirectList(StructType structType) { + GL46.glCreateBuffers(buffers); + objectBuffer = new DSABuffer(buffers[0]); + targetBuffer = new DSABuffer(buffers[1]); + boundingSphereBuffer = new DSABuffer(buffers[2]); + drawBuffer = new DSABuffer(buffers[3]); + + compute = ComputeCompiler.INSTANCE.get(Components.Files.CULL_INSTANCES); + draw = null; + } + + public void add(Mesh mesh, IndirectInstancer instancer) { + var pool = MeshPool.getInstance(); + var buffered = pool.alloc(mesh); + + batches.add(new Batch<>(instancer, buffered)); + } + + public void prepare() { + + try (var stack = MemoryStack.stackPush()) { + var size = batches.size() * 20; + long basePtr = stack.nmalloc(size); + long writePtr = basePtr; + for (Batch batch : batches) { + batch.writeIndirectCommand(writePtr); + writePtr += 20; + } + GL46C.nglNamedBufferData(drawBuffer.id, size, basePtr, GL46.GL_STREAM_DRAW); + } + } + + public void submit() { + + compute.bind(); + GL46.glBindBuffersBase(GL46.GL_SHADER_STORAGE_BUFFER, 0, buffers); + + var groupCount = (getTotalInstanceCount() + 31) >> 5; // ceil(totalInstanceCount / 32) + GL46.glDispatchCompute(groupCount, 1, 1); + + draw.bind(); + GL46.glMemoryBarrier(GL46.GL_SHADER_STORAGE_BARRIER_BIT); + GL46.glBindBuffer(GL46.GL_DRAW_INDIRECT_BUFFER, drawBuffer.id); + + GL46.glMultiDrawElementsIndirect(GL46.GL_TRIANGLES, GL46.GL_UNSIGNED_INT, 0, batches.size(), 0); + } + + private int getTotalInstanceCount() { + return 0; + } + + private static final class Batch { + final IndirectInstancer instancer; + final MeshPool.BufferedMesh mesh; + + private Batch(IndirectInstancer instancer, MeshPool.BufferedMesh mesh) { + this.instancer = instancer; + this.mesh = mesh; + } + + public void writeIndirectCommand(long ptr) { + // typedef struct { + // GLuint count; + // GLuint instanceCount; + // GLuint firstIndex; + // GLuint baseVertex; + // GLuint baseInstance; + //} DrawElementsIndirectCommand; + + MemoryUtil.memPutInt(ptr, mesh.getVertexCount()); + MemoryUtil.memPutInt(ptr + 4, 0); + MemoryUtil.memPutInt(ptr + 8, 0); + MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); + MemoryUtil.memPutInt(ptr + 16, 0); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java new file mode 100644 index 000000000..5c5f032bb --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java @@ -0,0 +1,51 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import java.util.List; +import java.util.Map; + +import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.core.model.Mesh; +import com.jozufozu.flywheel.core.model.Model; + +public class InstancedModel { + + private final Model model; + private final StructType type; + private final IndirectInstancer instancer; + + public InstancedModel(StructType type, Model model) { + this.model = model; + this.instancer = new IndirectInstancer<>(this, type); + this.type = type; + } + + public void init(RenderLists renderLists) { + var materialMeshMap = this.model.getMeshes(); + for (var entry : materialMeshMap.entrySet()) { + var material = entry.getKey(); + var mesh = entry.getValue(); + renderLists.add(material.getRenderStage(), type, mesh, instancer); + + return; // TODO: support multiple meshes per model + } + } + + public IndirectInstancer getInstancer() { + return instancer; + } + + public Model getModel() { + return model; + } + + public int getVertexCount() { + return model.getVertexCount() * instancer.maxInstanceCount; + } + + public void delete() { + + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java new file mode 100644 index 000000000..ee1743507 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java @@ -0,0 +1,39 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ListMultimap; +import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.core.model.Mesh; + +public class RenderLists { + + public final Map, IndirectList>> renderLists = new EnumMap<>(RenderStage.class); + + public Collection> get(RenderStage stage) { + var renderList = renderLists.get(stage); + if (renderList == null) { + return Collections.emptyList(); + } + return renderList.values(); + } + + @SuppressWarnings("unchecked") + public void add(RenderStage stage, StructType type, Mesh mesh, IndirectInstancer instancer) { + var indirectList = (IndirectList) renderLists.computeIfAbsent(stage, $ -> new HashMap<>()) + .computeIfAbsent(type, IndirectList::new); + + indirectList.add(mesh, instancer); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ShaderState.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ShaderState.java new file mode 100644 index 000000000..7f1844eb2 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ShaderState.java @@ -0,0 +1,8 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; + +public record ShaderState(Material material, VertexType vertex, StructType instance) { +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/package-info.java new file mode 100644 index 000000000..6ec4f9bda --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.instancing.indirect; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java index 759afd2c0..bcc39f82e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java @@ -6,7 +6,6 @@ import java.util.Set; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.backend.gl.array.GlVertexArray; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; @@ -89,22 +88,7 @@ public class GPUInstancer extends AbstractInstancer buf.clear(clearStart, clearLength); if (size > 0) { - - final StructWriter writer = structType.getWriter(buf.unwrap()); - - boolean sequential = true; - for (int i = 0; i < size; i++) { - final D element = data.get(i); - if (element.checkDirtyAndClear()) { - if (!sequential) { - writer.seek(i); - } - writer.write(element); - sequential = true; - } else { - sequential = false; - } - } + writeChangedUnchecked(structType.getWriter(buf.unwrap())); } } catch (Exception e) { Flywheel.LOGGER.error("Error updating GPUInstancer:", e); diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/InstancedArraysCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysCompiler.java similarity index 97% rename from src/main/java/com/jozufozu/flywheel/core/compile/InstancedArraysCompiler.java rename to src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysCompiler.java index 0c5625db8..e6848c084 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/InstancedArraysCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysCompiler.java @@ -1,14 +1,16 @@ -package com.jozufozu.flywheel.core.compile; +package com.jozufozu.flywheel.backend.instancing.instancing; import java.util.ArrayList; import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.gl.GLSLVersion; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.compile.*; import com.jozufozu.flywheel.core.source.CompilationContext; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.SourceFile; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index 45c14e724..e8d21a692 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -19,8 +19,7 @@ import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.core.RenderContext; -import com.jozufozu.flywheel.core.compile.ContextShader; -import com.jozufozu.flywheel.core.compile.InstancedArraysCompiler; +import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.uniform.UniformBuffer; import com.jozufozu.flywheel.util.WeakHashSet; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java index 145f90ec3..053af8645 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java @@ -19,8 +19,8 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; -import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.model.Mesh; +import com.jozufozu.flywheel.core.vertex.Formats; import com.jozufozu.flywheel.event.ReloadRenderersEvent; public class MeshPool { @@ -49,6 +49,7 @@ public class MeshPool { private final GlBuffer vbo; private long byteSize; + private int vertexCount; private boolean dirty; private boolean anyToRemove; @@ -70,8 +71,10 @@ public class MeshPool { */ public BufferedMesh alloc(Mesh mesh) { return meshes.computeIfAbsent(mesh, m -> { - BufferedMesh bufferedModel = new BufferedMesh(m, byteSize); - byteSize += m.size(); + // FIXME: culling experiments fixing everything to Formats.BLOCK + BufferedMesh bufferedModel = new BufferedMesh(Formats.BLOCK, m, byteSize, vertexCount); + byteSize += bufferedModel.getByteSize(); + vertexCount += bufferedModel.mesh.getVertexCount(); allBuffered.add(bufferedModel); pendingUpload.add(bufferedModel); @@ -115,17 +118,21 @@ public class MeshPool { // re-evaluate first vertex for each model int byteIndex = 0; + int baseVertex = 0; for (BufferedMesh model : allBuffered) { if (model.byteIndex != byteIndex) { pendingUpload.add(model); } model.byteIndex = byteIndex; + model.baseVertex = baseVertex; - byteIndex += model.mesh.size(); + byteIndex += model.getByteSize(); + baseVertex += model.mesh.getVertexCount(); } this.byteSize = byteIndex; + this.vertexCount = baseVertex; this.anyToRemove = false; } @@ -143,12 +150,15 @@ public class MeshPool { ByteBuffer buffer = mapped.unwrap(); int byteIndex = 0; + int baseVertex = 0; for (BufferedMesh model : allBuffered) { model.byteIndex = byteIndex; + model.baseVertex = baseVertex; model.buffer(buffer); - byteIndex += model.mesh.size(); + byteIndex += model.getByteSize(); + baseVertex += model.mesh.getVertexCount(); } } catch (Exception e) { @@ -179,8 +189,9 @@ public class MeshPool { private final ElementBuffer ebo; private final Mesh mesh; - private final BufferLayout layout; + private final VertexType type; private long byteIndex; + private int baseVertex; private boolean deleted; @@ -188,12 +199,20 @@ public class MeshPool { private final Set boundTo = new HashSet<>(); - public BufferedMesh(Mesh mesh, long byteIndex) { + public BufferedMesh(Mesh mesh, long byteIndex, int baseVertex) { this.mesh = mesh; this.byteIndex = byteIndex; + this.baseVertex = baseVertex; this.ebo = mesh.createEBO(); - this.layout = mesh.getVertexType() - .getLayout(); + this.type = mesh.getVertexType(); + } + + public BufferedMesh(VertexType type, Mesh mesh, long byteIndex, int baseVertex) { + this.mesh = mesh; + this.byteIndex = byteIndex; + this.baseVertex = baseVertex; + this.ebo = mesh.createEBO(); + this.type = type; } public void drawCall(GlVertexArray vao) { @@ -223,7 +242,7 @@ public class MeshPool { private void setup(GlVertexArray vao) { if (this.boundTo.add(vao)) { vao.enableArrays(getAttributeCount()); - vao.bindAttributes(MeshPool.this.vbo, 0, this.layout, this.byteIndex); + vao.bindAttributes(MeshPool.this.vbo, 0, type.getLayout(), this.byteIndex); } vao.bindElementArray(this.ebo.buffer); vao.bind(); @@ -240,14 +259,17 @@ public class MeshPool { } private void buffer(ByteBuffer buffer) { - this.mesh.writeInto(buffer, this.byteIndex); + var writer = type.createWriter(buffer); + writer.seek(this.byteIndex); + writer.writeVertexList(this.mesh.getReader()); this.boundTo.clear(); this.gpuResident = true; } public int getAttributeCount() { - return this.layout.getAttributeCount(); + return this.type.getLayout() + .getAttributeCount(); } public boolean isGpuResident() { @@ -255,7 +277,19 @@ public class MeshPool { } public VertexType getVertexType() { - return this.mesh.getVertexType(); + return this.type; + } + + public int getByteSize() { + return this.type.getLayout().getStride() * this.mesh.getVertexCount(); + } + + public int getBaseVertex() { + return baseVertex; + } + + public int getVertexCount() { + return this.mesh.getVertexCount(); } } diff --git a/src/main/java/com/jozufozu/flywheel/config/BackendType.java b/src/main/java/com/jozufozu/flywheel/config/BackendType.java index 822c28973..a64da9ddd 100644 --- a/src/main/java/com/jozufozu/flywheel/config/BackendType.java +++ b/src/main/java/com/jozufozu/flywheel/config/BackendType.java @@ -19,6 +19,11 @@ public enum BackendType { * Use GPU instancing to render everything. */ INSTANCING("GL33 Instanced Arrays"), + + /** + * Use Compute shaders to cull instances. + */ + INDIRECT("GL46 Compute Culling"), ; private static final Map lookup; diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index c9365b451..800d0a661 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -141,6 +141,7 @@ public class FlwCommands { 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 INDIRECT -> new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN); }; } diff --git a/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java b/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java index 6200c4679..c44251baf 100644 --- a/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java +++ b/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java @@ -11,7 +11,7 @@ import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.uniform.UniformProvider; import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.core.compile.ContextShader; +import com.jozufozu.flywheel.api.context.ContextShader; import net.minecraft.resources.ResourceLocation; diff --git a/src/main/java/com/jozufozu/flywheel/core/Components.java b/src/main/java/com/jozufozu/flywheel/core/Components.java index 3edc8496d..581dd62be 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Components.java +++ b/src/main/java/com/jozufozu/flywheel/core/Components.java @@ -3,7 +3,7 @@ package com.jozufozu.flywheel.core; import java.util.function.BiConsumer; import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.core.compile.ContextShader; +import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.core.crumbling.CrumblingProgram; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.SourceChecks; @@ -48,6 +48,11 @@ public class Components { public static final FileResolution WORLD_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.WORLD, ".frag")); public static final FileResolution CRUMBLING_VERTEX = contextVertex(ResourceUtil.subPath(Names.CRUMBLING, ".vert")); public static final FileResolution CRUMBLING_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.CRUMBLING, ".frag")); + public static final FileResolution CULL_INSTANCES = compute(Flywheel.rl("compute/cull_instances.glsl")); + + private static FileResolution compute(ResourceLocation rl) { + return FileResolution.get(rl); + } private static FileResolution uniform(ResourceLocation location) { return FileResolution.get(location); diff --git a/src/main/java/com/jozufozu/flywheel/core/DebugRender.java b/src/main/java/com/jozufozu/flywheel/core/DebugRender.java index 70a0c4d1c..10c8e414c 100644 --- a/src/main/java/com/jozufozu/flywheel/core/DebugRender.java +++ b/src/main/java/com/jozufozu/flywheel/core/DebugRender.java @@ -88,7 +88,7 @@ public class DebugRender { try (var stack = MemoryStack.stackPush()) { var buf = stack.malloc(3 * 8 * 4); - culler.bufferPlanes(buf); + culler.getCorners(buf); GL46.glNamedBufferSubData(buffer, indicesSize, buf); } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java b/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java index 19784f7fb..1518ea35f 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java @@ -16,7 +16,7 @@ public class CompileUtil { public static final Pattern vecType = Pattern.compile("^[biud]?vec([234])$"); public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$"); - protected static String generateHeader(GLSLVersion version, ShaderType type) { + public static String generateHeader(GLSLVersion version, ShaderType type) { return "#version " + version + '\n' + "#extension GL_ARB_explicit_attrib_location : enable\n" + "#extension GL_ARB_conservative_depth : enable\n" diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockMesh.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockMesh.java new file mode 100644 index 000000000..e69de29bb 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 b74a35989..ff76a0220 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java @@ -5,6 +5,7 @@ import java.nio.ByteBuffer; import org.jetbrains.annotations.Nullable; +import com.dreizak.miniball.highdim.Miniball; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.vertex.VertexList; diff --git a/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java b/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java index 3bb149f9c..b35664244 100644 --- a/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java +++ b/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java @@ -953,7 +953,7 @@ public class FrustumIntersection { return da >= 0.0f || db >= 0.0f; } - public void bufferPlanes(ByteBuffer buffer) { + public void getCorners(ByteBuffer buffer) { Vector3f scratch = new Vector3f(); Vector3f result = new Vector3f(); @@ -989,4 +989,60 @@ public class FrustumIntersection { return result.div(f); } + + /** + * Writes the planes of this frustum to the given buffer.

+ * Uses a different format that is friendly towards an optimized instruction-parallel + * implementation of sphere-frustum intersection.

+ * The format is as follows:

+ * {@code vec4(nxX, pxX, nyX, pyX)}
+ * {@code vec4(nxY, pxY, nyY, pyY)}
+ * {@code vec4(nxZ, pxZ, nyZ, pyZ)}
+ * {@code vec4(nxW, pxW, nyW, pyW)}
+ * {@code vec2(nzX, pzX)}
+ * {@code vec2(nzY, pzY)}
+ * {@code vec2(nzZ, pzZ)}
+ * {@code vec2(nzW, pzW)}
+ * + * @param buffer The buffer to write the planes to. + */ + public void getJozuPackedPlanes(ByteBuffer buffer) { + long addr = MemoryUtil.memAddress(buffer); + + MemoryUtil.memPutFloat(addr, nxX); + MemoryUtil.memPutFloat(addr + 4, pxX); + MemoryUtil.memPutFloat(addr + 8, nyX); + MemoryUtil.memPutFloat(addr + 12, pyX); + MemoryUtil.memPutFloat(addr + 16, nxY); + MemoryUtil.memPutFloat(addr + 20, pxY); + MemoryUtil.memPutFloat(addr + 24, nyY); + MemoryUtil.memPutFloat(addr + 28, pyY); + MemoryUtil.memPutFloat(addr + 32, nxZ); + MemoryUtil.memPutFloat(addr + 36, pxZ); + MemoryUtil.memPutFloat(addr + 40, nyZ); + MemoryUtil.memPutFloat(addr + 44, pyZ); + MemoryUtil.memPutFloat(addr + 48, nxW); + MemoryUtil.memPutFloat(addr + 52, pxW); + MemoryUtil.memPutFloat(addr + 56, nyW); + MemoryUtil.memPutFloat(addr + 60, pyW); + MemoryUtil.memPutFloat(addr + 64, nzX); + MemoryUtil.memPutFloat(addr + 68, pzX); + MemoryUtil.memPutFloat(addr + 72, nzY); + MemoryUtil.memPutFloat(addr + 76, pzY); + MemoryUtil.memPutFloat(addr + 80, nzZ); + MemoryUtil.memPutFloat(addr + 84, pzZ); + MemoryUtil.memPutFloat(addr + 88, nzW); + MemoryUtil.memPutFloat(addr + 92, pzW); + } + + public void getPlanes(ByteBuffer buffer) { + long addr = MemoryUtil.memAddress(buffer); + + planes[0].getToAddress(addr); + planes[1].getToAddress(addr + 16); + planes[2].getToAddress(addr + 32); + planes[3].getToAddress(addr + 48); + planes[4].getToAddress(addr + 64); + planes[5].getToAddress(addr + 80); + } } diff --git a/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl b/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl new file mode 100644 index 000000000..ade7d8eb4 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl @@ -0,0 +1,69 @@ +#version 450 +#define FLW_SUBGROUP_SIZE 32 +layout(local_size_x = FLW_SUBGROUP_SIZE) in; +#use "flywheel:compute/objects.glsl" +#use "flywheel:util/quaternion.glsl" + +layout(std130, binding = 3) uniform FrameData { + vec4 a1; // vec4(nx.x, px.x, ny.x, py.x) + vec4 a2; // vec4(nx.y, px.y, ny.y, py.y) + vec4 a3; // vec4(nx.z, px.z, ny.z, py.z) + vec4 a4; // vec4(nx.w, px.w, ny.w, py.w) + vec2 b1; // vec2(nz.x, pz.x) + vec2 b2; // vec2(nz.y, pz.y) + vec2 b3; // vec2(nz.z, pz.z) + vec2 b4; // vec2(nz.w, pz.w) + uint drawCount; +} frustum; + +// populated by instancers +layout(binding = 0) readonly buffer ObjectBuffer { + Instance objects[]; +}; + +layout(binding = 1) writeonly buffer TargetBuffer { + uint objectIDs[]; +}; + +layout(binding = 2) readonly buffer BoundingSpheres { + vec4 boundingSpheres[]; +}; + +layout(binding = 3) buffer DrawCommands { + MeshDrawCommand drawCommands[]; +}; + +// 83 - 27 = 56 spirv instruction results +bool testSphere(vec3 center, float radius) { + return + all(lessThanEqual(fma(frustum.a1, center.xxxx, fma(frustum.a2, center.yyyy, fma(frustum.a3, center.zzzz, frustum.a4))), -radius.xxxx)) && + all(lessThanEqual(fma(frustum.b1, center.xx, fma(frustum.b2, center.yy, fma(frustum.b3, center.zz, frustum.b4))), -radius.xx)); +} + +bool isVisible(uint objectID, uint batchID) { + vec4 sphere = boundingSpheres[batchID]; + + vec3 pivot = objects[objectID].pivot; + vec3 center = rotateQuat(sphere.xyz - pivot, objects[objectID].orientation) + pivot + objects[objectID].position; + float radius = sphere.r; + + return testSphere(center, radius); +} + +void main() { + uint objectID = gl_GlobalInvocationID.x; + + if (objectID >= frustum.drawCount) { + return; + } + + uint batchID = objects[objectID].batchID; + bool visible = isVisible(objectID, batchID); + + if (visible) { + uint batchIndex = atomicAdd(drawCommands[batchID].instanceCount, 1); + uint globalIndex = drawCommands[batchID].baseInstance + batchIndex; + + objectIDs[globalIndex] = objectID; + } +} diff --git a/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert b/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert new file mode 100644 index 000000000..ab72b635d --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert @@ -0,0 +1,28 @@ +#use "flywheel:api/vertex.glsl" +#use "flywheel:compute/objects.glsl" +#use "flywheel:pos_tex_normal.glsl" +#use "flywheel:context/world.vert" + +// populated by instancers +layout(binding = 0) readonly buffer ObjectBuffer { + Instance objects[]; +}; + +layout(binding = 1) readonly buffer TargetBuffer { + uint objectIDs[]; +}; + +void flw_instanceVertex(Instance i) { + flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - i.pivot, i.rotation) + i.pivot + i.pos, 1.0); + flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, i.rotation); + flw_vertexColor = i.color; + flw_vertexLight = i.light / 15.0; +} + +void main() { + uint instanceIndex = objectIDs[gl_BaseInstance + gl_InstanceID]; + flw_layoutVertex(); + Instance i = objects[instanceIndex]; + flw_instanceVertex(i); + flw_contextVertex(); +} diff --git a/src/main/resources/assets/flywheel/flywheel/compute/objects.glsl b/src/main/resources/assets/flywheel/flywheel/compute/objects.glsl new file mode 100644 index 000000000..c8179345a --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/compute/objects.glsl @@ -0,0 +1,17 @@ + +struct Instance { + ivec2 light; + vec4 color; + vec3 pos; + vec3 pivot; + vec4 rotation; + uint batchID; +}; + +struct MeshDrawCommands { + uint indexCount; + uint instanceCount; + uint firstIndex; + uint vertexOffset; + uint baseInstance; +}; diff --git a/src/main/resources/assets/flywheel/flywheel/compute/sphere_cull_frustum_experiment.glsl b/src/main/resources/assets/flywheel/flywheel/compute/sphere_cull_frustum_experiment.glsl new file mode 100644 index 000000000..43ef57f8b --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/compute/sphere_cull_frustum_experiment.glsl @@ -0,0 +1,72 @@ +#version 450 +#define FLW_SUBGROUP_SIZE 32 + +layout(local_size_x = FLW_SUBGROUP_SIZE) in; + + +// in uvec3 gl_NumWorkGroups; +// in uvec3 gl_WorkGroupID; +// in uvec3 gl_LocalInvocationID; +// in uvec3 gl_GlobalInvocationID; +// in uint gl_LocalInvocationIndex; + +layout(std430, binding = 0) buffer Frustum1 { + vec4 a1; // vec4(nx.x, px.x, ny.x, py.x) + vec4 a2; // vec4(nx.y, px.y, ny.y, py.y) + vec4 a3; // vec4(nx.z, px.z, ny.z, py.z) + vec4 a4; // vec4(nx.w, px.w, ny.w, py.w) + vec2 b1; // vec2(nz.x, pz.x) + vec2 b2; // vec2(nz.y, pz.y) + vec2 b3; // vec2(nz.z, pz.z) + vec2 b4; // vec2(nz.w, pz.w) +} frustum1; + +layout(binding = 1) buffer Frustum2 { + vec4 nx; + vec4 px; + vec4 ny; + vec4 py; + vec4 nz; + vec4 pz; +} frustum2; + +layout(binding = 2) buffer Result { + bool res1; + bool res2; + bool res3; +} result; + +// 83 - 27 = 56 spirv instruction results +bool testSphere1(vec4 sphere) { + return + all(lessThanEqual(fma(frustum1.a1, sphere.xxxx, fma(frustum1.a2, sphere.yyyy, fma(frustum1.a3, sphere.zzzz, frustum1.a4))), -sphere.wwww)) && + all(lessThanEqual(fma(frustum1.b1, sphere.xx, fma(frustum1.b2, sphere.yy, fma(frustum1.b3, sphere.zz, frustum1.b4))), -sphere.ww)); +} + +// 236 - 92 = 144 spirv instruction results +bool testSphere2(vec4 sphere) { + return + fma(frustum2.nx.x, sphere.x, fma(frustum2.nx.y, sphere.y, fma(frustum2.nx.z, sphere.z, frustum2.nx.w))) >= -sphere.w && + fma(frustum2.px.x, sphere.x, fma(frustum2.px.y, sphere.y, fma(frustum2.px.z, sphere.z, frustum2.px.w))) >= -sphere.w && + fma(frustum2.ny.x, sphere.x, fma(frustum2.ny.y, sphere.y, fma(frustum2.ny.z, sphere.z, frustum2.ny.w))) >= -sphere.w && + fma(frustum2.py.x, sphere.x, fma(frustum2.py.y, sphere.y, fma(frustum2.py.z, sphere.z, frustum2.py.w))) >= -sphere.w && + fma(frustum2.nz.x, sphere.x, fma(frustum2.nz.y, sphere.y, fma(frustum2.nz.z, sphere.z, frustum2.nz.w))) >= -sphere.w && + fma(frustum2.pz.x, sphere.x, fma(frustum2.pz.y, sphere.y, fma(frustum2.pz.z, sphere.z, frustum2.pz.w))) >= -sphere.w; +} + +// 322 - 240 = 82 spirv instruction results +bool testSphere3(vec4 sphere) { + return + (dot(frustum2.nx.xyz, sphere.xyz) + frustum2.nx.w) >= -sphere.w && + (dot(frustum2.px.xyz, sphere.xyz) + frustum2.px.w) >= -sphere.w && + (dot(frustum2.ny.xyz, sphere.xyz) + frustum2.ny.w) >= -sphere.w && + (dot(frustum2.py.xyz, sphere.xyz) + frustum2.py.w) >= -sphere.w && + (dot(frustum2.nz.xyz, sphere.xyz) + frustum2.nz.w) >= -sphere.w && + (dot(frustum2.pz.xyz, sphere.xyz) + frustum2.pz.w) >= -sphere.w; +} + +void main() { + result.res1 = testSphere1(vec4(0., 1., 0., 1.)); + result.res2 = testSphere2(vec4(0., 1., 0., 1.)); + result.res3 = testSphere3(vec4(0., 1., 0., 1.)); +} From e00d5772c3785ce9956e39e13439870f0591010b Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 5 Aug 2022 10:51:07 -0700 Subject: [PATCH 03/13] Things on the screen - Specialize MeshPool for the indirect engine - Temporarily force the engine to be indirect - Fix compilation issues with test shaders - VertexAttribute supports DSA - Fix Miniball issues in dev-env - VertexList implements PointSet for use with Miniball - Meshes store their bounding spheres - Add hook/system property to load renderdoc on client launch - StructTypes provide separate StorageBufferWriter for indirect --- build.gradle | 32 ++- .../api/struct/StorageBufferWriter.java | 10 + .../flywheel/api/struct/StructType.java | 2 + .../flywheel/api/vertex/VertexList.java | 24 +- .../jozufozu/flywheel/backend/Backend.java | 3 +- .../flywheel/backend/gl/GLSLVersion.java | 4 + .../backend/gl/array/VertexAttribute.java | 13 + .../backend/gl/array/VertexAttributeF.java | 6 + .../backend/gl/array/VertexAttributeI.java | 6 + .../backend/instancing/AbstractInstancer.java | 9 + .../instancing/batching/CPUInstancer.java | 8 - ...mpiler.java => ComputeCullerCompiler.java} | 26 +- .../instancing/indirect/DSABuffer.java | 10 - .../indirect/IndirectDrawCompiler.java | 65 +++++ .../instancing/indirect/IndirectEngine.java | 3 - .../indirect/IndirectInstancer.java | 6 +- .../instancing/indirect/IndirectList.java | 235 +++++++++++++----- .../instancing/indirect/IndirectMeshPool.java | 118 +++++++++ .../instancing/indirect/InstancedModel.java | 8 +- .../instancing/instancing/MeshPool.java | 4 +- .../jozufozu/flywheel/core/Components.java | 3 + .../flywheel/core/hardcoded/ModelPart.java | 12 + .../flywheel/core/layout/BufferLayout.java | 2 +- .../jozufozu/flywheel/core/model/Mesh.java | 3 + .../flywheel/core/model/ModelUtil.java | 10 + .../flywheel/core/model/SimpleMesh.java | 10 + .../flywheel/core/source/SourceFile.java | 12 +- .../core/structs/model/TransformedType.java | 6 + .../oriented/OrientedStorageWriter.java | 45 ++++ .../core/structs/oriented/OrientedType.java | 6 + .../flywheel/mixin/ClientMainMixin.java | 27 ++ .../flywheel/compute/cull_instances.glsl | 18 +- .../flywheel/compute/draw_instances.frag | 9 + .../flywheel/compute/draw_instances.vert | 11 +- .../flywheel/flywheel/compute/objects.glsl | 8 +- src/main/resources/flywheel.mixins.json | 1 + 36 files changed, 644 insertions(+), 131 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/api/struct/StorageBufferWriter.java rename src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/{ComputeCompiler.java => ComputeCullerCompiler.java} (52%) delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/DSABuffer.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawCompiler.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java create mode 100644 src/main/java/com/jozufozu/flywheel/mixin/ClientMainMixin.java create mode 100644 src/main/resources/assets/flywheel/flywheel/compute/draw_instances.frag diff --git a/build.gradle b/build.gradle index 5f87840a4..11b24de04 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,8 @@ apply plugin: 'eclipse' apply plugin: 'maven-publish' apply plugin: 'org.spongepowered.mixin' +jarJar.enable() + boolean dev = System.getenv('RELEASE') == null || System.getenv('RELEASE').equalsIgnoreCase('false'); ext.buildNumber = System.getenv('BUILD_NUMBER') @@ -31,8 +33,6 @@ version = mod_version + (dev && buildNumber != null ? "-${buildNumber}" : '') java.toolchain.languageVersion = JavaLanguageVersion.of(17) -jarJar.enable() - println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) minecraft { mappings channel: 'parchment', version: "${parchment_version}-${minecraft_version}" @@ -47,6 +47,7 @@ minecraft { property 'mixin.env.remapRefMap', 'true' property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" property 'flw.dumpShaderSource', 'true' + property 'flw.loadRenderDoc', 'true' arg '-mixin.config=flywheel.mixins.json' @@ -108,15 +109,31 @@ repositories { includeGroup "maven.modrinth" } } + mavenCentral() } +// Fix for loading non-mod libraries in dev-env, used for miniball. +// https://gist.github.com/SizableShrimp/66b22f1b24c255e1491c8d98d3f11f83 +// v--------------------------------------------------------------------v +configurations { + library + implementation.extendsFrom library +} + +minecraft.runs.all { + lazyToken('minecraft_classpath') { + configurations.library.copyRecursive().resolve().collect { it.absolutePath }.join(File.pathSeparator) + } +} +// ^--------------------------------------------------------------------^ + dependencies { minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" - implementation "com.dreizak:miniball:1.0.3" - jarJar(group: 'com.dreizak', name: 'miniball', version: '[1.0,2.0)') { - jarJar.pin(it, "[1.0,2.0)") + jarJar(group: 'com.dreizak', name: 'miniball', version: "1.0.3") { + jarJar.ranged(it, "[1.0,2.0)") } + library "com.dreizak:miniball:1.0.3" // switch to implementation for debugging compileOnly fg.deobf("maven.modrinth:starlight-forge:1.0.2+1.18.2") @@ -139,6 +156,7 @@ mixin { // Workaround for SpongePowered/MixinGradle#38 afterEvaluate { tasks.configureReobfTaskForReobfJar.mustRunAfter(tasks.compileJava) + tasks.configureReobfTaskForReobfJarJar.mustRunAfter(tasks.compileJava) } tasks.withType(JavaCompile).configureEach { @@ -151,6 +169,10 @@ javadoc { options.addStringOption('Xdoclint:none', '-quiet') } +compileJava { + options.compilerArgs = ['-Xdiags:verbose'] +} + jar { manifest { attributes([ diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/StorageBufferWriter.java b/src/main/java/com/jozufozu/flywheel/api/struct/StorageBufferWriter.java new file mode 100644 index 000000000..de7c9dfc1 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/api/struct/StorageBufferWriter.java @@ -0,0 +1,10 @@ +package com.jozufozu.flywheel.api.struct; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; + +public interface StorageBufferWriter { + + void write(final long ptr, final T instance, final int batchID); + + int getAlignment(); +} diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java index 7841f92de..3463c7cc5 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java @@ -36,6 +36,8 @@ public interface StructType { VertexTransformer getVertexTransformer(); + StorageBufferWriter getStorageBufferWriter(); + public interface VertexTransformer { void transform(MutableVertexList vertexList, S struct, ClientLevel level); } diff --git a/src/main/java/com/jozufozu/flywheel/api/vertex/VertexList.java b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexList.java index 519ef671f..cf9d84789 100644 --- a/src/main/java/com/jozufozu/flywheel/api/vertex/VertexList.java +++ b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexList.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.api.vertex; +import com.dreizak.miniball.model.PointSet; + /** * A read only view of a vertex buffer. * @@ -9,7 +11,7 @@ package com.jozufozu.flywheel.api.vertex; *

* TODO: more flexible elements? */ -public interface VertexList { +public interface VertexList extends PointSet { float x(int index); float y(int index); @@ -47,4 +49,24 @@ public interface VertexList { default boolean isEmpty() { return getVertexCount() == 0; } + + @Override + default int size() { + return getVertexCount(); + } + + @Override + default int dimension() { + return 3; + } + + @Override + default double coord(int i, int j) { + return switch (j) { + case 0 -> x(i); + case 1 -> y(i); + case 2 -> z(i); + default -> throw new IllegalArgumentException("Invalid dimension: " + j); + }; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index b48077615..20c6f1b62 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -54,7 +54,8 @@ public class Backend { } public static void refresh() { - TYPE = chooseEngine(); + // TODO: Revert when done testing + TYPE = BackendType.INDIRECT; // chooseEngine(); } public static boolean isOn() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java index eb128ad69..52ac01c2a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java @@ -26,4 +26,8 @@ public enum GLSLVersion { public String toString() { return Integer.toString(version); } + + public String getVersionLine() { + return "#version " + version + '\n'; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttribute.java b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttribute.java index 18c458643..d18a568d6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttribute.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttribute.java @@ -3,5 +3,18 @@ package com.jozufozu.flywheel.backend.gl.array; public interface VertexAttribute { int getByteWidth(); + /** + * Apply this vertex attribute to the bound vertex array. + * @param offset The byte offset to the first element of the attribute. + * @param i The attribute index. + * @param stride The byte stride between consecutive elements of the attribute. + */ void pointer(long offset, int i, int stride); + + /** + * Use DSA to apply this vertex attribute to the given vertex array. + * @param vaobj The vertex array object to modify. + * @param i The attribute index. + */ + void format(int vaobj, int i); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeF.java b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeF.java index 001e73d06..65cb51b7c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeF.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeF.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.backend.gl.array; import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL45; import com.jozufozu.flywheel.backend.gl.GlNumericType; @@ -22,4 +23,9 @@ public record VertexAttributeF(GlNumericType type, int size, boolean normalized) public void pointer(long offset, int i, int stride) { GL32.glVertexAttribPointer(i, size(), type().getGlEnum(), normalized(), stride, offset); } + + @Override + public void format(int vaobj, int i) { + GL45.glVertexArrayAttribFormat(vaobj, i, size(), type().getGlEnum(), normalized(), 0); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeI.java b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeI.java index d0f0bc903..64934cd3a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeI.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeI.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.backend.gl.array; import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL45; import com.jozufozu.flywheel.backend.gl.GlNumericType; @@ -21,4 +22,9 @@ public record VertexAttributeI(GlNumericType type, int size) implements VertexAt public void pointer(long offset, int i, int stride) { GL32.glVertexAttribIPointer(i, size(), type().getGlEnum(), stride, offset); } + + @Override + public void format(int vaobj, int i) { + GL45.glVertexArrayAttribIFormat(vaobj, i, size(), type().getGlEnum(), 0); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java index 77cad5920..4b5d4c0a0 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.instancing; import java.util.ArrayList; import java.util.BitSet; +import java.util.List; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.Instancer; @@ -60,6 +61,14 @@ public abstract class AbstractInstancer implements Inst return data.size(); } + public List getRange(int start, int end) { + return data.subList(start, end); + } + + public List getAll() { + return data; + } + protected void removeDeletedInstances() { // Figure out which elements are to be removed. final int oldSize = this.data.size(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java index 97fc5737f..61cc12016 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java @@ -19,14 +19,6 @@ public class CPUInstancer extends AbstractInstancer } } - public List getRange(int start, int end) { - return data.subList(start, end); - } - - public List getAll() { - return data; - } - @Override public void notifyDirty() { // noop diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java similarity index 52% rename from src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCompiler.java rename to src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java index 00f02c6d3..a104faf26 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java @@ -1,34 +1,42 @@ package com.jozufozu.flywheel.backend.instancing.indirect; import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.core.compile.Memoizer; import com.jozufozu.flywheel.core.compile.ProgramAssembler; +import com.jozufozu.flywheel.core.compile.ShaderCompilationException; import com.jozufozu.flywheel.core.source.CompilationContext; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.event.ReloadRenderersEvent; -public class ComputeCompiler extends Memoizer { +public class ComputeCullerCompiler extends Memoizer { - public static final ComputeCompiler INSTANCE = new ComputeCompiler(); + public static final ComputeCullerCompiler INSTANCE = new ComputeCullerCompiler(); - private ComputeCompiler() { + private ComputeCullerCompiler() { } @Override protected GlProgram _create(FileResolution file) { + CompilationContext context = new CompilationContext(); + + var header = GLSLVersion.V460.getVersionLine(); String source = file.getFile() - .generateFinalSource(new CompilationContext()); + .generateFinalSource(context); - var shader = new GlShader(source, ShaderType.COMPUTE, ImmutableList.of(file.getFileLoc())); + try { + var shader = new GlShader(header + source, ShaderType.COMPUTE, ImmutableList.of(file.getFileLoc())); - return new ProgramAssembler(file.getFileLoc()) - .attachShader(shader) - .link() - .build(GlProgram::new); + return new ProgramAssembler(file.getFileLoc()).attachShader(shader) + .link() + .build(GlProgram::new); + } catch (ShaderCompilationException e) { + throw e.withErrorLog(context); + } } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/DSABuffer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/DSABuffer.java deleted file mode 100644 index 43b1710e3..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/DSABuffer.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.indirect; - -public class DSABuffer { - int id; - int byteSize; - - public DSABuffer(int id) { - this.id = id; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawCompiler.java new file mode 100644 index 000000000..80fc7bbc5 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawCompiler.java @@ -0,0 +1,65 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import org.jetbrains.annotations.NotNull; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.WorldProgram; +import com.jozufozu.flywheel.core.compile.CompileUtil; +import com.jozufozu.flywheel.core.compile.Memoizer; +import com.jozufozu.flywheel.core.compile.ProgramAssembler; +import com.jozufozu.flywheel.core.compile.ShaderCompilationException; +import com.jozufozu.flywheel.core.source.CompilationContext; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.core.source.ShaderLoadingException; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.event.ReloadRenderersEvent; + +public class IndirectDrawCompiler extends Memoizer { + public static final IndirectDrawCompiler INSTANCE = new IndirectDrawCompiler(); + + private IndirectDrawCompiler() { + } + + @Override + protected GlProgram _create(IndirectDrawCompiler.Program program) { + + GlShader vertexShader = compile(program.vertex.getFile(), ShaderType.VERTEX); + GlShader fragmentShader = compile(program.fragment.getFile(), ShaderType.FRAGMENT); + + return new ProgramAssembler(program.vertex.getFileLoc()) + .attachShader(vertexShader) + .attachShader(fragmentShader) + .link() + .build(WorldProgram::new); + } + + @NotNull + private static GlShader compile(SourceFile file, ShaderType type) { + var context = new CompilationContext(); + try { + var header = CompileUtil.generateHeader(GLSLVersion.V460, type); + var source = file.generateFinalSource(context); + + return new GlShader(header + source, type, ImmutableList.of(file.name)); + } catch (ShaderCompilationException e) { + throw e.withErrorLog(context); + } + } + + @Override + protected void _destroy(GlProgram value) { + value.delete(); + } + + public static void invalidateAll(ReloadRenderersEvent ignored) { + INSTANCE.invalidate(); + } + + public record Program(FileResolution vertex, FileResolution fragment) { + + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java index 3294ff928..c60324ef6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -149,9 +149,6 @@ public class IndirectEngine implements Engine { model.init(renderLists); } uninitializedModels.clear(); - - MeshPool.getInstance() - .flush(); } private void shiftListeners(int cX, int cY, int cZ) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java index ff6ee8e51..bcc09acf2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java @@ -11,7 +11,7 @@ public class IndirectInstancer extends AbstractInstance public final BufferLayout instanceFormat; public final StructType structType; public final InstancedModel parent; - int maxInstanceCount = 0; + int instanceCount = 0; boolean anyToUpdate; @@ -28,7 +28,7 @@ public class IndirectInstancer extends AbstractInstance } public boolean isEmpty() { - return !anyToUpdate && !anyToRemove && maxInstanceCount == 0; + return !anyToUpdate && !anyToRemove && instanceCount == 0; } void update() { @@ -36,7 +36,7 @@ public class IndirectInstancer extends AbstractInstance removeDeletedInstances(); } - maxInstanceCount = data.size(); + instanceCount = data.size(); anyToRemove = false; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java index b68aec224..cff54901c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java @@ -1,121 +1,244 @@ package com.jozufozu.flywheel.backend.instancing.indirect; -import java.nio.ByteBuffer; +import static org.lwjgl.opengl.GL46.*; + +import java.text.Format; import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import org.lwjgl.opengl.GL46; import org.lwjgl.opengl.GL46C; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.struct.StorageBufferWriter; import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.gl.GlNumericType; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.backend.instancing.instancing.MeshPool; import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.Materials; +import com.jozufozu.flywheel.core.QuadConverter; import com.jozufozu.flywheel.core.model.Mesh; +import com.jozufozu.flywheel.core.uniform.UniformBuffer; +import com.jozufozu.flywheel.core.vertex.Formats; public class IndirectList { + private static final int DRAW_COMMAND_STRIDE = 20; + + final StorageBufferWriter storageBufferWriter; final GlProgram compute; final GlProgram draw; + private final StructType type; + private final long maxObjectCount; + private final long objectStride; + private final int maxBatchCount; + private final long objectClientStorage; + private final int elementBuffer; /** * Stores raw instance data per-object. */ - DSABuffer objectBuffer; + int objectBuffer; + int targetBuffer; /** * Stores bounding spheres */ - DSABuffer boundingSphereBuffer; + int boundingSphereBuffer; /** * Stores drawIndirect structs. */ - DSABuffer drawBuffer; - DSABuffer targetBuffer; + int drawBuffer; - final int[] buffers = new int[4]; + final IndirectMeshPool meshPool; + + int vertexArray; + + final int[] shaderStorageBuffers = new int[4]; final List> batches = new ArrayList<>(); IndirectList(StructType structType) { - GL46.glCreateBuffers(buffers); - objectBuffer = new DSABuffer(buffers[0]); - targetBuffer = new DSABuffer(buffers[1]); - boundingSphereBuffer = new DSABuffer(buffers[2]); - drawBuffer = new DSABuffer(buffers[3]); + type = structType; + storageBufferWriter = type.getStorageBufferWriter(); - compute = ComputeCompiler.INSTANCE.get(Components.Files.CULL_INSTANCES); - draw = null; + if (storageBufferWriter == null) { + throw new NullPointerException(); + } + + glCreateBuffers(shaderStorageBuffers); + objectBuffer = shaderStorageBuffers[0]; + targetBuffer = shaderStorageBuffers[1]; + boundingSphereBuffer = shaderStorageBuffers[2]; + drawBuffer = shaderStorageBuffers[3]; + meshPool = new IndirectMeshPool(Formats.BLOCK, 1024); + + // FIXME: Resizable buffers + maxObjectCount = 1024L * 100L; + maxBatchCount = 64; + + // +4 for the batch id + objectStride = storageBufferWriter.getAlignment(); + glNamedBufferStorage(objectBuffer, objectStride * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); + glNamedBufferStorage(targetBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); + glNamedBufferStorage(boundingSphereBuffer, 16 * maxBatchCount, GL_DYNAMIC_STORAGE_BIT); + glNamedBufferStorage(drawBuffer, DRAW_COMMAND_STRIDE * maxBatchCount, GL_DYNAMIC_STORAGE_BIT); + + objectClientStorage = MemoryUtil.nmemAlloc(objectStride * maxObjectCount); + + vertexArray = glCreateVertexArrays(); + + elementBuffer = QuadConverter.getInstance() + .quads2Tris(2048).buffer.handle(); + setupVertexArray(); + + compute = ComputeCullerCompiler.INSTANCE.get(Components.Files.CULL_INSTANCES); + draw = IndirectDrawCompiler.INSTANCE.get(new IndirectDrawCompiler.Program(Components.Files.DRAW_INDIRECT_VERTEX, Components.Files.DRAW_INDIRECT_FRAGMENT)); + } + + private void setupVertexArray() { + glVertexArrayElementBuffer(vertexArray, elementBuffer); + + var meshLayout = Formats.BLOCK.getLayout(); + var meshAttribs = meshLayout.getAttributeCount(); + + var attributes = meshLayout.getAttributes(); + + long offset = 0; + for (int i = 0; i < meshAttribs; i++) { + var attribute = attributes.get(i); + glEnableVertexArrayAttrib(vertexArray, i); + glVertexArrayVertexBuffer(vertexArray, i, meshPool.vbo, offset, meshLayout.getStride()); + attribute.format(vertexArray, i); + offset += attribute + .getByteWidth(); + } + + glEnableVertexArrayAttrib(vertexArray, meshAttribs); + glVertexArrayVertexBuffer(vertexArray, meshAttribs, targetBuffer, 0, 4); + glVertexArrayAttribIFormat(vertexArray, meshAttribs, 1, GlNumericType.UINT.getGlEnum(), 0); } public void add(Mesh mesh, IndirectInstancer instancer) { - var pool = MeshPool.getInstance(); - var buffered = pool.alloc(mesh); - - batches.add(new Batch<>(instancer, buffered)); + batches.add(new Batch<>(instancer, meshPool.alloc(mesh))); } - public void prepare() { + void submit() { + int instanceCountThisFrame = calculateTotalInstanceCount(); + if (instanceCountThisFrame == 0) { + return; + } + + meshPool.uploadAll(); + uploadInstanceData(); + uploadBoundingSpheres(); + uploadIndirectCommands(); + + UniformBuffer.getInstance().sync(); + + dispatchCompute(instanceCountThisFrame); + issueMemoryBarrier(); + dispatchDraw(); + } + + private void dispatchDraw() { + draw.bind(); + Materials.BELL.setup(); + glVertexArrayElementBuffer(vertexArray, elementBuffer); + glBindVertexArray(vertexArray); + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, drawBuffer); + glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, 0, batches.size(), 0); + Materials.BELL.clear(); + } + + private static void issueMemoryBarrier() { + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + } + + private void uploadBoundingSpheres() { try (var stack = MemoryStack.stackPush()) { - var size = batches.size() * 20; + final int size = batches.size() * 16; + final long basePtr = stack.nmalloc(size); + + long ptr = basePtr; + for (Batch batch : batches) { + var boundingSphere = batch.mesh.mesh.getBoundingSphere(); + boundingSphere.getToAddress(ptr); + ptr += 16; + } + + GL46C.nglNamedBufferSubData(boundingSphereBuffer, 0, size, basePtr); + } + } + + private void dispatchCompute(int instanceCount) { + compute.bind(); + glBindBuffersBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffers); + + var groupCount = (instanceCount + 31) >> 5; // ceil(totalInstanceCount / 32) + glDispatchCompute(groupCount, 1, 1); + } + + private void uploadInstanceData() { + long ptr = objectClientStorage; + int baseInstance = 0; + int batchID = 0; + for (var batch : batches) { + batch.baseInstance = baseInstance; + var instancer = batch.instancer; + for (T t : instancer.getAll()) { + storageBufferWriter.write(ptr, t, batchID); + ptr += objectStride; + } + baseInstance += batch.instancer.instanceCount; + batchID++; + } + + GL46C.nglNamedBufferSubData(objectBuffer, 0, ptr - objectClientStorage, objectClientStorage); + + } + + private void uploadIndirectCommands() { + try (var stack = MemoryStack.stackPush()) { + var size = batches.size() * DRAW_COMMAND_STRIDE; long basePtr = stack.nmalloc(size); long writePtr = basePtr; for (Batch batch : batches) { batch.writeIndirectCommand(writePtr); - writePtr += 20; + writePtr += DRAW_COMMAND_STRIDE; } - GL46C.nglNamedBufferData(drawBuffer.id, size, basePtr, GL46.GL_STREAM_DRAW); + GL46C.nglNamedBufferSubData(drawBuffer, 0, size, basePtr); } } - public void submit() { - - compute.bind(); - GL46.glBindBuffersBase(GL46.GL_SHADER_STORAGE_BUFFER, 0, buffers); - - var groupCount = (getTotalInstanceCount() + 31) >> 5; // ceil(totalInstanceCount / 32) - GL46.glDispatchCompute(groupCount, 1, 1); - - draw.bind(); - GL46.glMemoryBarrier(GL46.GL_SHADER_STORAGE_BARRIER_BIT); - GL46.glBindBuffer(GL46.GL_DRAW_INDIRECT_BUFFER, drawBuffer.id); - - GL46.glMultiDrawElementsIndirect(GL46.GL_TRIANGLES, GL46.GL_UNSIGNED_INT, 0, batches.size(), 0); - } - - private int getTotalInstanceCount() { - return 0; + private int calculateTotalInstanceCount() { + int total = 0; + for (Batch batch : batches) { + batch.instancer.update(); + total += batch.instancer.instanceCount; + } + return total; } private static final class Batch { final IndirectInstancer instancer; - final MeshPool.BufferedMesh mesh; + final IndirectMeshPool.BufferedMesh mesh; + int baseInstance; - private Batch(IndirectInstancer instancer, MeshPool.BufferedMesh mesh) { + private Batch(IndirectInstancer instancer, IndirectMeshPool.BufferedMesh mesh) { this.instancer = instancer; this.mesh = mesh; } public void writeIndirectCommand(long ptr) { - // typedef struct { - // GLuint count; - // GLuint instanceCount; - // GLuint firstIndex; - // GLuint baseVertex; - // GLuint baseInstance; - //} DrawElementsIndirectCommand; - - MemoryUtil.memPutInt(ptr, mesh.getVertexCount()); - MemoryUtil.memPutInt(ptr + 4, 0); - MemoryUtil.memPutInt(ptr + 8, 0); - MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); - MemoryUtil.memPutInt(ptr + 16, 0); + MemoryUtil.memPutInt(ptr, mesh.getVertexCount()); // count + MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be incremented by the compute shader + MemoryUtil.memPutInt(ptr + 8, 0); // firstIndex - all models share the same index buffer + MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex + MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java new file mode 100644 index 000000000..ade54f176 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java @@ -0,0 +1,118 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import static org.lwjgl.opengl.GL46.*; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer; +import com.jozufozu.flywheel.core.model.Mesh; + +public class IndirectMeshPool { + + private final Map meshes = new HashMap<>(); + private final List meshList = new ArrayList<>(); + + final VertexType vertexType; + + final int vbo; + private final ByteBuffer clientStorage; + + private boolean dirty; + + /** + * Create a new mesh pool. + */ + public IndirectMeshPool(VertexType type, int vertexCapacity) { + vertexType = type; + vbo = glCreateBuffers(); + var byteCapacity = type.byteOffset(vertexCapacity); + glNamedBufferStorage(vbo, byteCapacity, GL_DYNAMIC_STORAGE_BIT); + clientStorage = MemoryUtil.memAlloc(byteCapacity); + } + + /** + * Allocate a model in the arena. + * + * @param mesh The model to allocate. + * @return A handle to the allocated model. + */ + public BufferedMesh alloc(Mesh mesh) { + return meshes.computeIfAbsent(mesh, m -> { + BufferedMesh bufferedModel = new BufferedMesh(m); + meshList.add(bufferedModel); + + dirty = true; + return bufferedModel; + }); + } + + @Nullable + public BufferedMesh get(Mesh mesh) { + return meshes.get(mesh); + } + + void uploadAll() { + if (!dirty) { + return; + } + dirty = false; + + int byteIndex = 0; + int baseVertex = 0; + for (BufferedMesh model : meshList) { + model.byteIndex = byteIndex; + model.baseVertex = baseVertex; + + model.buffer(clientStorage); + + byteIndex += model.getByteSize(); + baseVertex += model.mesh.getVertexCount(); + } + + glNamedBufferSubData(vbo, 0, clientStorage); + } + + public void delete() { + glDeleteBuffers(vbo); + meshes.clear(); + meshList.clear(); + } + + public class BufferedMesh { + + public final Mesh mesh; + private long byteIndex; + private int baseVertex; + + public BufferedMesh(Mesh mesh) { + this.mesh = mesh; + } + + private void buffer(ByteBuffer buffer) { + var writer = IndirectMeshPool.this.vertexType.createWriter(buffer); + writer.seek(this.byteIndex); + writer.writeVertexList(this.mesh.getReader()); + } + + public int getByteSize() { + return IndirectMeshPool.this.vertexType.getLayout().getStride() * this.mesh.getVertexCount(); + } + + public int getBaseVertex() { + return baseVertex; + } + + public int getVertexCount() { + return this.mesh.getVertexCount(); + } + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java index 5c5f032bb..e3faeb739 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java @@ -1,13 +1,7 @@ package com.jozufozu.flywheel.backend.instancing.indirect; -import java.util.List; -import java.util.Map; - -import com.jozufozu.flywheel.api.RenderStage; import com.jozufozu.flywheel.api.instancer.InstancedPart; -import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Model; public class InstancedModel { @@ -42,7 +36,7 @@ public class InstancedModel { } public int getVertexCount() { - return model.getVertexCount() * instancer.maxInstanceCount; + return model.getVertexCount() * instancer.instanceCount; } public void delete() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java index 053af8645..0438ac71d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java @@ -27,7 +27,7 @@ public class MeshPool { private static MeshPool allocator; - public static MeshPool getInstance() { + static MeshPool getInstance() { if (allocator == null) { allocator = new MeshPool(); } @@ -188,7 +188,7 @@ public class MeshPool { public class BufferedMesh { private final ElementBuffer ebo; - private final Mesh mesh; + public final Mesh mesh; private final VertexType type; private long byteIndex; private int baseVertex; diff --git a/src/main/java/com/jozufozu/flywheel/core/Components.java b/src/main/java/com/jozufozu/flywheel/core/Components.java index 581dd62be..f89df8d97 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Components.java +++ b/src/main/java/com/jozufozu/flywheel/core/Components.java @@ -49,6 +49,8 @@ public class Components { public static final FileResolution CRUMBLING_VERTEX = contextVertex(ResourceUtil.subPath(Names.CRUMBLING, ".vert")); public static final FileResolution CRUMBLING_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.CRUMBLING, ".frag")); public static final FileResolution CULL_INSTANCES = compute(Flywheel.rl("compute/cull_instances.glsl")); + public static final FileResolution DRAW_INDIRECT_VERTEX = FileResolution.get(ResourceUtil.subPath(Names.DRAW_INDIRECT, ".vert")); + public static final FileResolution DRAW_INDIRECT_FRAGMENT = FileResolution.get(ResourceUtil.subPath(Names.DRAW_INDIRECT, ".frag")); private static FileResolution compute(ResourceLocation rl) { return FileResolution.get(rl); @@ -115,5 +117,6 @@ public class Components { public static final ResourceLocation SHADED = Flywheel.rl("material/shaded"); public static final ResourceLocation WORLD = Flywheel.rl("context/world"); public static final ResourceLocation CRUMBLING = Flywheel.rl("context/crumbling"); + public static final ResourceLocation DRAW_INDIRECT = Flywheel.rl("compute/draw_instances"); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java index b639d64e0..91620e819 100644 --- a/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java +++ b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java @@ -2,19 +2,24 @@ package com.jozufozu.flywheel.core.hardcoded; import java.util.List; +import org.jetbrains.annotations.NotNull; import org.lwjgl.system.MemoryStack; import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.core.model.Mesh; +import com.jozufozu.flywheel.core.model.ModelUtil; import com.jozufozu.flywheel.core.vertex.Formats; import com.jozufozu.flywheel.core.vertex.PosTexNormalVertex; import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe; +import com.jozufozu.flywheel.util.joml.Vector4f; +import com.jozufozu.flywheel.util.joml.Vector4fc; public class ModelPart implements Mesh { private final int vertices; private final String name; private final VertexList reader; + private final @NotNull Vector4f boundingSphere; public ModelPart(List cuboids, String name) { this.name = name; @@ -35,6 +40,8 @@ public class ModelPart implements Mesh { reader = writer.intoReader(this.vertices); } + + boundingSphere = ModelUtil.computeBoundingSphere(reader); } public static PartBuilder builder(String name, int sizeU, int sizeV) { @@ -60,4 +67,9 @@ public class ModelPart implements Mesh { public PosTexNormalVertex getVertexType() { return Formats.POS_TEX_NORMAL; } + + @Override + public Vector4fc getBoundingSphere() { + return boundingSphere; + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java b/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java index 74f958d6a..8f28d9f2d 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java @@ -35,7 +35,7 @@ public class BufferLayout { this.stride = calculateStride(this.attributes) + padding; } - public Collection getAttributes() { + public List getAttributes() { return attributes; } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java b/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java index c382211c7..dcc7ffa8f 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java @@ -7,6 +7,7 @@ import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexWriter; import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer; import com.jozufozu.flywheel.core.QuadConverter; +import com.jozufozu.flywheel.util.joml.Vector4fc; /** * A mesh that can be rendered by flywheel. @@ -39,6 +40,8 @@ public interface Mesh { VertexList getReader(); + Vector4fc getBoundingSphere(); + /** * @return The number of vertices the model has. */ 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 ff76a0220..fe3041287 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java @@ -3,6 +3,7 @@ package com.jozufozu.flywheel.core.model; import java.lang.reflect.Field; import java.nio.ByteBuffer; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.dreizak.miniball.highdim.Miniball; @@ -11,6 +12,7 @@ import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.vertex.Formats; +import com.jozufozu.flywheel.util.joml.Vector4f; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.datafixers.util.Pair; @@ -74,4 +76,12 @@ public class ModelUtil { } return null; } + + @NotNull + public static Vector4f computeBoundingSphere(VertexList reader) { + var miniball = new Miniball(reader); + double[] center = miniball.center(); + double radius = miniball.radius(); + return new Vector4f((float) center[0], (float) center[1], (float) center[2], (float) radius); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/SimpleMesh.java b/src/main/java/com/jozufozu/flywheel/core/model/SimpleMesh.java index 55f76ef44..e5808067f 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/SimpleMesh.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/SimpleMesh.java @@ -2,16 +2,21 @@ package com.jozufozu.flywheel.core.model; import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.util.joml.Vector4f; +import com.jozufozu.flywheel.util.joml.Vector4fc; public class SimpleMesh implements Mesh { private final VertexList reader; private final VertexType vertexType; private final String name; + private final Vector4f boundingSphere; public SimpleMesh(VertexList reader, VertexType vertexType, String name) { this.reader = reader; this.vertexType = vertexType; this.name = name; + + boundingSphere = ModelUtil.computeBoundingSphere(reader); } @Override @@ -29,6 +34,11 @@ public class SimpleMesh implements Mesh { return reader; } + @Override + public Vector4fc getBoundingSphere() { + return boundingSphere; + } + @Override public String toString() { return "SimpleMesh{" + "name='" + name + "',vertexType='" + vertexType + "}"; diff --git a/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java index 4e63ef409..2f799677b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java @@ -184,24 +184,24 @@ public class SourceFile { return "#use " + '"' + name + '"'; } - public String generateFinalSource(CompilationContext env) { - return generateFinalSource(env, Collections.emptyList()); + public String generateFinalSource(CompilationContext context) { + return generateFinalSource(context, Collections.emptyList()); } - public String generateFinalSource(CompilationContext env, List> replacements) { + public String generateFinalSource(CompilationContext context, List> replacements) { var out = new StringBuilder(); for (Import include : flattenedImports) { SourceFile file = include.getFile(); - if (file == null || env.contains(file)) { + if (file == null || context.contains(file)) { continue; } - out.append(file.generateLineHeader(env)) + out.append(file.generateLineHeader(context)) .append(file.replaceAndElide(replacements)); } - out.append(this.generateLineHeader(env)) + out.append(this.generateLineHeader(context)) .append(this.replaceAndElide(replacements)); return out.toString(); diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedType.java b/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedType.java index 30d750672..c754bc1ab 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedType.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedType.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.core.structs.model; import java.nio.ByteBuffer; +import com.jozufozu.flywheel.api.struct.StorageBufferWriter; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.core.Components; @@ -33,6 +34,11 @@ public class TransformedType implements StructType { return new TransformedWriterUnsafe(this, backing); } + @Override + public StorageBufferWriter getStorageBufferWriter() { + return null; // TODO + } + @Override public FileResolution getInstanceShader() { return Components.Files.TRANSFORMED; diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java new file mode 100644 index 000000000..43c8a0f82 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java @@ -0,0 +1,45 @@ +package com.jozufozu.flywheel.core.structs.oriented; + + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.struct.StorageBufferWriter; + +public class OrientedStorageWriter implements StorageBufferWriter { + + public static final OrientedStorageWriter INSTANCE = new OrientedStorageWriter(); + + private OrientedStorageWriter() { + } + + @Override + public void write(final long ptr, OrientedPart d, final int batchID) { + MemoryUtil.memPutFloat(ptr, d.qX); + MemoryUtil.memPutFloat(ptr + 4, d.qY); + MemoryUtil.memPutFloat(ptr + 8, d.qZ); + MemoryUtil.memPutFloat(ptr + 12, d.qW); + + MemoryUtil.memPutFloat(ptr + 16, d.posX); + MemoryUtil.memPutFloat(ptr + 20, d.posY); + MemoryUtil.memPutFloat(ptr + 24, d.posZ); + + MemoryUtil.memPutFloat(ptr + 28, d.pivotX); + MemoryUtil.memPutFloat(ptr + 32, d.pivotY); + MemoryUtil.memPutFloat(ptr + 36, d.pivotZ); + + MemoryUtil.memPutShort(ptr + 40, d.blockLight); + MemoryUtil.memPutShort(ptr + 42, d.skyLight); + + MemoryUtil.memPutByte(ptr + 44, d.r); + MemoryUtil.memPutByte(ptr + 45, d.g); + MemoryUtil.memPutByte(ptr + 46, d.b); + MemoryUtil.memPutByte(ptr + 47, d.a); + + MemoryUtil.memPutInt(ptr + 48, batchID); + } + + @Override + public int getAlignment() { + return 52; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java index 93a0226ef..be6fbfa28 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.core.structs.oriented; import java.nio.ByteBuffer; +import com.jozufozu.flywheel.api.struct.StorageBufferWriter; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.core.Components; @@ -36,6 +37,11 @@ public class OrientedType implements StructType { return new OrientedWriterUnsafe(this, backing); } + @Override + public OrientedStorageWriter getStorageBufferWriter() { + return OrientedStorageWriter.INSTANCE; + } + @Override public FileResolution getInstanceShader() { return Components.Files.ORIENTED; diff --git a/src/main/java/com/jozufozu/flywheel/mixin/ClientMainMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/ClientMainMixin.java new file mode 100644 index 000000000..49f7d0abe --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/ClientMainMixin.java @@ -0,0 +1,27 @@ +package com.jozufozu.flywheel.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.main.Main; + +@Mixin(Main.class) +public class ClientMainMixin { + @Inject(method = "main", at = @At("HEAD")) + private static void injectRenderDoc(CallbackInfo ci) { + // Only try to load RenderDoc if a system property is set. + if (System.getProperty("flw.loadRenderDoc") == null) { + return; + } + + try { + System.loadLibrary("renderdoc"); + } catch (Throwable ignored) { + // Oh well, we tried. + // On Windows, RenderDoc installs to "C:\Program Files\RenderDoc\" + System.err.println("Is RenderDoc in your PATH?"); + } + } +} diff --git a/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl b/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl index ade7d8eb4..ef4577120 100644 --- a/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl +++ b/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl @@ -1,10 +1,9 @@ -#version 450 #define FLW_SUBGROUP_SIZE 32 layout(local_size_x = FLW_SUBGROUP_SIZE) in; #use "flywheel:compute/objects.glsl" #use "flywheel:util/quaternion.glsl" -layout(std130, binding = 3) uniform FrameData { +layout(std140, binding = 3) uniform FrameData { vec4 a1; // vec4(nx.x, px.x, ny.x, py.x) vec4 a2; // vec4(nx.y, px.y, ny.y, py.y) vec4 a3; // vec4(nx.z, px.z, ny.z, py.z) @@ -13,23 +12,22 @@ layout(std130, binding = 3) uniform FrameData { vec2 b2; // vec2(nz.y, pz.y) vec2 b3; // vec2(nz.z, pz.z) vec2 b4; // vec2(nz.w, pz.w) - uint drawCount; } frustum; // populated by instancers -layout(binding = 0) readonly buffer ObjectBuffer { +layout(std430, binding = 0) readonly buffer ObjectBuffer { Instance objects[]; }; -layout(binding = 1) writeonly buffer TargetBuffer { +layout(std430, binding = 1) writeonly buffer TargetBuffer { uint objectIDs[]; }; -layout(binding = 2) readonly buffer BoundingSpheres { +layout(std430, binding = 2) readonly buffer BoundingSpheres { vec4 boundingSpheres[]; }; -layout(binding = 3) buffer DrawCommands { +layout(std430, binding = 3) buffer DrawCommands { MeshDrawCommand drawCommands[]; }; @@ -44,16 +42,16 @@ bool isVisible(uint objectID, uint batchID) { vec4 sphere = boundingSpheres[batchID]; vec3 pivot = objects[objectID].pivot; - vec3 center = rotateQuat(sphere.xyz - pivot, objects[objectID].orientation) + pivot + objects[objectID].position; + vec3 center = rotateVertexByQuat(sphere.xyz - pivot, objects[objectID].rotation) + pivot + objects[objectID].pos; float radius = sphere.r; - return testSphere(center, radius); + return true; //testSphere(center, radius); } void main() { uint objectID = gl_GlobalInvocationID.x; - if (objectID >= frustum.drawCount) { + if (objectID >= objects.length()) { return; } diff --git a/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.frag b/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.frag new file mode 100644 index 000000000..6591bea51 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.frag @@ -0,0 +1,9 @@ +#use "flywheel:api/fragment.glsl" +#use "flywheel:context/world.frag" +#use "flywheel:material/default.frag" + +void main() { + flw_initFragment(); + flw_materialFragment(); + flw_contextFragment(); +} diff --git a/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert b/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert index ab72b635d..030d82200 100644 --- a/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert +++ b/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert @@ -1,22 +1,23 @@ #use "flywheel:api/vertex.glsl" #use "flywheel:compute/objects.glsl" -#use "flywheel:pos_tex_normal.glsl" +#use "flywheel:layout/block.vert" #use "flywheel:context/world.vert" +#use "flywheel:util/quaternion.glsl" // populated by instancers -layout(binding = 0) readonly buffer ObjectBuffer { +layout(std430, binding = 0) readonly buffer ObjectBuffer { Instance objects[]; }; -layout(binding = 1) readonly buffer TargetBuffer { +layout(std430, binding = 1) readonly buffer TargetBuffer { uint objectIDs[]; }; void flw_instanceVertex(Instance i) { flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - i.pivot, i.rotation) + i.pivot + i.pos, 1.0); flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, i.rotation); - flw_vertexColor = i.color; - flw_vertexLight = i.light / 15.0; + flw_vertexColor = unpackUnorm4x8(i.color); + flw_vertexLight = unpackUnorm2x16(i.light) / 15.0; } void main() { diff --git a/src/main/resources/assets/flywheel/flywheel/compute/objects.glsl b/src/main/resources/assets/flywheel/flywheel/compute/objects.glsl index c8179345a..635ae39db 100644 --- a/src/main/resources/assets/flywheel/flywheel/compute/objects.glsl +++ b/src/main/resources/assets/flywheel/flywheel/compute/objects.glsl @@ -1,14 +1,14 @@ struct Instance { - ivec2 light; - vec4 color; + vec4 rotation; vec3 pos; vec3 pivot; - vec4 rotation; + uint light; + uint color; uint batchID; }; -struct MeshDrawCommands { +struct MeshDrawCommand { uint indexCount; uint instanceCount; uint firstIndex; diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index fab33485c..5c36a9163 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -11,6 +11,7 @@ "BufferUploaderMixin", "ChunkRebuildHooksMixin", "ClientLevelMixin", + "ClientMainMixin", "EntityTypeMixin", "FixFabulousDepthMixin", "FogUpdateMixin", From 0a4fcf27d8a36cf9a60a7b488c29cf342a89c5f8 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 5 Aug 2022 21:53:21 -0700 Subject: [PATCH 04/13] Functional frustum filter - Manually fix alignment issue - Align UBO size to 16 - Manual frustum ubo upload --- .../flywheel/api/uniform/UniformProvider.java | 2 +- .../instancing/indirect/FrustumUBO.java | 31 +++++++++++ .../instancing/indirect/IndirectEngine.java | 21 ++++++- .../instancing/indirect/IndirectList.java | 39 ++++++------- .../instancing/indirect/RenderLists.java | 1 + .../jozufozu/flywheel/core/RenderContext.java | 7 +-- .../oriented/OrientedStorageWriter.java | 22 ++++---- .../flywheel/core/uniform/FogProvider.java | 2 +- .../flywheel/core/uniform/UniformBuffer.java | 10 +++- .../flywheel/core/uniform/ViewProvider.java | 2 +- .../flywheel/mixin/LevelRendererMixin.java | 4 +- .../util/joml/FrustumIntersection.java | 6 +- .../flywheel/compute/cull_instances.glsl | 55 +++++++++++++------ 13 files changed, 137 insertions(+), 65 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/FrustumUBO.java diff --git a/src/main/java/com/jozufozu/flywheel/api/uniform/UniformProvider.java b/src/main/java/com/jozufozu/flywheel/api/uniform/UniformProvider.java index 2e189f452..b23639183 100644 --- a/src/main/java/com/jozufozu/flywheel/api/uniform/UniformProvider.java +++ b/src/main/java/com/jozufozu/flywheel/api/uniform/UniformProvider.java @@ -9,7 +9,7 @@ public abstract class UniformProvider { protected ByteBuffer buffer; protected Notifier notifier; - public abstract int getSize(); + public abstract int getActualByteSize(); public void updatePtr(ByteBuffer backing, Notifier notifier) { this.buffer = backing; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/FrustumUBO.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/FrustumUBO.java new file mode 100644 index 000000000..554d78dea --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/FrustumUBO.java @@ -0,0 +1,31 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import static org.lwjgl.opengl.GL46.*; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.util.joml.FrustumIntersection; + +// This should be a push constant :whywheel: +public class FrustumUBO { + + public static final int BUFFER_SIZE = 96; + + private final int ubo; + private final long clientStorage; + + FrustumUBO() { + ubo = glCreateBuffers(); + glNamedBufferStorage(ubo, BUFFER_SIZE, GL_DYNAMIC_STORAGE_BIT); + clientStorage = MemoryUtil.nmemAlloc(BUFFER_SIZE); + } + + public void update(FrustumIntersection frustum) { + frustum.getJozuPackedPlanes(clientStorage); + nglNamedBufferSubData(ubo, 0, BUFFER_SIZE, clientStorage); + } + + public void bind() { + glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java index c60324ef6..ddc66f0a1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -17,7 +17,6 @@ import com.jozufozu.flywheel.backend.gl.GlTextureUnit; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.TaskEngine; -import com.jozufozu.flywheel.backend.instancing.instancing.MeshPool; import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysCompiler; @@ -46,6 +45,8 @@ public class IndirectEngine implements Engine { protected final List> uninitializedModels = new ArrayList<>(); protected final RenderLists renderLists = new RenderLists(); + private FrustumUBO frustumUBO; + /** * The set of instance managers that are attached to this engine. */ @@ -76,7 +77,7 @@ public class IndirectEngine implements Engine { setup(); for (var group : groups) { - group.submit(); + group.submit(frustumUBO); } } @@ -145,10 +146,26 @@ public class IndirectEngine implements Engine { @Override public void beginFrame(TaskEngine taskEngine, RenderContext context) { + if (frustumUBO == null) { + frustumUBO = new FrustumUBO(); + } + for (var model : uninitializedModels) { model.init(renderLists); } uninitializedModels.clear(); + + Vec3 camera = context.camera() + .getPosition(); + + var camX = (float) (camera.x - originCoordinate.getX()); + var camY = (float) (camera.y - originCoordinate.getY()); + var camZ = (float) (camera.z - originCoordinate.getZ()); + + var culler = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ); + + frustumUBO.update(culler); + } private void shiftListeners(int cX, int cY, int cZ) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java index cff54901c..5e6642b7e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java @@ -2,18 +2,15 @@ package com.jozufozu.flywheel.backend.instancing.indirect; import static org.lwjgl.opengl.GL46.*; -import java.text.Format; import java.util.ArrayList; import java.util.List; -import org.lwjgl.opengl.GL46C; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.struct.StorageBufferWriter; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.backend.gl.GlNumericType; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.Materials; @@ -24,7 +21,7 @@ import com.jozufozu.flywheel.core.vertex.Formats; public class IndirectList { - private static final int DRAW_COMMAND_STRIDE = 20; + private static final long DRAW_COMMAND_STRIDE = 20; final StorageBufferWriter storageBufferWriter; final GlProgram compute; @@ -51,12 +48,13 @@ public class IndirectList { * Stores drawIndirect structs. */ int drawBuffer; + int debugBuffer; final IndirectMeshPool meshPool; int vertexArray; - final int[] shaderStorageBuffers = new int[4]; + final int[] shaderStorageBuffers = new int[5]; final List> batches = new ArrayList<>(); @@ -73,10 +71,11 @@ public class IndirectList { targetBuffer = shaderStorageBuffers[1]; boundingSphereBuffer = shaderStorageBuffers[2]; drawBuffer = shaderStorageBuffers[3]; + debugBuffer = shaderStorageBuffers[4]; meshPool = new IndirectMeshPool(Formats.BLOCK, 1024); // FIXME: Resizable buffers - maxObjectCount = 1024L * 100L; + maxObjectCount = 1024L; maxBatchCount = 64; // +4 for the batch id @@ -85,6 +84,7 @@ public class IndirectList { glNamedBufferStorage(targetBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); glNamedBufferStorage(boundingSphereBuffer, 16 * maxBatchCount, GL_DYNAMIC_STORAGE_BIT); glNamedBufferStorage(drawBuffer, DRAW_COMMAND_STRIDE * maxBatchCount, GL_DYNAMIC_STORAGE_BIT); + glNamedBufferStorage(debugBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); objectClientStorage = MemoryUtil.nmemAlloc(objectStride * maxObjectCount); @@ -115,17 +115,13 @@ public class IndirectList { offset += attribute .getByteWidth(); } - - glEnableVertexArrayAttrib(vertexArray, meshAttribs); - glVertexArrayVertexBuffer(vertexArray, meshAttribs, targetBuffer, 0, 4); - glVertexArrayAttribIFormat(vertexArray, meshAttribs, 1, GlNumericType.UINT.getGlEnum(), 0); } public void add(Mesh mesh, IndirectInstancer instancer) { batches.add(new Batch<>(instancer, meshPool.alloc(mesh))); } - void submit() { + void submit(FrustumUBO frustumUBO) { int instanceCountThisFrame = calculateTotalInstanceCount(); if (instanceCountThisFrame == 0) { @@ -137,14 +133,15 @@ public class IndirectList { uploadBoundingSpheres(); uploadIndirectCommands(); - UniformBuffer.getInstance().sync(); - + frustumUBO.bind(); dispatchCompute(instanceCountThisFrame); issueMemoryBarrier(); dispatchDraw(); } private void dispatchDraw() { + UniformBuffer.getInstance().sync(); + draw.bind(); Materials.BELL.setup(); glVertexArrayElementBuffer(vertexArray, elementBuffer); @@ -170,13 +167,17 @@ public class IndirectList { ptr += 16; } - GL46C.nglNamedBufferSubData(boundingSphereBuffer, 0, size, basePtr); + nglNamedBufferSubData(boundingSphereBuffer, 0, size, basePtr); } } private void dispatchCompute(int instanceCount) { compute.bind(); - glBindBuffersBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffers); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, objectBuffer, 0, instanceCount * objectStride); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, targetBuffer, 0, instanceCount * 4L); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, boundingSphereBuffer, 0, batches.size() * 16L); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, drawBuffer, 0, batches.size() * DRAW_COMMAND_STRIDE); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 4, debugBuffer, 0, instanceCount * 4L); var groupCount = (instanceCount + 31) >> 5; // ceil(totalInstanceCount / 32) glDispatchCompute(groupCount, 1, 1); @@ -197,20 +198,20 @@ public class IndirectList { batchID++; } - GL46C.nglNamedBufferSubData(objectBuffer, 0, ptr - objectClientStorage, objectClientStorage); + nglNamedBufferSubData(objectBuffer, 0, ptr - objectClientStorage, objectClientStorage); } private void uploadIndirectCommands() { try (var stack = MemoryStack.stackPush()) { - var size = batches.size() * DRAW_COMMAND_STRIDE; - long basePtr = stack.nmalloc(size); + long size = batches.size() * DRAW_COMMAND_STRIDE; + long basePtr = stack.nmalloc((int) size); long writePtr = basePtr; for (Batch batch : batches) { batch.writeIndirectCommand(writePtr); writePtr += DRAW_COMMAND_STRIDE; } - GL46C.nglNamedBufferSubData(drawBuffer, 0, size, basePtr); + nglNamedBufferSubData(drawBuffer, 0, size, basePtr); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java index ee1743507..38ecfc8f6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.backend.instancing.indirect; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; diff --git a/src/main/java/com/jozufozu/flywheel/core/RenderContext.java b/src/main/java/com/jozufozu/flywheel/core/RenderContext.java index 121e2d93a..0f4db1f6f 100644 --- a/src/main/java/com/jozufozu/flywheel/core/RenderContext.java +++ b/src/main/java/com/jozufozu/flywheel/core/RenderContext.java @@ -23,13 +23,10 @@ public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack return viewProjection; } - public static FrustumIntersection createCuller(Camera camera, Matrix4f viewProjection) { + public static FrustumIntersection createCuller(Matrix4f viewProjection, float camX, float camY, float camZ) { com.jozufozu.flywheel.util.joml.Matrix4f proj = Matrix4fExtension.clone(viewProjection); - Vec3 cam = camera - .getPosition(); - - proj.translate((float) -cam.x, (float) -cam.y, (float) -cam.z); + proj.translate(camX, camY, camZ); return new FrustumIntersection(proj); } diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java index 43c8a0f82..07c40e7e5 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java @@ -23,23 +23,23 @@ public class OrientedStorageWriter implements StorageBufferWriter MemoryUtil.memPutFloat(ptr + 20, d.posY); MemoryUtil.memPutFloat(ptr + 24, d.posZ); - MemoryUtil.memPutFloat(ptr + 28, d.pivotX); - MemoryUtil.memPutFloat(ptr + 32, d.pivotY); - MemoryUtil.memPutFloat(ptr + 36, d.pivotZ); + MemoryUtil.memPutFloat(ptr + 32, d.pivotX); + MemoryUtil.memPutFloat(ptr + 36, d.pivotY); + MemoryUtil.memPutFloat(ptr + 40, d.pivotZ); - MemoryUtil.memPutShort(ptr + 40, d.blockLight); - MemoryUtil.memPutShort(ptr + 42, d.skyLight); + MemoryUtil.memPutShort(ptr + 44, d.blockLight); + MemoryUtil.memPutShort(ptr + 46, d.skyLight); - MemoryUtil.memPutByte(ptr + 44, d.r); - MemoryUtil.memPutByte(ptr + 45, d.g); - MemoryUtil.memPutByte(ptr + 46, d.b); - MemoryUtil.memPutByte(ptr + 47, d.a); + MemoryUtil.memPutByte(ptr + 48, d.r); + MemoryUtil.memPutByte(ptr + 49, d.g); + MemoryUtil.memPutByte(ptr + 50, d.b); + MemoryUtil.memPutByte(ptr + 51, d.a); - MemoryUtil.memPutInt(ptr + 48, batchID); + MemoryUtil.memPutInt(ptr + 52, batchID); } @Override public int getAlignment() { - return 52; + return 64; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/FogProvider.java b/src/main/java/com/jozufozu/flywheel/core/uniform/FogProvider.java index 9a7586c41..76579490e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/FogProvider.java +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/FogProvider.java @@ -12,7 +12,7 @@ public class FogProvider extends UniformProvider { @Override - public int getSize() { + public int getActualByteSize() { return 16 + 8 + 4; } diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/UniformBuffer.java b/src/main/java/com/jozufozu/flywheel/core/uniform/UniformBuffer.java index 5f3b10bb7..e20db4a78 100644 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/UniformBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/UniformBuffer.java @@ -47,11 +47,11 @@ public class UniformBuffer { int totalBytes = 0; int index = 0; for (UniformProvider provider : providers) { - int size = provider.getSize(); + int size = alignPo2(provider.getActualByteSize(), 16); builder.add(new Allocated(provider, totalBytes, size, index)); - totalBytes = align(totalBytes + size); + totalBytes = alignUniformBuffer(totalBytes + size); index++; } @@ -82,7 +82,7 @@ public class UniformBuffer { } // https://stackoverflow.com/questions/3407012/rounding-up-to-the-nearest-multiple-of-a-number - private static int align(int numToRound) { + private static int alignUniformBuffer(int numToRound) { if (PO2_ALIGNMENT) { return (numToRound + OFFSET_ALIGNMENT - 1) & -OFFSET_ALIGNMENT; } else { @@ -90,6 +90,10 @@ public class UniformBuffer { } } + private static int alignPo2(int numToRound, int alignment) { + return (numToRound + alignment - 1) & -alignment; + } + private class Allocated implements UniformProvider.Notifier { private final UniformProvider provider; private final int offset; diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java b/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java index 03e5c208e..65ffb4077 100644 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java @@ -26,7 +26,7 @@ public class ViewProvider extends UniformProvider { } @Override - public int getSize() { + public int getActualByteSize() { return 4 * 16 + 16 + 4; } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java index e14d9936c..9fac4b319 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java @@ -27,6 +27,7 @@ import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.RenderBuffers; +import net.minecraft.world.phys.Vec3; import net.minecraftforge.common.MinecraftForge; @Mixin(value = LevelRenderer.class, priority = 1001) // Higher priority to go after sodium @@ -44,7 +45,8 @@ public class LevelRendererMixin { @Inject(at = @At("HEAD"), method = "renderLevel") private void beginRender(PoseStack pPoseStack, float pPartialTick, long pFinishNanoTime, boolean pRenderBlockOutline, Camera pCamera, GameRenderer pGameRenderer, LightTexture pLightTexture, Matrix4f pProjectionMatrix, CallbackInfo ci) { var viewProjection = RenderContext.createViewProjection(pPoseStack, pProjectionMatrix); - var culler = RenderContext.createCuller(pCamera, viewProjection); + var cameraPos = pCamera.getPosition(); + var culler = RenderContext.createCuller(viewProjection, (float) -cameraPos.x, (float) -cameraPos.y, (float) -cameraPos.z); renderContext = new RenderContext((LevelRenderer) (Object) this, level, pPoseStack, viewProjection, pProjectionMatrix, renderBuffers, pCamera, culler); try (var restoreState = GlStateTracker.getRestoreState()) { diff --git a/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java b/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java index b35664244..4e4881aaf 100644 --- a/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java +++ b/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java @@ -1004,11 +1004,9 @@ public class FrustumIntersection { * {@code vec2(nzZ, pzZ)}
* {@code vec2(nzW, pzW)}
* - * @param buffer The buffer to write the planes to. + * @param addr The buffer to write the planes to. */ - public void getJozuPackedPlanes(ByteBuffer buffer) { - long addr = MemoryUtil.memAddress(buffer); - + public void getJozuPackedPlanes(long addr) { MemoryUtil.memPutFloat(addr, nxX); MemoryUtil.memPutFloat(addr + 4, pxX); MemoryUtil.memPutFloat(addr + 8, nyX); diff --git a/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl b/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl index ef4577120..361ca6810 100644 --- a/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl +++ b/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl @@ -3,7 +3,10 @@ layout(local_size_x = FLW_SUBGROUP_SIZE) in; #use "flywheel:compute/objects.glsl" #use "flywheel:util/quaternion.glsl" -layout(std140, binding = 3) uniform FrameData { +uint flw_objectID; +uint flw_batchID; + +layout(std140, binding = 0) uniform FrameData { vec4 a1; // vec4(nx.x, px.x, ny.x, py.x) vec4 a2; // vec4(nx.y, px.y, ny.y, py.y) vec4 a3; // vec4(nx.z, px.z, ny.z, py.z) @@ -31,37 +34,55 @@ layout(std430, binding = 3) buffer DrawCommands { MeshDrawCommand drawCommands[]; }; +layout(std430, binding = 4) writeonly buffer DebugVisibility { + uint objectVisibilityBits[]; +}; + // 83 - 27 = 56 spirv instruction results bool testSphere(vec3 center, float radius) { - return - all(lessThanEqual(fma(frustum.a1, center.xxxx, fma(frustum.a2, center.yyyy, fma(frustum.a3, center.zzzz, frustum.a4))), -radius.xxxx)) && - all(lessThanEqual(fma(frustum.b1, center.xx, fma(frustum.b2, center.yy, fma(frustum.b3, center.zz, frustum.b4))), -radius.xx)); + bvec4 resultA = greaterThanEqual(fma(frustum.a1, center.xxxx, fma(frustum.a2, center.yyyy, fma(frustum.a3, center.zzzz, frustum.a4))), -radius.xxxx); + bvec2 resultB = greaterThanEqual(fma(frustum.b1, center.xx, fma(frustum.b2, center.yy, fma(frustum.b3, center.zz, frustum.b4))), -radius.xx); + + uint debug = uint(resultA.x); + debug |= uint(resultA.y) << 1; + debug |= uint(resultA.z) << 2; + debug |= uint(resultA.w) << 3; + debug |= uint(resultB.x) << 4; + debug |= uint(resultB.y) << 5; + + objectVisibilityBits[flw_objectID] = debug; + + return all(resultA) && all(resultB); } -bool isVisible(uint objectID, uint batchID) { - vec4 sphere = boundingSpheres[batchID]; +void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) { + center = rotateVertexByQuat(center - i.pivot, i.rotation) + i.pivot + i.pos; + radius = radius; +} - vec3 pivot = objects[objectID].pivot; - vec3 center = rotateVertexByQuat(sphere.xyz - pivot, objects[objectID].rotation) + pivot + objects[objectID].pos; +bool isVisible() { + vec4 sphere = boundingSpheres[flw_batchID]; + + vec3 center = sphere.xyz; float radius = sphere.r; + flw_transformBoundingSphere(objects[flw_objectID], center, radius); - return true; //testSphere(center, radius); + return testSphere(center, radius); } void main() { - uint objectID = gl_GlobalInvocationID.x; + flw_objectID = gl_GlobalInvocationID.x; - if (objectID >= objects.length()) { + if (flw_objectID >= objects.length()) { return; } - uint batchID = objects[objectID].batchID; - bool visible = isVisible(objectID, batchID); + flw_batchID = objects[objectID].batchID; - if (visible) { - uint batchIndex = atomicAdd(drawCommands[batchID].instanceCount, 1); - uint globalIndex = drawCommands[batchID].baseInstance + batchIndex; + if (isVisible()) { + uint batchIndex = atomicAdd(drawCommands[flw_batchID].instanceCount, 1); + uint globalIndex = drawCommands[flw_batchID].baseInstance + batchIndex; - objectIDs[globalIndex] = objectID; + objectIDs[globalIndex] = flw_objectID; } } From a9ee85ff76cde7e4f2004e4bd9dcb8bdbf84dde6 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 6 Aug 2022 17:17:46 -0700 Subject: [PATCH 05/13] Separate shader logic and fix rendering errors - Batch IDs are stored in a separate buffer - Bounding spheres in draw command buffer, subject to change - Guard shader api files - Generate complete header for compute culler compiler - Move Frustum UBO to uniform shader/provider --- .../api/struct/StorageBufferWriter.java | 2 +- .../com/jozufozu/flywheel/backend/Loader.java | 3 +- .../indirect/ComputeCullerCompiler.java | 3 +- .../instancing/indirect/FrustumUBO.java | 31 --------- .../instancing/indirect/IndirectEngine.java | 20 +----- .../instancing/indirect/IndirectList.java | 66 ++++++++---------- .../instancing/indirect/IndirectMeshPool.java | 11 ++- .../instancing/InstancedArraysCompiler.java | 2 + .../jozufozu/flywheel/core/Components.java | 3 + .../flywheel/core/compile/CompileUtil.java | 4 +- .../flywheel/core/source/FileResolution.java | 2 +- .../core/source/error/ErrorBuilder.java | 8 ++- .../core/source/error/ErrorReporter.java | 12 ++-- .../oriented/OrientedStorageWriter.java | 8 +-- .../core/uniform/FrustumProvider.java | 60 ++++++++++++++++ .../assets/flywheel/flywheel/api/cull.glsl | 4 ++ .../flywheel/flywheel/api/fragment.glsl | 2 + .../assets/flywheel/flywheel/api/vertex.glsl | 2 + .../flywheel/compute/cull_instances.glsl | 69 +++++++++---------- .../flywheel/compute/draw_instances.vert | 15 ++-- .../flywheel/flywheel/compute/objects.glsl | 17 ----- .../flywheel/instance/oriented_indirect.glsl | 24 +++++++ .../instance/transformed_indirect.glsl | 22 ++++++ .../flywheel/flywheel/uniform/frustum.glsl | 14 ++++ 24 files changed, 230 insertions(+), 174 deletions(-) delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/FrustumUBO.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java create mode 100644 src/main/resources/assets/flywheel/flywheel/api/cull.glsl delete mode 100644 src/main/resources/assets/flywheel/flywheel/compute/objects.glsl create mode 100644 src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl create mode 100644 src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl create mode 100644 src/main/resources/assets/flywheel/flywheel/uniform/frustum.glsl diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/StorageBufferWriter.java b/src/main/java/com/jozufozu/flywheel/api/struct/StorageBufferWriter.java index de7c9dfc1..02eb42db9 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/StorageBufferWriter.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/StorageBufferWriter.java @@ -4,7 +4,7 @@ import com.jozufozu.flywheel.api.instancer.InstancedPart; public interface StorageBufferWriter { - void write(final long ptr, final T instance, final int batchID); + void write(final long ptr, final T instance); int getAlignment(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index 7f923e5a3..25893bac4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -52,8 +52,7 @@ public class Loader implements ResourceManagerReloadListener { FileResolution.run(errorReporter, sources); if (errorReporter.hasErrored()) { - errorReporter.dump(); - throw new ShaderLoadingException("Failed to resolve all source files, see log for details"); + throw errorReporter.dump(); } sources.postResolve(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java index a104faf26..151fbcb03 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java @@ -5,6 +5,7 @@ import com.jozufozu.flywheel.backend.gl.GLSLVersion; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.compile.CompileUtil; import com.jozufozu.flywheel.core.compile.Memoizer; import com.jozufozu.flywheel.core.compile.ProgramAssembler; import com.jozufozu.flywheel.core.compile.ShaderCompilationException; @@ -24,7 +25,7 @@ public class ComputeCullerCompiler extends Memoizer { CompilationContext context = new CompilationContext(); - var header = GLSLVersion.V460.getVersionLine(); + var header = CompileUtil.generateHeader(GLSLVersion.V460, ShaderType.COMPUTE); String source = file.getFile() .generateFinalSource(context); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/FrustumUBO.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/FrustumUBO.java deleted file mode 100644 index 554d78dea..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/FrustumUBO.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.indirect; - -import static org.lwjgl.opengl.GL46.*; - -import org.lwjgl.system.MemoryUtil; - -import com.jozufozu.flywheel.util.joml.FrustumIntersection; - -// This should be a push constant :whywheel: -public class FrustumUBO { - - public static final int BUFFER_SIZE = 96; - - private final int ubo; - private final long clientStorage; - - FrustumUBO() { - ubo = glCreateBuffers(); - glNamedBufferStorage(ubo, BUFFER_SIZE, GL_DYNAMIC_STORAGE_BIT); - clientStorage = MemoryUtil.nmemAlloc(BUFFER_SIZE); - } - - public void update(FrustumIntersection frustum) { - frustum.getJozuPackedPlanes(clientStorage); - nglNamedBufferSubData(ubo, 0, BUFFER_SIZE, clientStorage); - } - - public void bind() { - glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java index ddc66f0a1..a422428cc 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -45,8 +45,6 @@ public class IndirectEngine implements Engine { protected final List> uninitializedModels = new ArrayList<>(); protected final RenderLists renderLists = new RenderLists(); - private FrustumUBO frustumUBO; - /** * The set of instance managers that are attached to this engine. */ @@ -77,7 +75,7 @@ public class IndirectEngine implements Engine { setup(); for (var group : groups) { - group.submit(frustumUBO); + group.submit(); } } @@ -146,26 +144,10 @@ public class IndirectEngine implements Engine { @Override public void beginFrame(TaskEngine taskEngine, RenderContext context) { - if (frustumUBO == null) { - frustumUBO = new FrustumUBO(); - } - for (var model : uninitializedModels) { model.init(renderLists); } uninitializedModels.clear(); - - Vec3 camera = context.camera() - .getPosition(); - - var camX = (float) (camera.x - originCoordinate.getX()); - var camY = (float) (camera.y - originCoordinate.getY()); - var camZ = (float) (camera.z - originCoordinate.getZ()); - - var culler = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ); - - frustumUBO.update(culler); - } private void shiftListeners(int cX, int cY, int cZ) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java index 5e6642b7e..fb5874298 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java @@ -21,7 +21,8 @@ import com.jozufozu.flywheel.core.vertex.Formats; public class IndirectList { - private static final long DRAW_COMMAND_STRIDE = 20; + private static final long DRAW_COMMAND_STRIDE = 48; + private static final long DRAW_COMMAND_OFFSET = 0; final StorageBufferWriter storageBufferWriter; final GlProgram compute; @@ -31,18 +32,15 @@ public class IndirectList { private final long objectStride; private final int maxBatchCount; private final long objectClientStorage; + private final long batchIDClientStorage; private final int elementBuffer; /** * Stores raw instance data per-object. */ int objectBuffer; - int targetBuffer; - /** - * Stores bounding spheres - */ - int boundingSphereBuffer; + int batchBuffer; /** * Stores drawIndirect structs. @@ -69,7 +67,7 @@ public class IndirectList { glCreateBuffers(shaderStorageBuffers); objectBuffer = shaderStorageBuffers[0]; targetBuffer = shaderStorageBuffers[1]; - boundingSphereBuffer = shaderStorageBuffers[2]; + batchBuffer = shaderStorageBuffers[2]; drawBuffer = shaderStorageBuffers[3]; debugBuffer = shaderStorageBuffers[4]; meshPool = new IndirectMeshPool(Formats.BLOCK, 1024); @@ -78,15 +76,15 @@ public class IndirectList { maxObjectCount = 1024L; maxBatchCount = 64; - // +4 for the batch id objectStride = storageBufferWriter.getAlignment(); glNamedBufferStorage(objectBuffer, objectStride * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); glNamedBufferStorage(targetBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); - glNamedBufferStorage(boundingSphereBuffer, 16 * maxBatchCount, GL_DYNAMIC_STORAGE_BIT); + glNamedBufferStorage(batchBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); glNamedBufferStorage(drawBuffer, DRAW_COMMAND_STRIDE * maxBatchCount, GL_DYNAMIC_STORAGE_BIT); glNamedBufferStorage(debugBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); objectClientStorage = MemoryUtil.nmemAlloc(objectStride * maxObjectCount); + batchIDClientStorage = MemoryUtil.nmemAlloc(4 * maxObjectCount); vertexArray = glCreateVertexArrays(); @@ -121,7 +119,7 @@ public class IndirectList { batches.add(new Batch<>(instancer, meshPool.alloc(mesh))); } - void submit(FrustumUBO frustumUBO) { + void submit() { int instanceCountThisFrame = calculateTotalInstanceCount(); if (instanceCountThisFrame == 0) { @@ -130,24 +128,22 @@ public class IndirectList { meshPool.uploadAll(); uploadInstanceData(); - uploadBoundingSpheres(); uploadIndirectCommands(); - frustumUBO.bind(); + UniformBuffer.getInstance().sync(); + dispatchCompute(instanceCountThisFrame); issueMemoryBarrier(); dispatchDraw(); } private void dispatchDraw() { - UniformBuffer.getInstance().sync(); - draw.bind(); Materials.BELL.setup(); glVertexArrayElementBuffer(vertexArray, elementBuffer); glBindVertexArray(vertexArray); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, drawBuffer); - glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, 0, batches.size(), 0); + glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, DRAW_COMMAND_OFFSET, batches.size(), (int) DRAW_COMMAND_STRIDE); Materials.BELL.clear(); } @@ -155,27 +151,11 @@ public class IndirectList { glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); } - private void uploadBoundingSpheres() { - try (var stack = MemoryStack.stackPush()) { - final int size = batches.size() * 16; - final long basePtr = stack.nmalloc(size); - - long ptr = basePtr; - for (Batch batch : batches) { - var boundingSphere = batch.mesh.mesh.getBoundingSphere(); - boundingSphere.getToAddress(ptr); - ptr += 16; - } - - nglNamedBufferSubData(boundingSphereBuffer, 0, size, basePtr); - } - } - private void dispatchCompute(int instanceCount) { compute.bind(); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, objectBuffer, 0, instanceCount * objectStride); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, targetBuffer, 0, instanceCount * 4L); - glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, boundingSphereBuffer, 0, batches.size() * 16L); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, batchBuffer, 0, instanceCount * 4L); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, drawBuffer, 0, batches.size() * DRAW_COMMAND_STRIDE); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 4, debugBuffer, 0, instanceCount * 4L); @@ -184,22 +164,28 @@ public class IndirectList { } private void uploadInstanceData() { - long ptr = objectClientStorage; + long objectPtr = objectClientStorage; + long batchIDPtr = batchIDClientStorage; int baseInstance = 0; int batchID = 0; for (var batch : batches) { batch.baseInstance = baseInstance; var instancer = batch.instancer; for (T t : instancer.getAll()) { - storageBufferWriter.write(ptr, t, batchID); - ptr += objectStride; + // write object + storageBufferWriter.write(objectPtr, t); + objectPtr += objectStride; + + // write batchID + MemoryUtil.memPutInt(batchIDPtr, batchID); + batchIDPtr += 4; } baseInstance += batch.instancer.instanceCount; batchID++; } - nglNamedBufferSubData(objectBuffer, 0, ptr - objectClientStorage, objectClientStorage); - + nglNamedBufferSubData(objectBuffer, 0, objectPtr - objectClientStorage, objectClientStorage); + nglNamedBufferSubData(batchBuffer, 0, batchIDPtr - batchIDClientStorage, batchIDClientStorage); } private void uploadIndirectCommands() { @@ -235,11 +221,15 @@ public class IndirectList { } public void writeIndirectCommand(long ptr) { - MemoryUtil.memPutInt(ptr, mesh.getVertexCount()); // count + var boundingSphere = mesh.mesh.getBoundingSphere(); + + MemoryUtil.memPutInt(ptr, mesh.getIndexCount()); // count MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be incremented by the compute shader MemoryUtil.memPutInt(ptr + 8, 0); // firstIndex - all models share the same index buffer MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance + + boundingSphere.getToAddress(ptr + 32); // boundingSphere } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java index ade54f176..b88fb380e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java @@ -89,11 +89,14 @@ public class IndirectMeshPool { public class BufferedMesh { public final Mesh mesh; + private final int vertexCount; private long byteIndex; private int baseVertex; public BufferedMesh(Mesh mesh) { this.mesh = mesh; + + vertexCount = mesh.getVertexCount(); } private void buffer(ByteBuffer buffer) { @@ -103,7 +106,7 @@ public class IndirectMeshPool { } public int getByteSize() { - return IndirectMeshPool.this.vertexType.getLayout().getStride() * this.mesh.getVertexCount(); + return IndirectMeshPool.this.vertexType.getLayout().getStride() * this.vertexCount; } public int getBaseVertex() { @@ -111,7 +114,11 @@ public class IndirectMeshPool { } public int getVertexCount() { - return this.mesh.getVertexCount(); + return this.vertexCount; + } + + public int getIndexCount() { + return this.vertexCount * 6 / 4; } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysCompiler.java index e6848c084..4a4f92779 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysCompiler.java @@ -112,6 +112,7 @@ public class InstancedArraysCompiler extends Memoizer maxLength) maxLength = length; + if (length > maxLength) { + maxLength = length; + } } StringBuilder builder = new StringBuilder(); diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java index 859ec71e2..90224f0e2 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java @@ -3,9 +3,11 @@ package com.jozufozu.flywheel.core.source.error; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.core.source.ShaderLoadingException; import com.jozufozu.flywheel.core.source.SourceFile; import com.jozufozu.flywheel.core.source.parse.ShaderFunction; import com.jozufozu.flywheel.core.source.parse.ShaderStruct; @@ -82,10 +84,12 @@ public class ErrorReporter { return !reportedErrors.isEmpty(); } - public void dump() { - for (var error : reportedErrors) { - Backend.LOGGER.error(error.build()); - } + public ShaderLoadingException dump() { + var allErrors = reportedErrors.stream() + .map(ErrorBuilder::build) + .collect(Collectors.joining()); + + return new ShaderLoadingException(allErrors); } public static void printLines(CharSequence source) { diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java index 07c40e7e5..ffea61b53 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java @@ -13,7 +13,7 @@ public class OrientedStorageWriter implements StorageBufferWriter } @Override - public void write(final long ptr, OrientedPart d, final int batchID) { + public void write(final long ptr, OrientedPart d) { MemoryUtil.memPutFloat(ptr, d.qX); MemoryUtil.memPutFloat(ptr + 4, d.qY); MemoryUtil.memPutFloat(ptr + 8, d.qZ); @@ -27,15 +27,13 @@ public class OrientedStorageWriter implements StorageBufferWriter MemoryUtil.memPutFloat(ptr + 36, d.pivotY); MemoryUtil.memPutFloat(ptr + 40, d.pivotZ); - MemoryUtil.memPutShort(ptr + 44, d.blockLight); - MemoryUtil.memPutShort(ptr + 46, d.skyLight); + MemoryUtil.memPutShort(ptr + 44, d.skyLight); + MemoryUtil.memPutShort(ptr + 46, d.blockLight); MemoryUtil.memPutByte(ptr + 48, d.r); MemoryUtil.memPutByte(ptr + 49, d.g); MemoryUtil.memPutByte(ptr + 50, d.b); MemoryUtil.memPutByte(ptr + 51, d.a); - - MemoryUtil.memPutInt(ptr + 52, batchID); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java b/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java new file mode 100644 index 000000000..9070a7eae --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java @@ -0,0 +1,60 @@ +package com.jozufozu.flywheel.core.uniform; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.uniform.UniformProvider; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; +import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.RenderContext; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.event.BeginFrameEvent; +import com.jozufozu.flywheel.util.extension.MatrixExtension; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.core.Vec3i; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.MinecraftForge; + +public class FrustumProvider extends UniformProvider { + + public FrustumProvider() { + MinecraftForge.EVENT_BUS.addListener(this::beginFrame); + } + + @Override + public int getActualByteSize() { + return 96; + } + + @Override + public FileResolution getUniformShader() { + return Components.Files.FRUSTUM_UNIFORMS; + } + + public void beginFrame(BeginFrameEvent event) { + update(event.getContext()); + } + + public void update(RenderContext context) { + if (buffer == null) { + return; + } + + Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(context.level()); + Vec3 camera = context.camera() + .getPosition(); + + var camX = (float) (camera.x - originCoordinate.getX()); + var camY = (float) (camera.y - originCoordinate.getY()); + var camZ = (float) (camera.z - originCoordinate.getZ()); + + var shiftedCuller = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ); + + long ptr = MemoryUtil.memAddress(buffer); + + shiftedCuller.getJozuPackedPlanes(ptr); + + notifier.signalChanged(); + } +} diff --git a/src/main/resources/assets/flywheel/flywheel/api/cull.glsl b/src/main/resources/assets/flywheel/flywheel/api/cull.glsl new file mode 100644 index 000000000..d89bebbf3 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/api/cull.glsl @@ -0,0 +1,4 @@ +#ifdef COMPUTE_SHADER +uint flw_objectID; +uint flw_batchID; +#endif diff --git a/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl b/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl index 735f7a84e..11be92a1e 100644 --- a/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl +++ b/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl @@ -1,3 +1,4 @@ +#ifdef FRAGMENT_SHADER in vec4 flw_vertexPos; in vec4 flw_vertexColor; in vec2 flw_vertexTexCoord; @@ -32,3 +33,4 @@ vec4 flw_fogFilter(vec4 color); * Guard calls with FLW_DISCARD */ bool flw_discardPredicate(vec4 finalColor); +#endif diff --git a/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl b/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl index c5d670ec3..c00b99cbd 100644 --- a/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl +++ b/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl @@ -1,3 +1,4 @@ +#ifdef VERTEX_SHADER out vec4 flw_vertexPos; out vec4 flw_vertexColor; out vec2 flw_vertexTexCoord; @@ -11,3 +12,4 @@ out vec4 flw_var0; out vec4 flw_var1; out vec4 flw_var2; out vec4 flw_var3; +#endif diff --git a/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl b/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl index 361ca6810..705b379c2 100644 --- a/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl +++ b/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl @@ -1,67 +1,60 @@ #define FLW_SUBGROUP_SIZE 32 layout(local_size_x = FLW_SUBGROUP_SIZE) in; -#use "flywheel:compute/objects.glsl" +#use "flywheel:api/cull.glsl" #use "flywheel:util/quaternion.glsl" +#use "flywheel:uniform/frustum.glsl" +#use "flywheel:instance/oriented_indirect.glsl" -uint flw_objectID; -uint flw_batchID; +struct MeshDrawCommand { + uint indexCount; + uint instanceCount; + uint firstIndex; + uint vertexOffset; + uint baseInstance; -layout(std140, binding = 0) uniform FrameData { - vec4 a1; // vec4(nx.x, px.x, ny.x, py.x) - vec4 a2; // vec4(nx.y, px.y, ny.y, py.y) - vec4 a3; // vec4(nx.z, px.z, ny.z, py.z) - vec4 a4; // vec4(nx.w, px.w, ny.w, py.w) - vec2 b1; // vec2(nz.x, pz.x) - vec2 b2; // vec2(nz.y, pz.y) - vec2 b3; // vec2(nz.z, pz.z) - vec2 b4; // vec2(nz.w, pz.w) -} frustum; - -// populated by instancers -layout(std430, binding = 0) readonly buffer ObjectBuffer { - Instance objects[]; + vec4 boundingSphere; }; -layout(std430, binding = 1) writeonly buffer TargetBuffer { +// populated by instancers +layout(std430, binding = 0) restrict readonly buffer ObjectBuffer { + FLW_INSTANCE_STRUCT objects[]; +}; + +layout(std430, binding = 1) restrict writeonly buffer TargetBuffer { uint objectIDs[]; }; -layout(std430, binding = 2) readonly buffer BoundingSpheres { - vec4 boundingSpheres[]; +layout(std430, binding = 2) restrict readonly buffer BatchBuffer { + uint batchIDs[]; }; -layout(std430, binding = 3) buffer DrawCommands { +layout(std430, binding = 3) restrict buffer DrawCommands { MeshDrawCommand drawCommands[]; }; -layout(std430, binding = 4) writeonly buffer DebugVisibility { +layout(std430, binding = 4) restrict writeonly buffer DebugVisibility { uint objectVisibilityBits[]; }; // 83 - 27 = 56 spirv instruction results bool testSphere(vec3 center, float radius) { - bvec4 resultA = greaterThanEqual(fma(frustum.a1, center.xxxx, fma(frustum.a2, center.yyyy, fma(frustum.a3, center.zzzz, frustum.a4))), -radius.xxxx); - bvec2 resultB = greaterThanEqual(fma(frustum.b1, center.xx, fma(frustum.b2, center.yy, fma(frustum.b3, center.zz, frustum.b4))), -radius.xx); + bvec4 xyInside = greaterThanEqual(fma(flw_planes.xyX, center.xxxx, fma(flw_planes.xyY, center.yyyy, fma(flw_planes.xyZ, center.zzzz, flw_planes.xyW))), -radius.xxxx); + bvec2 zInside = greaterThanEqual(fma(flw_planes.zX, center.xx, fma(flw_planes.zY, center.yy, fma(flw_planes.zZ, center.zz, flw_planes.zW))), -radius.xx); - uint debug = uint(resultA.x); - debug |= uint(resultA.y) << 1; - debug |= uint(resultA.z) << 2; - debug |= uint(resultA.w) << 3; - debug |= uint(resultB.x) << 4; - debug |= uint(resultB.y) << 5; + uint debug = uint(xyInside.x); + debug |= uint(xyInside.y) << 1; + debug |= uint(xyInside.z) << 2; + debug |= uint(xyInside.w) << 3; + debug |= uint(zInside.x) << 4; + debug |= uint(zInside.y) << 5; objectVisibilityBits[flw_objectID] = debug; - return all(resultA) && all(resultB); -} - -void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) { - center = rotateVertexByQuat(center - i.pivot, i.rotation) + i.pivot + i.pos; - radius = radius; + return all(xyInside) && all(zInside); } bool isVisible() { - vec4 sphere = boundingSpheres[flw_batchID]; + vec4 sphere = drawCommands[flw_batchID].boundingSphere; vec3 center = sphere.xyz; float radius = sphere.r; @@ -77,7 +70,7 @@ void main() { return; } - flw_batchID = objects[objectID].batchID; + flw_batchID = batchIDs[flw_objectID]; if (isVisible()) { uint batchIndex = atomicAdd(drawCommands[flw_batchID].instanceCount, 1); diff --git a/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert b/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert index 030d82200..66fea9dae 100644 --- a/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert +++ b/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert @@ -1,25 +1,18 @@ #use "flywheel:api/vertex.glsl" -#use "flywheel:compute/objects.glsl" #use "flywheel:layout/block.vert" #use "flywheel:context/world.vert" #use "flywheel:util/quaternion.glsl" +#use "flywheel:instance/oriented_indirect.glsl" // populated by instancers -layout(std430, binding = 0) readonly buffer ObjectBuffer { - Instance objects[]; +layout(std430, binding = 0) restrict readonly buffer ObjectBuffer { + FLW_INSTANCE_STRUCT objects[]; }; -layout(std430, binding = 1) readonly buffer TargetBuffer { +layout(std430, binding = 1) restrict readonly buffer TargetBuffer { uint objectIDs[]; }; -void flw_instanceVertex(Instance i) { - flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - i.pivot, i.rotation) + i.pivot + i.pos, 1.0); - flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, i.rotation); - flw_vertexColor = unpackUnorm4x8(i.color); - flw_vertexLight = unpackUnorm2x16(i.light) / 15.0; -} - void main() { uint instanceIndex = objectIDs[gl_BaseInstance + gl_InstanceID]; flw_layoutVertex(); diff --git a/src/main/resources/assets/flywheel/flywheel/compute/objects.glsl b/src/main/resources/assets/flywheel/flywheel/compute/objects.glsl deleted file mode 100644 index 635ae39db..000000000 --- a/src/main/resources/assets/flywheel/flywheel/compute/objects.glsl +++ /dev/null @@ -1,17 +0,0 @@ - -struct Instance { - vec4 rotation; - vec3 pos; - vec3 pivot; - uint light; - uint color; - uint batchID; -}; - -struct MeshDrawCommand { - uint indexCount; - uint instanceCount; - uint firstIndex; - uint vertexOffset; - uint baseInstance; -}; diff --git a/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl b/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl new file mode 100644 index 000000000..c02c57bfc --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl @@ -0,0 +1,24 @@ +#use "flywheel:api/vertex.glsl" +#use "flywheel:util/quaternion.glsl" +#define FLW_INSTANCE_STRUCT Instance + +struct Instance { + vec4 rotation; + vec3 pos; + vec3 pivot; + uint light; + uint color; +}; + +void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) { + center = rotateVertexByQuat(center - i.pivot, i.rotation) + i.pivot + i.pos; +} + +#ifdef VERTEX_SHADER +void flw_instanceVertex(Instance i) { + flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - i.pivot, i.rotation) + i.pivot + i.pos, 1.0); + flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, i.rotation); + flw_vertexColor = unpackUnorm4x8(i.color); + flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0; +} +#endif diff --git a/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl new file mode 100644 index 000000000..1ae709e24 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl @@ -0,0 +1,22 @@ +#use "flywheel:api/vertex.glsl" +#define FLW_INSTANCE_STRUCT Instance + +struct Instance { + mat4 pose; + mat3 normal; + uint color; + uint light; +}; + +void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) { + center = (i.pose * vec4(center, 1.0)).xyz; +} + +#ifdef VERTEX_SHADER +void flw_instanceVertex(Instance i) { + flw_vertexPos = i.pose * flw_vertexPos; + flw_vertexNormal = i.normal * flw_vertexNormal; + flw_vertexColor = unpackUnorm4x8(i.color); + flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0; +} +#endif diff --git a/src/main/resources/assets/flywheel/flywheel/uniform/frustum.glsl b/src/main/resources/assets/flywheel/flywheel/uniform/frustum.glsl new file mode 100644 index 000000000..39866a922 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/uniform/frustum.glsl @@ -0,0 +1,14 @@ +struct FLWPackedPlanes { + vec4 xyX; // + vec4 xyY; // + vec4 xyZ; // + vec4 xyW; // + vec2 zX; // + vec2 zY; // + vec2 zZ; // + vec2 zW; // +}; + +layout(std140, binding = 2) uniform FLWFrustum { + FLWPackedPlanes flw_planes; +}; From e7339dc7ef5ac096f52867348241cfaeb0dbf600 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 9 Aug 2022 15:38:56 -0700 Subject: [PATCH 06/13] Condensing culling - Adapt compilers to work with arbitrary instance types - Use single compiler for all draw shaders - Temp solution for instanced array attributes - Introduce pipeline shaders --- .../java/com/jozufozu/flywheel/Flywheel.java | 4 +- .../flywheel/api/pipeline/PipelineShader.java | 8 + .../com/jozufozu/flywheel/backend/Loader.java | 8 +- .../backend/instancing/PipelineCompiler.java | 154 +++++++++++ .../indirect/ComputeCullerCompiler.java | 13 +- .../indirect/IndirectDrawCompiler.java | 65 ----- .../instancing/indirect/IndirectEngine.java | 16 +- .../instancing/indirect/IndirectList.java | 29 +- .../instancing/InstancedArraysCompiler.java | 252 ------------------ .../instancing/InstancingEngine.java | 6 +- .../jozufozu/flywheel/core/Components.java | 33 ++- .../flywheel/core/source/FileResolution.java | 21 ++ .../flywheel/core/source/SourceChecks.java | 8 + .../flywheel/core/source/SourceFile.java | 5 +- .../flywheel/flywheel/instance/oriented.vert | 10 +- .../flywheel/instance/oriented_indirect.glsl | 2 +- .../flywheel/instance/transformed.vert | 8 +- .../flywheel/flywheel/layout/block.vert | 1 + .../flywheel/layout/pos_tex_normal.vert | 1 + .../draw.frag} | 2 - .../indirect_cull.glsl} | 12 +- .../indirect_draw.vert} | 8 +- .../pipeline/instanced_arrays_draw.vert | 8 + .../assets/flywheel/flywheel/util/types.glsl | 17 ++ 24 files changed, 299 insertions(+), 392 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/api/pipeline/PipelineShader.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/PipelineCompiler.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawCompiler.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysCompiler.java rename src/main/resources/assets/flywheel/flywheel/{compute/draw_instances.frag => pipeline/draw.frag} (64%) rename src/main/resources/assets/flywheel/flywheel/{compute/cull_instances.glsl => pipeline/indirect_cull.glsl} (90%) rename src/main/resources/assets/flywheel/flywheel/{compute/draw_instances.vert => pipeline/indirect_draw.vert} (64%) create mode 100644 src/main/resources/assets/flywheel/flywheel/pipeline/instanced_arrays_draw.vert create mode 100644 src/main/resources/assets/flywheel/flywheel/util/types.glsl diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java index 925dd3eb4..d03a87d29 100644 --- a/src/main/java/com/jozufozu/flywheel/Flywheel.java +++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java @@ -16,7 +16,7 @@ import com.jozufozu.flywheel.core.DebugRender; import com.jozufozu.flywheel.core.PartialModel; import com.jozufozu.flywheel.core.QuadConverter; import com.jozufozu.flywheel.core.StitchedSprite; -import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysCompiler; +import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.model.Models; import com.jozufozu.flywheel.event.EntityWorldHandler; @@ -80,7 +80,7 @@ public class Flywheel { forgeEventBus.addListener(FlwCommands::registerClientCommands); forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onRendererReload); - forgeEventBus.addListener(InstancedArraysCompiler::invalidateAll); + forgeEventBus.addListener(PipelineCompiler::invalidateAll); forgeEventBus.addListener(Models::onReload); forgeEventBus.addListener(MeshPool::reset); forgeEventBus.addListener(CrumblingRenderer::onReloadRenderers); diff --git a/src/main/java/com/jozufozu/flywheel/api/pipeline/PipelineShader.java b/src/main/java/com/jozufozu/flywheel/api/pipeline/PipelineShader.java new file mode 100644 index 000000000..155614856 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/api/pipeline/PipelineShader.java @@ -0,0 +1,8 @@ +package com.jozufozu.flywheel.api.pipeline; + +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.core.source.FileResolution; + +public record PipelineShader(GLSLVersion glslVersion, FileResolution vertex, FileResolution fragment) { + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index 25893bac4..8db9c2da0 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -6,10 +6,10 @@ import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.core.ComponentRegistry; import com.jozufozu.flywheel.api.context.ContextShader; -import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysCompiler; +import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; +import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.source.FileResolution; -import com.jozufozu.flywheel.core.source.ShaderLoadingException; import com.jozufozu.flywheel.core.source.ShaderSources; import com.jozufozu.flywheel.core.source.error.ErrorReporter; import com.jozufozu.flywheel.util.StringUtil; @@ -69,8 +69,8 @@ public class Loader implements ResourceManagerReloadListener { for (StructType structType : ComponentRegistry.structTypes) { for (VertexType vertexType : ComponentRegistry.vertexTypes) { for (ContextShader contextShader : ComponentRegistry.contextShaders) { - var ctx = new InstancedArraysCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader); - InstancedArraysCompiler.INSTANCE.getProgram(ctx); + var ctx = new PipelineCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader, Components.INSTANCED_ARRAYS); + PipelineCompiler.INSTANCE.getProgram(ctx); } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/PipelineCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/PipelineCompiler.java new file mode 100644 index 000000000..1cf21721a --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/PipelineCompiler.java @@ -0,0 +1,154 @@ +package com.jozufozu.flywheel.backend.instancing; + +import java.util.List; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.api.context.ContextShader; +import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.api.pipeline.PipelineShader; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.compile.*; +import com.jozufozu.flywheel.core.source.CompilationContext; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.event.ReloadRenderersEvent; + +import net.minecraft.resources.ResourceLocation; + +/** + * A caching compiler. + * + *

+ * This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of + * compiled programs, and will only compile a program if it is not already in the cache. + *

+ *

+ * A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload. + *

+ */ +public class PipelineCompiler extends Memoizer { + + public static final PipelineCompiler INSTANCE = new PipelineCompiler(); + + private final ShaderCompiler shaderCompiler; + + private PipelineCompiler() { + this.shaderCompiler = new ShaderCompiler(); + } + + /** + * Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec. + * + * @param ctx The context of compilation. + * @return A compiled GlProgram. + */ + public GlProgram getProgram(PipelineCompiler.Context ctx) { + return super.get(ctx); + } + + @Override + public void invalidate() { + super.invalidate(); + shaderCompiler.invalidate(); + } + + @Override + protected GlProgram _create(PipelineCompiler.Context ctx) { + + var glslVersion = ctx.pipelineShader() + .glslVersion(); + + var vertex = new ShaderCompiler.Context(glslVersion, ShaderType.VERTEX, ctx.getVertexComponents()); + var fragment = new ShaderCompiler.Context(glslVersion, ShaderType.FRAGMENT, ctx.getFragmentComponents()); + + return new ProgramAssembler(ctx.instanceShader.getFileLoc()) + .attachShader(shaderCompiler.get(vertex)) + .attachShader(shaderCompiler.get(fragment)) + .link() + .build(ctx.contextShader.factory()); + } + + @Override + protected void _destroy(GlProgram value) { + value.delete(); + } + + public static void invalidateAll(ReloadRenderersEvent ignored) { + INSTANCE.invalidate(); + } + + /** + * Represents the entire context of a program's usage. + * + * @param vertexType The vertexType the program should be adapted for. + * @param material The material shader to use. + * @param instanceShader The instance shader to use. + * @param contextShader The context shader to use. + */ + public record Context(VertexType vertexType, Material material, FileResolution instanceShader, + ContextShader contextShader, PipelineShader pipelineShader) { + + ImmutableList getVertexComponents() { + return ImmutableList.of(vertexType.getLayoutShader().getFile(), instanceShader.getFile(), material.getVertexShader().getFile(), + contextShader.getVertexShader(), pipelineShader.vertex().getFile()); + } + + ImmutableList getFragmentComponents() { + return ImmutableList.of(material.getFragmentShader().getFile(), contextShader.getFragmentShader(), + pipelineShader.fragment().getFile()); + } + } + + /** + * Handles compilation and deletion of vertex shaders. + */ + public static class ShaderCompiler extends Memoizer { + + public ShaderCompiler() { + } + + @Override + protected GlShader _create(Context key) { + StringBuilder finalSource = new StringBuilder(); + + finalSource.append(key.generateHeader()); + finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n"); + finalSource.append("#extension GL_ARB_conservative_depth : enable\n"); + finalSource.append("#extension GL_ARB_enhanced_layouts : enable\n"); + + var ctx = new CompilationContext(); + + var names = ImmutableList.builder(); + for (var file : key.components) { + finalSource.append(file.generateFinalSource(ctx)); + names.add(file.name); + } + + try { + return new GlShader(finalSource.toString(), key.shaderType, names.build()); + } catch (ShaderCompilationException e) { + throw e.withErrorLog(ctx); + } + } + + @Override + protected void _destroy(GlShader value) { + value.delete(); + } + + /** + * @param glslVersion The GLSL version to use. + * @param components A list of shader components to stitch together, in order. + */ + public record Context(GLSLVersion glslVersion, ShaderType shaderType, List components) { + + public String generateHeader() { + return CompileUtil.generateHeader(glslVersion, shaderType); + } + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java index 151fbcb03..5f664367b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java @@ -5,6 +5,7 @@ import com.jozufozu.flywheel.backend.gl.GLSLVersion; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.compile.CompileUtil; import com.jozufozu.flywheel.core.compile.Memoizer; import com.jozufozu.flywheel.core.compile.ProgramAssembler; @@ -23,14 +24,18 @@ public class ComputeCullerCompiler extends Memoizer { @Override protected GlProgram _create(FileResolution file) { + var finalSource = new StringBuilder(); CompilationContext context = new CompilationContext(); - var header = CompileUtil.generateHeader(GLSLVersion.V460, ShaderType.COMPUTE); - String source = file.getFile() - .generateFinalSource(context); + finalSource.append(CompileUtil.generateHeader(GLSLVersion.V460, ShaderType.COMPUTE)); + finalSource.append(file.getFile() + .generateFinalSource(context)); + + finalSource.append(Components.Pipeline.INDIRECT_CULL.getFile() + .generateFinalSource(context)); try { - var shader = new GlShader(header + source, ShaderType.COMPUTE, ImmutableList.of(file.getFileLoc())); + var shader = new GlShader(finalSource.toString(), ShaderType.COMPUTE, ImmutableList.of(file.getFileLoc())); return new ProgramAssembler(file.getFileLoc()).attachShader(shader) .link() diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawCompiler.java deleted file mode 100644 index 80fc7bbc5..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawCompiler.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.indirect; - -import org.jetbrains.annotations.NotNull; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.gl.GLSLVersion; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.backend.gl.shader.GlShader; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.core.WorldProgram; -import com.jozufozu.flywheel.core.compile.CompileUtil; -import com.jozufozu.flywheel.core.compile.Memoizer; -import com.jozufozu.flywheel.core.compile.ProgramAssembler; -import com.jozufozu.flywheel.core.compile.ShaderCompilationException; -import com.jozufozu.flywheel.core.source.CompilationContext; -import com.jozufozu.flywheel.core.source.FileResolution; -import com.jozufozu.flywheel.core.source.ShaderLoadingException; -import com.jozufozu.flywheel.core.source.SourceFile; -import com.jozufozu.flywheel.event.ReloadRenderersEvent; - -public class IndirectDrawCompiler extends Memoizer { - public static final IndirectDrawCompiler INSTANCE = new IndirectDrawCompiler(); - - private IndirectDrawCompiler() { - } - - @Override - protected GlProgram _create(IndirectDrawCompiler.Program program) { - - GlShader vertexShader = compile(program.vertex.getFile(), ShaderType.VERTEX); - GlShader fragmentShader = compile(program.fragment.getFile(), ShaderType.FRAGMENT); - - return new ProgramAssembler(program.vertex.getFileLoc()) - .attachShader(vertexShader) - .attachShader(fragmentShader) - .link() - .build(WorldProgram::new); - } - - @NotNull - private static GlShader compile(SourceFile file, ShaderType type) { - var context = new CompilationContext(); - try { - var header = CompileUtil.generateHeader(GLSLVersion.V460, type); - var source = file.generateFinalSource(context); - - return new GlShader(header + source, type, ImmutableList.of(file.name)); - } catch (ShaderCompilationException e) { - throw e.withErrorLog(context); - } - } - - @Override - protected void _destroy(GlProgram value) { - value.delete(); - } - - public static void invalidateAll(ReloadRenderersEvent ignored) { - INSTANCE.invalidate(); - } - - public record Program(FileResolution vertex, FileResolution fragment) { - - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java index a422428cc..c47c9c59d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -19,7 +19,7 @@ import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.api.context.ContextShader; -import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysCompiler; +import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.uniform.UniformBuffer; import com.jozufozu.flywheel.util.WeakHashSet; @@ -91,20 +91,6 @@ public class IndirectEngine implements Engine { RenderSystem.enableCull(); } - protected void setup(ShaderState desc) { - - VertexType vertexType = desc.vertex(); - FileResolution instanceShader = desc.instance() - .getInstanceShader(); - Material material = desc.material(); - - var ctx = new InstancedArraysCompiler.Context(vertexType, material, instanceShader, context); - - InstancedArraysCompiler.INSTANCE.getProgram(ctx) - .bind(); - UniformBuffer.getInstance().sync(); - } - public void clearAll() { factories.values().forEach(IndirectFactory::clear); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java index fb5874298..c5b982471 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java @@ -12,6 +12,7 @@ import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.struct.StorageBufferWriter; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.QuadConverter; @@ -21,7 +22,7 @@ import com.jozufozu.flywheel.core.vertex.Formats; public class IndirectList { - private static final long DRAW_COMMAND_STRIDE = 48; + private static final long DRAW_COMMAND_STRIDE = 36; private static final long DRAW_COMMAND_OFFSET = 0; final StorageBufferWriter storageBufferWriter; @@ -73,18 +74,20 @@ public class IndirectList { meshPool = new IndirectMeshPool(Formats.BLOCK, 1024); // FIXME: Resizable buffers - maxObjectCount = 1024L; + maxObjectCount = 64 * 64 * 64; maxBatchCount = 64; objectStride = storageBufferWriter.getAlignment(); - glNamedBufferStorage(objectBuffer, objectStride * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); - glNamedBufferStorage(targetBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); - glNamedBufferStorage(batchBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); + int persistentBits = GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT; + glNamedBufferStorage(objectBuffer, objectStride * maxObjectCount, persistentBits); + glNamedBufferStorage(targetBuffer, 4 * maxObjectCount, 0); + glNamedBufferStorage(batchBuffer, 4 * maxObjectCount, persistentBits); glNamedBufferStorage(drawBuffer, DRAW_COMMAND_STRIDE * maxBatchCount, GL_DYNAMIC_STORAGE_BIT); - glNamedBufferStorage(debugBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); + glNamedBufferStorage(debugBuffer, 4 * maxObjectCount, 0); - objectClientStorage = MemoryUtil.nmemAlloc(objectStride * maxObjectCount); - batchIDClientStorage = MemoryUtil.nmemAlloc(4 * maxObjectCount); + int mapBits = persistentBits | GL_MAP_FLUSH_EXPLICIT_BIT; + objectClientStorage = nglMapNamedBufferRange(objectBuffer, 0, objectStride * maxObjectCount, mapBits); + batchIDClientStorage = nglMapNamedBufferRange(batchBuffer, 0, 4 * maxObjectCount, mapBits); vertexArray = glCreateVertexArrays(); @@ -92,8 +95,8 @@ public class IndirectList { .quads2Tris(2048).buffer.handle(); setupVertexArray(); - compute = ComputeCullerCompiler.INSTANCE.get(Components.Files.CULL_INSTANCES); - draw = IndirectDrawCompiler.INSTANCE.get(new IndirectDrawCompiler.Program(Components.Files.DRAW_INDIRECT_VERTEX, Components.Files.DRAW_INDIRECT_FRAGMENT)); + compute = ComputeCullerCompiler.INSTANCE.get(Components.Files.ORIENTED_INDIRECT); + draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(Formats.BLOCK, Materials.BELL, Components.Files.ORIENTED_INDIRECT, Components.WORLD, Components.INDIRECT)); } private void setupVertexArray() { @@ -184,8 +187,8 @@ public class IndirectList { batchID++; } - nglNamedBufferSubData(objectBuffer, 0, objectPtr - objectClientStorage, objectClientStorage); - nglNamedBufferSubData(batchBuffer, 0, batchIDPtr - batchIDClientStorage, batchIDClientStorage); + glFlushMappedNamedBufferRange(objectBuffer, 0, objectPtr - objectClientStorage); + glFlushMappedNamedBufferRange(batchBuffer, 0, batchIDPtr - batchIDClientStorage); } private void uploadIndirectCommands() { @@ -229,7 +232,7 @@ public class IndirectList { MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance - boundingSphere.getToAddress(ptr + 32); // boundingSphere + boundingSphere.getToAddress(ptr + 20); // boundingSphere } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysCompiler.java deleted file mode 100644 index 4a4f92779..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysCompiler.java +++ /dev/null @@ -1,252 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.instancing; - -import java.util.ArrayList; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.api.context.ContextShader; -import com.jozufozu.flywheel.api.material.Material; -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.gl.GLSLVersion; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.backend.gl.shader.GlShader; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.core.compile.*; -import com.jozufozu.flywheel.core.source.CompilationContext; -import com.jozufozu.flywheel.core.source.FileResolution; -import com.jozufozu.flywheel.core.source.SourceFile; -import com.jozufozu.flywheel.core.source.parse.ShaderField; -import com.jozufozu.flywheel.core.source.span.Span; -import com.jozufozu.flywheel.event.ReloadRenderersEvent; -import com.jozufozu.flywheel.util.Pair; - -/** - * A caching compiler. - * - *

- * This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of - * compiled programs, and will only compile a program if it is not already in the cache. - *

- *

- * A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload. - *

- */ -public class InstancedArraysCompiler extends Memoizer { - - public static final InstancedArraysCompiler INSTANCE = new InstancedArraysCompiler(); - - private final VertexCompiler vertexCompiler; - private final FragmentCompiler fragmentCompiler; - - private InstancedArraysCompiler() { - this.vertexCompiler = new VertexCompiler(); - this.fragmentCompiler = new FragmentCompiler(); - } - - /** - * Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec. - * - * @param ctx The context of compilation. - * @return A compiled GlProgram. - */ - public GlProgram getProgram(InstancedArraysCompiler.Context ctx) { - return super.get(ctx); - } - - @Override - public void invalidate() { - super.invalidate(); - vertexCompiler.invalidate(); - fragmentCompiler.invalidate(); - } - - @Override - protected GlProgram _create(InstancedArraysCompiler.Context ctx) { - // TODO: try-catch here to prevent crashing if shaders failed to compile - Material material = ctx.material; - FileResolution instanceShader = ctx.instanceShader(); - ContextShader contextShader = ctx.contextShader; - - var vertex = new VertexCompiler.Context(ctx.vertexType(), instanceShader.getFile(), material.getVertexShader().getFile(), - contextShader.getVertexShader()); - - var fragment = new FragmentCompiler.Context(material.getFragmentShader().getFile(), contextShader.getFragmentShader()); - - return new ProgramAssembler(instanceShader.getFileLoc()) - .attachShader(vertexCompiler.get(vertex)) - .attachShader(fragmentCompiler.get(fragment)) - .link() - .build(contextShader.factory()); - } - - @Override - protected void _destroy(GlProgram value) { - value.delete(); - } - - public static void invalidateAll(ReloadRenderersEvent ignored) { - INSTANCE.invalidate(); - } - - /** - * Represents the entire context of a program's usage. - * - * @param vertexType The vertexType the program should be adapted for. - * @param material The material shader to use. - * @param instanceShader The instance shader to use. - * @param contextShader The context shader to use. - */ - public record Context(VertexType vertexType, Material material, FileResolution instanceShader, - ContextShader contextShader) { - } - - /** - * Handles compilation and deletion of vertex shaders. - */ - public static class VertexCompiler extends Memoizer { - - public VertexCompiler() { - } - - @Override - protected GlShader _create(Context key) { - StringBuilder finalSource = new StringBuilder(); - - finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX)); - finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n"); - - var index = new CompilationContext(); - - // LAYOUT - - var layoutShader = key.vertexType.getLayoutShader().getFile(); - finalSource.append(layoutShader.generateFinalSource(index)); - - // INSTANCE - - int attributeBaseIndex = key.vertexType.getLayout() - .getAttributeCount(); - - var instanceShader = key.instanceShader; - var replacements = new ArrayList>(); - for (ShaderField field : instanceShader.fields.values()) { - if (field.decoration != ShaderField.Decoration.IN) { - continue; - } - - int location = Integer.parseInt(field.location.get()); - int newLocation = location + attributeBaseIndex; - replacements.add(Pair.of(field.location, Integer.toString(newLocation))); - } - finalSource.append(instanceShader.generateFinalSource(index, replacements)); - - // MATERIAL - - var materialShader = key.materialShader; - finalSource.append(materialShader.generateFinalSource(index)); - - // CONTEXT - - var contextShaderSource = key.contextShader; - finalSource.append(contextShaderSource.generateFinalSource(index)); - - // MAIN - - finalSource.append(""" - void main() { - flw_layoutVertex(); - - flw_instanceVertex(); - - flw_materialVertex(); - - flw_contextVertex(); - } - """); - - try { - return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name)); - } catch (ShaderCompilationException e) { - throw e.withErrorLog(index); - } - } - - @Override - protected void _destroy(GlShader value) { - value.delete(); - } - - /** - * @param vertexType The vertex type to use. - * @param instanceShader The instance shader source. - * @param materialShader The vertex material shader source. - * @param contextShader The context shader source. - */ - public record Context(VertexType vertexType, SourceFile instanceShader, SourceFile materialShader, SourceFile contextShader) { - } - } - - /** - * Handles compilation and deletion of fragment shaders. - */ - public static class FragmentCompiler extends Memoizer { - - public FragmentCompiler() { - } - - @Override - protected GlShader _create(Context key) { - StringBuilder finalSource = new StringBuilder(); - - finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.FRAGMENT)); - finalSource.append("#extension GL_ARB_conservative_depth : enable\n"); - - var ctx = new CompilationContext(); - - // MATERIAL - - SourceFile materialShader = key.materialShader; - finalSource.append(materialShader.generateFinalSource(ctx)); - - // CONTEXT - - SourceFile contextShaderSource = key.contextShader; - finalSource.append(contextShaderSource.generateFinalSource(ctx)); - - // MAIN - - finalSource.append(generateFooter()); - - try { - return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name)); - } catch (ShaderCompilationException e) { - throw e.withErrorLog(ctx); - } - } - - protected String generateFooter() { - return """ - void main() { - flw_initFragment(); - - flw_materialFragment(); - - flw_contextFragment(); - } - """; - } - - @Override - protected void _destroy(GlShader value) { - value.delete(); - } - - /** - * Represents the conditions under which a shader is compiled. - * - * @param materialShader The fragment material shader source. - */ - public record Context(SourceFile materialShader, SourceFile contextShader) { - - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index e8d21a692..5685795fa 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -17,7 +17,9 @@ import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.gl.GlTextureUnit; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.InstanceManager; +import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; import com.jozufozu.flywheel.backend.instancing.TaskEngine; +import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.core.source.FileResolution; @@ -136,9 +138,9 @@ public class InstancingEngine implements Engine { .getInstanceShader(); Material material = desc.material(); - var ctx = new InstancedArraysCompiler.Context(vertexType, material, instanceShader, context); + var ctx = new PipelineCompiler.Context(vertexType, material, instanceShader, context, Components.INSTANCED_ARRAYS); - InstancedArraysCompiler.INSTANCE.getProgram(ctx) + PipelineCompiler.INSTANCE.getProgram(ctx) .bind(); UniformBuffer.getInstance().sync(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/Components.java b/src/main/java/com/jozufozu/flywheel/core/Components.java index b031bea83..ab85c20d4 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Components.java +++ b/src/main/java/com/jozufozu/flywheel/core/Components.java @@ -4,6 +4,8 @@ import java.util.function.BiConsumer; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.context.ContextShader; +import com.jozufozu.flywheel.api.pipeline.PipelineShader; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; import com.jozufozu.flywheel.core.crumbling.CrumblingProgram; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.SourceChecks; @@ -27,6 +29,9 @@ public class Components { public static final ContextShader WORLD = ComponentRegistry.register(new ContextShader(WorldProgram::new, Files.WORLD_VERTEX, Files.WORLD_FRAGMENT)); public static final ContextShader CRUMBLING = ComponentRegistry.register(new ContextShader(CrumblingProgram::new, Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT)); + public static final PipelineShader INSTANCED_ARRAYS = new PipelineShader(GLSLVersion.V420, Pipeline.INSTANCED_ARRAYS_DRAW, Pipeline.DRAW_FRAGMENT); + public static final PipelineShader INDIRECT = new PipelineShader(GLSLVersion.V460, Pipeline.INDIRECT_DRAW, Pipeline.DRAW_FRAGMENT); + public static void init() { Files.init(); Formats.init(); @@ -34,6 +39,18 @@ public class Components { Materials.init(); } + public static class Pipeline { + public static final FileResolution DRAW_FRAGMENT = pipeline("pipeline/draw.frag"); + public static final FileResolution INSTANCED_ARRAYS_DRAW = pipeline("pipeline/instanced_arrays_draw.vert"); + public static final FileResolution INDIRECT_DRAW = pipeline("pipeline/indirect_draw.vert"); + public static final FileResolution INDIRECT_CULL = pipeline("pipeline/indirect_cull.glsl"); + + private static FileResolution pipeline(String name) { + return FileResolution.get(Flywheel.rl(name)) + .validateWith(Checks.PIPELINE); + } + } + public static class Files { public static final FileResolution VIEW_UNIFORMS = uniform(Flywheel.rl("uniform/view.glsl")); @@ -42,7 +59,9 @@ public class Components { public static final FileResolution BLOCK_LAYOUT = layoutVertex(ResourceUtil.subPath(Names.BLOCK, ".vert")); public static final FileResolution POS_TEX_NORMAL_LAYOUT = layoutVertex(ResourceUtil.subPath(Names.POS_TEX_NORMAL, ".vert")); public static final FileResolution TRANSFORMED = instanceVertex(ResourceUtil.subPath(Names.TRANSFORMED, ".vert")); + public static final FileResolution TRANSFORMED_INDIRECT = instanceVertex(ResourceUtil.subPath(Names.TRANSFORMED, "_indirect.glsl")); public static final FileResolution ORIENTED = instanceVertex(ResourceUtil.subPath(Names.ORIENTED, ".vert")); + public static final FileResolution ORIENTED_INDIRECT = instanceVertex(ResourceUtil.subPath(Names.ORIENTED, "_indirect.glsl")); public static final FileResolution DEFAULT_VERTEX = materialVertex(ResourceUtil.subPath(Names.DEFAULT, ".vert")); public static final FileResolution SHADED_VERTEX = materialVertex(ResourceUtil.subPath(Names.SHADED, ".vert")); public static final FileResolution DEFAULT_FRAGMENT = materialFragment(ResourceUtil.subPath(Names.DEFAULT, ".frag")); @@ -51,9 +70,6 @@ public class Components { public static final FileResolution WORLD_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.WORLD, ".frag")); public static final FileResolution CRUMBLING_VERTEX = contextVertex(ResourceUtil.subPath(Names.CRUMBLING, ".vert")); public static final FileResolution CRUMBLING_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.CRUMBLING, ".frag")); - public static final FileResolution CULL_INSTANCES = compute(Flywheel.rl("compute/cull_instances.glsl")); - public static final FileResolution DRAW_INDIRECT_VERTEX = FileResolution.get(ResourceUtil.subPath(Names.DRAW_INDIRECT, ".vert")); - public static final FileResolution DRAW_INDIRECT_FRAGMENT = FileResolution.get(ResourceUtil.subPath(Names.DRAW_INDIRECT, ".frag")); private static FileResolution compute(ResourceLocation rl) { return FileResolution.get(rl); @@ -69,8 +85,7 @@ public class Components { } private static FileResolution instanceVertex(ResourceLocation location) { - return FileResolution.get(location) - .validateWith(Checks.INSTANCE_VERTEX); + return FileResolution.get(location); // .validateWith(Checks.INSTANCE_VERTEX); } private static FileResolution materialVertex(ResourceLocation location) { @@ -100,12 +115,16 @@ public class Components { public static class Checks { - public static final BiConsumer LAYOUT_VERTEX = SourceChecks.checkFunctionArity("flw_layoutVertex", 0); - public static final BiConsumer INSTANCE_VERTEX = SourceChecks.checkFunctionArity("flw_instanceVertex", 0); + public static final BiConsumer LAYOUT_VERTEX = SourceChecks.checkFunctionArity("flw_layoutVertex", 0) + .andThen(SourceChecks.checkDefine("FLW_INSTANCE_BASE_INDEX")); + public static final BiConsumer INSTANCE_VERTEX = SourceChecks.checkFunctionParameterTypeExists("flw_instanceVertex", 1, 0) + .andThen(SourceChecks.checkDefine("FLW_INSTANCE_STRUCT")); public static final BiConsumer MATERIAL_VERTEX = SourceChecks.checkFunctionArity("flw_materialVertex", 0); public static final BiConsumer MATERIAL_FRAGMENT = SourceChecks.checkFunctionArity("flw_materialFragment", 0); public static final BiConsumer CONTEXT_VERTEX = SourceChecks.checkFunctionArity("flw_contextVertex", 0); public static final BiConsumer CONTEXT_FRAGMENT = SourceChecks.checkFunctionArity("flw_contextFragment", 0).andThen(SourceChecks.checkFunctionArity("flw_initFragment", 0)); + + public static final BiConsumer PIPELINE = SourceChecks.checkFunctionArity("main", 0); } public static class Names { diff --git a/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java b/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java index 8d704ab55..ea4ca0d43 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java @@ -162,4 +162,25 @@ public class FileResolution { public String toString() { return "FileResolution[" + fileLoc + "]"; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + FileResolution that = (FileResolution) o; + + return fileLoc.equals(that.fileLoc); + } + + @Override + public int hashCode() { + // FileResolutions are interned and therefore can be hashed based on object identity. + // Overriding this to make it explicit. + return System.identityHashCode(this); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/SourceChecks.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceChecks.java index a8247a33e..1d4023705 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/SourceChecks.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceChecks.java @@ -54,4 +54,12 @@ public class SourceChecks { return func; } + + public static BiConsumer checkDefine(String define) { + return (errorReporter, file) -> { +// if (!file.hasDefine(define)) { +// errorReporter.generateMissingDefine(file, define, "\"" + define + "\" define not defined"); +// } + }; + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java index 2f799677b..faeb46acb 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java @@ -185,10 +185,7 @@ public class SourceFile { } public String generateFinalSource(CompilationContext context) { - return generateFinalSource(context, Collections.emptyList()); - } - - public String generateFinalSource(CompilationContext context, List> replacements) { + List> replacements = Collections.emptyList(); var out = new StringBuilder(); for (Import include : flattenedImports) { SourceFile file = include.getFile(); diff --git a/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert b/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert index 93a50ddee..880d55de4 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert +++ b/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert @@ -1,11 +1,11 @@ #use "flywheel:api/vertex.glsl" #use "flywheel:util/quaternion.glsl" -layout(location = 0) in ivec2 oriented_light; -layout(location = 1) in vec4 oriented_color; -layout(location = 2) in vec3 oriented_pos; -layout(location = 3) in vec3 oriented_pivot; -layout(location = 4) in vec4 oriented_rotation; +layout(location = FLW_INSTANCE_BASE_INDEX + 0) in ivec2 oriented_light; +layout(location = FLW_INSTANCE_BASE_INDEX + 1) in vec4 oriented_color; +layout(location = FLW_INSTANCE_BASE_INDEX + 2) in vec3 oriented_pos; +layout(location = FLW_INSTANCE_BASE_INDEX + 3) in vec3 oriented_pivot; +layout(location = FLW_INSTANCE_BASE_INDEX + 4) in vec4 oriented_rotation; void flw_instanceVertex() { flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - oriented_pivot, oriented_rotation) + oriented_pivot + oriented_pos, 1.0); diff --git a/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl b/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl index c02c57bfc..fbf76667c 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl +++ b/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl @@ -1,7 +1,7 @@ #use "flywheel:api/vertex.glsl" #use "flywheel:util/quaternion.glsl" -#define FLW_INSTANCE_STRUCT Instance +#define FLW_INSTANCE_STRUCT Instance struct Instance { vec4 rotation; vec3 pos; diff --git a/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert b/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert index e2ea23ef4..2c6161d18 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert +++ b/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert @@ -1,9 +1,9 @@ #use "flywheel:api/vertex.glsl" -layout(location = 0) in ivec2 transformed_light; -layout(location = 1) in vec4 transformed_color; -layout(location = 2) in mat4 transformed_pose; -layout(location = 6) in mat3 transformed_normal; +layout(location = FLW_INSTANCE_BASE_INDEX + 0) in ivec2 transformed_light; +layout(location = FLW_INSTANCE_BASE_INDEX + 1) in vec4 transformed_color; +layout(location = FLW_INSTANCE_BASE_INDEX + 2) in mat4 transformed_pose; +layout(location = FLW_INSTANCE_BASE_INDEX + 6) in mat3 transformed_normal; void flw_instanceVertex() { flw_vertexPos = transformed_pose * flw_vertexPos; diff --git a/src/main/resources/assets/flywheel/flywheel/layout/block.vert b/src/main/resources/assets/flywheel/flywheel/layout/block.vert index a3af3b16d..4123724aa 100644 --- a/src/main/resources/assets/flywheel/flywheel/layout/block.vert +++ b/src/main/resources/assets/flywheel/flywheel/layout/block.vert @@ -5,6 +5,7 @@ layout(location = 1) in vec4 _flw_v_color; layout(location = 2) in vec2 _flw_v_texCoord; layout(location = 3) in ivec2 _flw_v_light; layout(location = 4) in vec3 _flw_v_normal; +#define FLW_INSTANCE_BASE_INDEX 5 void flw_layoutVertex() { flw_vertexPos = vec4(_flw_v_pos, 1.0); diff --git a/src/main/resources/assets/flywheel/flywheel/layout/pos_tex_normal.vert b/src/main/resources/assets/flywheel/flywheel/layout/pos_tex_normal.vert index 4ecfb2c3c..80b02f8bc 100644 --- a/src/main/resources/assets/flywheel/flywheel/layout/pos_tex_normal.vert +++ b/src/main/resources/assets/flywheel/flywheel/layout/pos_tex_normal.vert @@ -3,6 +3,7 @@ layout(location = 0) in vec3 _flw_v_pos; layout(location = 1) in vec2 _flw_v_texCoord; layout(location = 2) in vec3 _flw_v_normal; +#define FLW_INSTANCE_BASE_INDEX 3 void flw_layoutVertex() { flw_vertexPos = vec4(_flw_v_pos, 1.0); diff --git a/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.frag b/src/main/resources/assets/flywheel/flywheel/pipeline/draw.frag similarity index 64% rename from src/main/resources/assets/flywheel/flywheel/compute/draw_instances.frag rename to src/main/resources/assets/flywheel/flywheel/pipeline/draw.frag index 6591bea51..f57dd9a87 100644 --- a/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.frag +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/draw.frag @@ -1,6 +1,4 @@ #use "flywheel:api/fragment.glsl" -#use "flywheel:context/world.frag" -#use "flywheel:material/default.frag" void main() { flw_initFragment(); diff --git a/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl similarity index 90% rename from src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl rename to src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl index 705b379c2..170df512a 100644 --- a/src/main/resources/assets/flywheel/flywheel/compute/cull_instances.glsl +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl @@ -1,9 +1,8 @@ #define FLW_SUBGROUP_SIZE 32 layout(local_size_x = FLW_SUBGROUP_SIZE) in; #use "flywheel:api/cull.glsl" -#use "flywheel:util/quaternion.glsl" #use "flywheel:uniform/frustum.glsl" -#use "flywheel:instance/oriented_indirect.glsl" +#use "flywheel:util/types.glsl" struct MeshDrawCommand { uint indexCount; @@ -12,7 +11,7 @@ struct MeshDrawCommand { uint vertexOffset; uint baseInstance; - vec4 boundingSphere; + BoundingSphere boundingSphere; }; // populated by instancers @@ -54,10 +53,11 @@ bool testSphere(vec3 center, float radius) { } bool isVisible() { - vec4 sphere = drawCommands[flw_batchID].boundingSphere; + BoundingSphere sphere = drawCommands[flw_batchID].boundingSphere; - vec3 center = sphere.xyz; - float radius = sphere.r; + vec3 center; + float radius; + unpackBoundingSphere(sphere, center, radius); flw_transformBoundingSphere(objects[flw_objectID], center, radius); return testSphere(center, radius); diff --git a/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw.vert similarity index 64% rename from src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert rename to src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw.vert index 66fea9dae..7583ebbd6 100644 --- a/src/main/resources/assets/flywheel/flywheel/compute/draw_instances.vert +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw.vert @@ -1,10 +1,5 @@ #use "flywheel:api/vertex.glsl" -#use "flywheel:layout/block.vert" -#use "flywheel:context/world.vert" -#use "flywheel:util/quaternion.glsl" -#use "flywheel:instance/oriented_indirect.glsl" -// populated by instancers layout(std430, binding = 0) restrict readonly buffer ObjectBuffer { FLW_INSTANCE_STRUCT objects[]; }; @@ -16,7 +11,8 @@ layout(std430, binding = 1) restrict readonly buffer TargetBuffer { void main() { uint instanceIndex = objectIDs[gl_BaseInstance + gl_InstanceID]; flw_layoutVertex(); - Instance i = objects[instanceIndex]; + FLW_INSTANCE_STRUCT i = objects[instanceIndex]; flw_instanceVertex(i); + flw_materialVertex(); flw_contextVertex(); } diff --git a/src/main/resources/assets/flywheel/flywheel/pipeline/instanced_arrays_draw.vert b/src/main/resources/assets/flywheel/flywheel/pipeline/instanced_arrays_draw.vert new file mode 100644 index 000000000..0b75edde3 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/instanced_arrays_draw.vert @@ -0,0 +1,8 @@ +#use "flywheel:api/vertex.glsl" + +void main() { + flw_layoutVertex(); + flw_instanceVertex(); + flw_materialVertex(); + flw_contextVertex(); +} diff --git a/src/main/resources/assets/flywheel/flywheel/util/types.glsl b/src/main/resources/assets/flywheel/flywheel/util/types.glsl new file mode 100644 index 000000000..a6f58174b --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/util/types.glsl @@ -0,0 +1,17 @@ + +struct Vec3F { + float x; + float y; + float z; +}; + +// 4-aligned instead of a 16-aligned vec4 +struct BoundingSphere { + Vec3F center; + float radius; +}; + +void unpackBoundingSphere(in BoundingSphere sphere, out vec3 center, out float radius) { + center = vec3(sphere.center.x, sphere.center.y, sphere.center.z); + radius = sphere.radius; +} From 56ded52193c9ed392190dc89c520cb254467d42e Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 13 Aug 2022 21:28:39 -0700 Subject: [PATCH 07/13] Indirectly transformed - Indirect rendering supports TransformedType - Use single object to manage all ssbos --- gradle.properties | 2 +- .../flywheel/api/struct/StructType.java | 2 + .../jozufozu/flywheel/backend/Backend.java | 3 +- .../backend/gl/versioned/GlCompat.java | 2 +- .../instancing/indirect/IndirectBuffers.java | 134 ++++++++++++++++ .../instancing/indirect/IndirectEngine.java | 8 +- .../indirect/IndirectInstancer.java | 24 --- .../instancing/indirect/IndirectList.java | 146 +++++++----------- .../instancing/indirect/IndirectMeshPool.java | 2 + .../instancing/indirect/InstancedModel.java | 2 +- .../instancing/indirect/RenderLists.java | 30 +--- .../model/TransformedStorageWriter.java | 31 ++++ .../core/structs/model/TransformedType.java | 7 +- .../core/structs/oriented/OrientedType.java | 5 + .../util/extension/MatrixExtension.java | 5 + .../instance/transformed_indirect.glsl | 2 +- .../flywheel/pipeline/indirect_cull.glsl | 13 -- 17 files changed, 259 insertions(+), 159 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedStorageWriter.java diff --git a/gradle.properties b/gradle.properties index 95019e8cf..79c69ac95 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ minecraft_version = 1.18.2 forge_version = 40.1.68 # build dependency versions -forgegradle_version = 5.1.+ +forgegradle_version = 5.1.53 mixingradle_version = 0.7-SNAPSHOT mixin_version = 0.8.5 librarian_version = 1.+ diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java index 3463c7cc5..ba6922888 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java @@ -38,6 +38,8 @@ public interface StructType { StorageBufferWriter getStorageBufferWriter(); + FileResolution getIndirectShader(); + public interface VertexTransformer { void transform(MutableVertexList vertexList, S struct, ClientLevel level); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index 20c6f1b62..b48077615 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -54,8 +54,7 @@ public class Backend { } public static void refresh() { - // TODO: Revert when done testing - TYPE = BackendType.INDIRECT; // chooseEngine(); + TYPE = chooseEngine(); } public static boolean isOn() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java index 3ae01a15d..972962dc3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java @@ -39,7 +39,7 @@ public class GlCompat { instancedArrays = getLatest(InstancedArrays.class, caps); bufferStorage = getLatest(BufferStorage.class, caps); - supportsIndirect = caps.OpenGL46; + supportsIndirect = true; amd = _isAmdWindows(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java new file mode 100644 index 000000000..6f32d02c4 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java @@ -0,0 +1,134 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import static org.lwjgl.opengl.GL46.*; + +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.Pointer; + +public class IndirectBuffers { + public static final int BUFFER_COUNT = 4; + public static final long INT_SIZE = Integer.BYTES; + public static final long PTR_SIZE = Pointer.POINTER_SIZE; + + // DRAW COMMAND + public static final long DRAW_COMMAND_STRIDE = 36; + public static final long DRAW_COMMAND_OFFSET = 0; + + // BITS + private static final int SUB_DATA_BITS = GL_DYNAMIC_STORAGE_BIT; + private static final int PERSISTENT_BITS = GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT; + private static final int MAP_BITS = PERSISTENT_BITS | GL_MAP_FLUSH_EXPLICIT_BIT; + private static final int GPU_ONLY_BITS = 0; + + // OFFSETS + private static final long OFFSET_OFFSET = BUFFER_COUNT * INT_SIZE; + private static final long SIZE_OFFSET = OFFSET_OFFSET + BUFFER_COUNT * PTR_SIZE; + private static final long BUFFERS_SIZE_BYTES = SIZE_OFFSET + BUFFER_COUNT * PTR_SIZE; + + private static final long OBJECT_SIZE_OFFSET = SIZE_OFFSET; + private static final long TARGET_SIZE_OFFSET = OBJECT_SIZE_OFFSET + PTR_SIZE; + private static final long BATCH_SIZE_OFFSET = TARGET_SIZE_OFFSET + PTR_SIZE; + private static final long DRAW_SIZE_OFFSET = BATCH_SIZE_OFFSET + PTR_SIZE; + + final long buffers; + final long objectStride; + int object; + int target; + int batch; + int draw; + + long objectPtr; + long batchPtr; + long drawPtr; + + int maxObjectCount; + int maxDrawCount; + + IndirectBuffers(long objectStride) { + this.objectStride = objectStride; + this.buffers = MemoryUtil.nmemAlloc(BUFFERS_SIZE_BYTES); + + if (this.buffers == MemoryUtil.NULL) { + throw new OutOfMemoryError(); + } + + MemoryUtil.memSet(this.buffers, 0, BUFFERS_SIZE_BYTES); + } + + void createBuffers() { + nglCreateBuffers(4, buffers); + object = MemoryUtil.memGetInt(buffers); + target = MemoryUtil.memGetInt(buffers + 4); + batch = MemoryUtil.memGetInt(buffers + 8); + draw = MemoryUtil.memGetInt(buffers + 12); + } + + void updateCounts(int objectCount, int drawCount) { + + if (objectCount > maxObjectCount) { + createObjectStorage(objectCount); + } + if (drawCount > maxDrawCount) { + createDrawStorage(drawCount); + } + + long objectSize = objectStride * objectCount; + long targetSize = INT_SIZE * objectCount; + long drawSize = DRAW_COMMAND_STRIDE * drawCount; + + MemoryUtil.memPutAddress(buffers + OBJECT_SIZE_OFFSET, objectSize); + MemoryUtil.memPutAddress(buffers + TARGET_SIZE_OFFSET, targetSize); + MemoryUtil.memPutAddress(buffers + BATCH_SIZE_OFFSET, targetSize); + MemoryUtil.memPutAddress(buffers + DRAW_SIZE_OFFSET, drawSize); + } + + void createObjectStorage(int objectCount) { + var objectSize = objectStride * objectCount; + var targetSize = INT_SIZE * objectCount; + + glNamedBufferStorage(object, objectSize, PERSISTENT_BITS); + glNamedBufferStorage(target, targetSize, GPU_ONLY_BITS); + glNamedBufferStorage(batch, targetSize, PERSISTENT_BITS); + + objectPtr = nglMapNamedBufferRange(object, 0, objectSize, MAP_BITS); + batchPtr = nglMapNamedBufferRange(batch, 0, targetSize, MAP_BITS); + maxObjectCount = objectCount; + } + + void createDrawStorage(int drawCount) { + var drawSize = DRAW_COMMAND_STRIDE * drawCount; + glNamedBufferStorage(draw, drawSize, SUB_DATA_BITS); + drawPtr = MemoryUtil.nmemAlloc(drawSize); + // drawPtr = nglMapNamedBufferRange(draw, 0, drawSize, MAP_BITS); + maxDrawCount = drawCount; + } + + public void bindAll() { + bindN(BUFFER_COUNT); + } + + public void bindObjectAndTarget() { + bindN(2); + } + + private void bindN(int bufferCount) { + nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, bufferCount, buffers, buffers + OFFSET_OFFSET, buffers + SIZE_OFFSET); + } + + void bindIndirectBuffer() { + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, draw); + } + + void flushBatchIDs(long length) { + glFlushMappedNamedBufferRange(batch, 0, length); + } + + void flushObjects(long length) { + glFlushMappedNamedBufferRange(object, 0, length); + } + + void flushDrawCommands(long length) { + nglNamedBufferSubData(draw, 0, length, drawPtr); + // glFlushMappedNamedBufferRange(this.draw, 0, length); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java index c47c9c59d..5b4a61d2e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -70,12 +70,14 @@ public class IndirectEngine implements Engine { @Override public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) { - var groups = renderLists.get(stage); + if (stage != RenderStage.AFTER_SOLID_TERRAIN) { + return; + } setup(); - for (var group : groups) { - group.submit(); + for (IndirectList list : renderLists.lists.values()) { + list.submit(stage); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java index bcc09acf2..561bfe3db 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java @@ -41,30 +41,6 @@ public class IndirectInstancer extends AbstractInstance anyToRemove = false; } - void writeAll(final StructWriter writer) { - anyToUpdate = false; - - for (var instance : data) { - writer.write(instance); - } - } - - void writeChanged(final StructWriter writer) { - if (!anyToUpdate) { - return; - } - - anyToUpdate = false; - - final int size = data.size(); - - if (size == 0) { - return; - } - - writeChangedUnchecked(writer); - } - @Override public void delete() { // noop diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java index c5b982471..90bfe5777 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java @@ -5,12 +5,15 @@ import static org.lwjgl.opengl.GL46.*; import java.util.ArrayList; import java.util.List; -import org.lwjgl.system.MemoryStack; +import org.jetbrains.annotations.NotNull; import org.lwjgl.system.MemoryUtil; +import com.jozufozu.flywheel.api.RenderStage; import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.struct.StorageBufferWriter; import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; import com.jozufozu.flywheel.core.Components; @@ -18,76 +21,37 @@ import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.QuadConverter; import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.uniform.UniformBuffer; -import com.jozufozu.flywheel.core.vertex.Formats; public class IndirectList { - private static final long DRAW_COMMAND_STRIDE = 36; - private static final long DRAW_COMMAND_OFFSET = 0; - final StorageBufferWriter storageBufferWriter; final GlProgram compute; final GlProgram draw; - private final StructType type; - private final long maxObjectCount; + private final StructType structType; + private final VertexType vertexType; private final long objectStride; - private final int maxBatchCount; - private final long objectClientStorage; - private final long batchIDClientStorage; - private final int elementBuffer; - /** - * Stores raw instance data per-object. - */ - int objectBuffer; - int targetBuffer; - int batchBuffer; - - /** - * Stores drawIndirect structs. - */ - int drawBuffer; - int debugBuffer; + final IndirectBuffers buffers; final IndirectMeshPool meshPool; + private final int elementBuffer; int vertexArray; - final int[] shaderStorageBuffers = new int[5]; - final List> batches = new ArrayList<>(); - IndirectList(StructType structType) { - type = structType; - storageBufferWriter = type.getStorageBufferWriter(); - - if (storageBufferWriter == null) { - throw new NullPointerException(); - } - - glCreateBuffers(shaderStorageBuffers); - objectBuffer = shaderStorageBuffers[0]; - targetBuffer = shaderStorageBuffers[1]; - batchBuffer = shaderStorageBuffers[2]; - drawBuffer = shaderStorageBuffers[3]; - debugBuffer = shaderStorageBuffers[4]; - meshPool = new IndirectMeshPool(Formats.BLOCK, 1024); - - // FIXME: Resizable buffers - maxObjectCount = 64 * 64 * 64; - maxBatchCount = 64; + IndirectList(StructType structType, VertexType vertexType) { + this.structType = structType; + this.vertexType = vertexType; + storageBufferWriter = this.structType.getStorageBufferWriter(); objectStride = storageBufferWriter.getAlignment(); - int persistentBits = GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT; - glNamedBufferStorage(objectBuffer, objectStride * maxObjectCount, persistentBits); - glNamedBufferStorage(targetBuffer, 4 * maxObjectCount, 0); - glNamedBufferStorage(batchBuffer, 4 * maxObjectCount, persistentBits); - glNamedBufferStorage(drawBuffer, DRAW_COMMAND_STRIDE * maxBatchCount, GL_DYNAMIC_STORAGE_BIT); - glNamedBufferStorage(debugBuffer, 4 * maxObjectCount, 0); + buffers = new IndirectBuffers(objectStride); + buffers.createBuffers(); + buffers.createObjectStorage(64 * 64 * 64); + buffers.createDrawStorage(64); - int mapBits = persistentBits | GL_MAP_FLUSH_EXPLICIT_BIT; - objectClientStorage = nglMapNamedBufferRange(objectBuffer, 0, objectStride * maxObjectCount, mapBits); - batchIDClientStorage = nglMapNamedBufferRange(batchBuffer, 0, 4 * maxObjectCount, mapBits); + meshPool = new IndirectMeshPool(vertexType, 1024); vertexArray = glCreateVertexArrays(); @@ -95,14 +59,15 @@ public class IndirectList { .quads2Tris(2048).buffer.handle(); setupVertexArray(); - compute = ComputeCullerCompiler.INSTANCE.get(Components.Files.ORIENTED_INDIRECT); - draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(Formats.BLOCK, Materials.BELL, Components.Files.ORIENTED_INDIRECT, Components.WORLD, Components.INDIRECT)); + var indirectShader = this.structType.getIndirectShader(); + compute = ComputeCullerCompiler.INSTANCE.get(indirectShader); + draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.CHEST, indirectShader, Components.WORLD, Components.INDIRECT)); } private void setupVertexArray() { glVertexArrayElementBuffer(vertexArray, elementBuffer); - var meshLayout = Formats.BLOCK.getLayout(); + var meshLayout = vertexType.getLayout(); var meshAttribs = meshLayout.getAttributeCount(); var attributes = meshLayout.getAttributes(); @@ -118,17 +83,23 @@ public class IndirectList { } } - public void add(Mesh mesh, IndirectInstancer instancer) { - batches.add(new Batch<>(instancer, meshPool.alloc(mesh))); + public void add(IndirectInstancer instancer, Material material, Mesh mesh) { + batches.add(new Batch<>(instancer, material, meshPool.alloc(mesh))); } - void submit() { + void submit(RenderStage stage) { + if (batches.isEmpty()) { + return; + } int instanceCountThisFrame = calculateTotalInstanceCount(); if (instanceCountThisFrame == 0) { return; } + // TODO: Sort meshes by material and draw many contiguous sections of the draw indirect buffer, + // adjusting uniforms/textures accordingly + buffers.updateCounts(instanceCountThisFrame, batches.size()); meshPool.uploadAll(); uploadInstanceData(); uploadIndirectCommands(); @@ -136,39 +107,38 @@ public class IndirectList { UniformBuffer.getInstance().sync(); dispatchCompute(instanceCountThisFrame); - issueMemoryBarrier(); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); dispatchDraw(); } private void dispatchDraw() { draw.bind(); - Materials.BELL.setup(); glVertexArrayElementBuffer(vertexArray, elementBuffer); glBindVertexArray(vertexArray); - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, drawBuffer); - glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, DRAW_COMMAND_OFFSET, batches.size(), (int) DRAW_COMMAND_STRIDE); - Materials.BELL.clear(); - } + buffers.bindIndirectBuffer(); - private static void issueMemoryBarrier() { - glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + final int stride = (int) IndirectBuffers.DRAW_COMMAND_STRIDE; + long offset = 0; + for (Batch batch : batches) { + + batch.material.setup(); + glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, offset, 1, stride); + batch.material.clear(); + offset += stride; + } } private void dispatchCompute(int instanceCount) { compute.bind(); - glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, objectBuffer, 0, instanceCount * objectStride); - glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, targetBuffer, 0, instanceCount * 4L); - glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, batchBuffer, 0, instanceCount * 4L); - glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, drawBuffer, 0, batches.size() * DRAW_COMMAND_STRIDE); - glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 4, debugBuffer, 0, instanceCount * 4L); + buffers.bindAll(); - var groupCount = (instanceCount + 31) >> 5; // ceil(totalInstanceCount / 32) + var groupCount = (instanceCount + 31) >> 5; // ceil(instanceCount / 32) glDispatchCompute(groupCount, 1, 1); } private void uploadInstanceData() { - long objectPtr = objectClientStorage; - long batchIDPtr = batchIDClientStorage; + long objectPtr = buffers.objectPtr; + long batchIDPtr = buffers.batchPtr; int baseInstance = 0; int batchID = 0; for (var batch : batches) { @@ -181,27 +151,23 @@ public class IndirectList { // write batchID MemoryUtil.memPutInt(batchIDPtr, batchID); - batchIDPtr += 4; + batchIDPtr += IndirectBuffers.INT_SIZE; } baseInstance += batch.instancer.instanceCount; batchID++; } - glFlushMappedNamedBufferRange(objectBuffer, 0, objectPtr - objectClientStorage); - glFlushMappedNamedBufferRange(batchBuffer, 0, batchIDPtr - batchIDClientStorage); + buffers.flushObjects(objectPtr - buffers.objectPtr); + buffers.flushBatchIDs(batchIDPtr - buffers.batchPtr); } private void uploadIndirectCommands() { - try (var stack = MemoryStack.stackPush()) { - long size = batches.size() * DRAW_COMMAND_STRIDE; - long basePtr = stack.nmalloc((int) size); - long writePtr = basePtr; - for (Batch batch : batches) { - batch.writeIndirectCommand(writePtr); - writePtr += DRAW_COMMAND_STRIDE; - } - nglNamedBufferSubData(drawBuffer, 0, size, basePtr); + long writePtr = buffers.drawPtr; + for (Batch batch : batches) { + batch.writeIndirectCommand(writePtr); + writePtr += IndirectBuffers.DRAW_COMMAND_STRIDE; } + buffers.flushDrawCommands(writePtr - buffers.drawPtr); } private int calculateTotalInstanceCount() { @@ -216,10 +182,12 @@ public class IndirectList { private static final class Batch { final IndirectInstancer instancer; final IndirectMeshPool.BufferedMesh mesh; - int baseInstance; + final Material material; + int baseInstance = -1; - private Batch(IndirectInstancer instancer, IndirectMeshPool.BufferedMesh mesh) { + private Batch(IndirectInstancer instancer, Material material, IndirectMeshPool.BufferedMesh mesh) { this.instancer = instancer; + this.material = material; this.mesh = mesh; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java index b88fb380e..1ae8f1722 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java @@ -77,6 +77,8 @@ public class IndirectMeshPool { baseVertex += model.mesh.getVertexCount(); } + clientStorage.rewind(); + glNamedBufferSubData(vbo, 0, clientStorage); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java index e3faeb739..dfa76f2c8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java @@ -21,7 +21,7 @@ public class InstancedModel { for (var entry : materialMeshMap.entrySet()) { var material = entry.getKey(); var mesh = entry.getValue(); - renderLists.add(material.getRenderStage(), type, mesh, instancer); + renderLists.add(instancer, material, mesh); return; // TODO: support multiple meshes per model } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java index 38ecfc8f6..1d823e0fb 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java @@ -1,40 +1,24 @@ package com.jozufozu.flywheel.backend.instancing.indirect; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; import java.util.HashMap; -import java.util.List; import java.util.Map; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ListMultimap; -import com.jozufozu.flywheel.api.RenderStage; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.core.model.Mesh; +import com.jozufozu.flywheel.util.Pair; public class RenderLists { - public final Map, IndirectList>> renderLists = new EnumMap<>(RenderStage.class); - - public Collection> get(RenderStage stage) { - var renderList = renderLists.get(stage); - if (renderList == null) { - return Collections.emptyList(); - } - return renderList.values(); - } + public final Map, VertexType>, IndirectList> lists = new HashMap<>(); @SuppressWarnings("unchecked") - public void add(RenderStage stage, StructType type, Mesh mesh, IndirectInstancer instancer) { - var indirectList = (IndirectList) renderLists.computeIfAbsent(stage, $ -> new HashMap<>()) - .computeIfAbsent(type, IndirectList::new); + public void add(IndirectInstancer instancer, Material material, Mesh mesh) { + var indirectList = (IndirectList) lists.computeIfAbsent(Pair.of(instancer.structType, mesh.getVertexType()), + p -> new IndirectList<>(p.first(), p.second())); - indirectList.add(mesh, instancer); + indirectList.add(instancer, material, mesh); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedStorageWriter.java b/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedStorageWriter.java new file mode 100644 index 000000000..70619bdd1 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedStorageWriter.java @@ -0,0 +1,31 @@ +package com.jozufozu.flywheel.core.structs.model; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.struct.StorageBufferWriter; +import com.jozufozu.flywheel.util.extension.MatrixExtension; + +public class TransformedStorageWriter implements StorageBufferWriter { + + public static final TransformedStorageWriter INSTANCE = new TransformedStorageWriter(); + + private TransformedStorageWriter() { + } + + @Override + public void write(long ptr, TransformedPart instance) { + MatrixExtension.writeUnsafe(instance.model, ptr); + MatrixExtension.writeUnsafe(instance.normal, ptr + 64); + MemoryUtil.memPutByte(ptr + 112, instance.r); + MemoryUtil.memPutByte(ptr + 113, instance.g); + MemoryUtil.memPutByte(ptr + 114, instance.b); + MemoryUtil.memPutByte(ptr + 115, instance.a); + MemoryUtil.memPutShort(ptr + 116, instance.skyLight); + MemoryUtil.memPutShort(ptr + 118, instance.blockLight); + } + + @Override + public int getAlignment() { + return 128; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedType.java b/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedType.java index c754bc1ab..86b727125 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedType.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/model/TransformedType.java @@ -36,7 +36,7 @@ public class TransformedType implements StructType { @Override public StorageBufferWriter getStorageBufferWriter() { - return null; // TODO + return TransformedStorageWriter.INSTANCE; } @Override @@ -44,6 +44,11 @@ public class TransformedType implements StructType { return Components.Files.TRANSFORMED; } + @Override + public FileResolution getIndirectShader() { + return Components.Files.TRANSFORMED_INDIRECT; + } + @Override public VertexTransformer getVertexTransformer() { return (vertexList, struct, level) -> { diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java index be6fbfa28..5bf956fa1 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java @@ -47,6 +47,11 @@ public class OrientedType implements StructType { return Components.Files.ORIENTED; } + @Override + public FileResolution getIndirectShader() { + return Components.Files.ORIENTED_INDIRECT; + } + @Override public VertexTransformer getVertexTransformer() { return (vertexList, struct, level) -> { diff --git a/src/main/java/com/jozufozu/flywheel/util/extension/MatrixExtension.java b/src/main/java/com/jozufozu/flywheel/util/extension/MatrixExtension.java index 4dd0ac116..d1e7253af 100644 --- a/src/main/java/com/jozufozu/flywheel/util/extension/MatrixExtension.java +++ b/src/main/java/com/jozufozu/flywheel/util/extension/MatrixExtension.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.util.extension; import java.nio.ByteBuffer; +import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; /** @@ -24,4 +25,8 @@ public interface MatrixExtension { static void writeUnsafe(Matrix4f matrix, long ptr) { ((MatrixExtension) (Object) matrix).flywheel$writeUnsafe(ptr); } + + static void writeUnsafe(Matrix3f matrix, long ptr) { + ((MatrixExtension) (Object) matrix).flywheel$writeUnsafe(ptr); + } } diff --git a/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl index 1ae709e24..0fbf19511 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl +++ b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl @@ -1,6 +1,6 @@ #use "flywheel:api/vertex.glsl" -#define FLW_INSTANCE_STRUCT Instance +#define FLW_INSTANCE_STRUCT Instance struct Instance { mat4 pose; mat3 normal; diff --git a/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl index 170df512a..b1706edf0 100644 --- a/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl @@ -31,24 +31,11 @@ layout(std430, binding = 3) restrict buffer DrawCommands { MeshDrawCommand drawCommands[]; }; -layout(std430, binding = 4) restrict writeonly buffer DebugVisibility { - uint objectVisibilityBits[]; -}; - // 83 - 27 = 56 spirv instruction results bool testSphere(vec3 center, float radius) { bvec4 xyInside = greaterThanEqual(fma(flw_planes.xyX, center.xxxx, fma(flw_planes.xyY, center.yyyy, fma(flw_planes.xyZ, center.zzzz, flw_planes.xyW))), -radius.xxxx); bvec2 zInside = greaterThanEqual(fma(flw_planes.zX, center.xx, fma(flw_planes.zY, center.yy, fma(flw_planes.zZ, center.zz, flw_planes.zW))), -radius.xx); - uint debug = uint(xyInside.x); - debug |= uint(xyInside.y) << 1; - debug |= uint(xyInside.z) << 2; - debug |= uint(xyInside.w) << 3; - debug |= uint(zInside.x) << 4; - debug |= uint(zInside.y) << 5; - - objectVisibilityBits[flw_objectID] = debug; - return all(xyInside) && all(zInside); } From 2910e336260e280290f28d8255522f594192c8ef Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 16 Aug 2022 23:43:23 -0700 Subject: [PATCH 08/13] Sparse instance updates - Use MemoryBlock in IndirectBuffers --- .../instancing/indirect/IndirectBuffers.java | 50 +++++---- .../instancing/indirect/IndirectEngine.java | 9 +- .../instancing/indirect/IndirectFactory.java | 37 ++----- .../indirect/IndirectInstancer.java | 5 +- .../instancing/indirect/IndirectList.java | 104 ++++++++++++------ ...InstancedModel.java => IndirectModel.java} | 6 +- 6 files changed, 115 insertions(+), 96 deletions(-) rename src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/{InstancedModel.java => IndirectModel.java} (84%) diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java index 6f32d02c4..3fade4ff6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java @@ -5,6 +5,8 @@ import static org.lwjgl.opengl.GL46.*; import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.Pointer; +import com.jozufozu.flywheel.backend.memory.MemoryBlock; + public class IndirectBuffers { public static final int BUFFER_COUNT = 4; public static final long INT_SIZE = Integer.BYTES; @@ -30,7 +32,7 @@ public class IndirectBuffers { private static final long BATCH_SIZE_OFFSET = TARGET_SIZE_OFFSET + PTR_SIZE; private static final long DRAW_SIZE_OFFSET = BATCH_SIZE_OFFSET + PTR_SIZE; - final long buffers; + final MemoryBlock buffers; final long objectStride; int object; int target; @@ -46,21 +48,16 @@ public class IndirectBuffers { IndirectBuffers(long objectStride) { this.objectStride = objectStride; - this.buffers = MemoryUtil.nmemAlloc(BUFFERS_SIZE_BYTES); - - if (this.buffers == MemoryUtil.NULL) { - throw new OutOfMemoryError(); - } - - MemoryUtil.memSet(this.buffers, 0, BUFFERS_SIZE_BYTES); + this.buffers = MemoryBlock.calloc(BUFFERS_SIZE_BYTES, 1); } void createBuffers() { - nglCreateBuffers(4, buffers); - object = MemoryUtil.memGetInt(buffers); - target = MemoryUtil.memGetInt(buffers + 4); - batch = MemoryUtil.memGetInt(buffers + 8); - draw = MemoryUtil.memGetInt(buffers + 12); + final long ptr = buffers.ptr(); + nglCreateBuffers(4, ptr); + object = MemoryUtil.memGetInt(ptr); + target = MemoryUtil.memGetInt(ptr + 4); + batch = MemoryUtil.memGetInt(ptr + 8); + draw = MemoryUtil.memGetInt(ptr + 12); } void updateCounts(int objectCount, int drawCount) { @@ -72,14 +69,15 @@ public class IndirectBuffers { createDrawStorage(drawCount); } - long objectSize = objectStride * objectCount; - long targetSize = INT_SIZE * objectCount; - long drawSize = DRAW_COMMAND_STRIDE * drawCount; + final long objectSize = objectStride * objectCount; + final long targetSize = INT_SIZE * objectCount; + final long drawSize = DRAW_COMMAND_STRIDE * drawCount; - MemoryUtil.memPutAddress(buffers + OBJECT_SIZE_OFFSET, objectSize); - MemoryUtil.memPutAddress(buffers + TARGET_SIZE_OFFSET, targetSize); - MemoryUtil.memPutAddress(buffers + BATCH_SIZE_OFFSET, targetSize); - MemoryUtil.memPutAddress(buffers + DRAW_SIZE_OFFSET, drawSize); + final long ptr = buffers.ptr(); + MemoryUtil.memPutAddress(ptr + OBJECT_SIZE_OFFSET, objectSize); + MemoryUtil.memPutAddress(ptr + TARGET_SIZE_OFFSET, targetSize); + MemoryUtil.memPutAddress(ptr + BATCH_SIZE_OFFSET, targetSize); + MemoryUtil.memPutAddress(ptr + DRAW_SIZE_OFFSET, drawSize); } void createObjectStorage(int objectCount) { @@ -112,7 +110,12 @@ public class IndirectBuffers { } private void bindN(int bufferCount) { - nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, bufferCount, buffers, buffers + OFFSET_OFFSET, buffers + SIZE_OFFSET); + if (bufferCount > BUFFER_COUNT) { + throw new IllegalArgumentException("Can't bind more than " + BUFFER_COUNT + " buffers"); + } + + final long ptr = buffers.ptr(); + nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, bufferCount, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET); } void bindIndirectBuffer() { @@ -131,4 +134,9 @@ public class IndirectBuffers { nglNamedBufferSubData(draw, 0, length, drawPtr); // glFlushMappedNamedBufferRange(this.draw, 0, length); } + + public void delete() { + nglDeleteBuffers(BUFFER_COUNT, buffers.ptr()); + buffers.free(); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java index 5b4a61d2e..3722d7ee1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -10,18 +10,13 @@ import org.lwjgl.opengl.GL32; import com.jozufozu.flywheel.api.RenderStage; import com.jozufozu.flywheel.api.instancer.InstancedPart; -import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.gl.GlTextureUnit; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.api.context.ContextShader; -import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; -import com.jozufozu.flywheel.core.source.FileResolution; -import com.jozufozu.flywheel.core.uniform.UniformBuffer; import com.jozufozu.flywheel.util.WeakHashSet; import com.mojang.blaze3d.systems.RenderSystem; @@ -42,7 +37,7 @@ public class IndirectEngine implements Engine { protected final Map, IndirectFactory> factories = new HashMap<>(); - protected final List> uninitializedModels = new ArrayList<>(); + protected final List> uninitializedModels = new ArrayList<>(); protected final RenderLists renderLists = new RenderLists(); /** @@ -102,6 +97,8 @@ public class IndirectEngine implements Engine { factories.values() .forEach(IndirectFactory::delete); + renderLists.lists.values().forEach(IndirectList::delete); + factories.clear(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectFactory.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectFactory.java index cf2f64912..d0bfaa3b8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectFactory.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectFactory.java @@ -13,11 +13,11 @@ import com.jozufozu.flywheel.core.model.Model; public class IndirectFactory implements InstancerFactory { - protected final Map> models = new HashMap<>(); + protected final Map> models = new HashMap<>(); protected final StructType type; - private final Consumer> creationListener; + private final Consumer> creationListener; - public IndirectFactory(StructType type, Consumer> creationListener) { + public IndirectFactory(StructType type, Consumer> creationListener) { this.type = type; this.creationListener = creationListener; } @@ -27,23 +27,8 @@ public class IndirectFactory implements InstancerFactor return models.computeIfAbsent(modelKey, this::createInstancer).getInstancer(); } - public int getInstanceCount() { - return models.values() - .stream() - .map(InstancedModel::getInstancer) - .mapToInt(AbstractInstancer::getInstanceCount) - .sum(); - } - - public int getVertexCount() { - return models.values() - .stream() - .mapToInt(InstancedModel::getVertexCount) - .sum(); - } - public void delete() { - models.values().forEach(InstancedModel::delete); + models.values().forEach(IndirectModel::delete); models.clear(); } @@ -53,21 +38,13 @@ public class IndirectFactory implements InstancerFactor public void clear() { models.values() .stream() - .map(InstancedModel::getInstancer) + .map(IndirectModel::getInstancer) .forEach(AbstractInstancer::clear); } - private InstancedModel createInstancer(Model model) { - var instancer = new InstancedModel<>(type, model); + private IndirectModel createInstancer(Model model) { + var instancer = new IndirectModel<>(type, model); this.creationListener.accept(instancer); return instancer; } - -// private void bindInstanceAttributes(GlVertexArray vao) { -// vao.bindAttributes(this.vbo, this.attributeBaseIndex, this.instanceFormat, 0L); -// -// for (int i = 0; i < this.instanceFormat.getAttributeCount(); i++) { -// vao.setAttributeDivisor(this.attributeBaseIndex + i, 1); -// } -// } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java index 561bfe3db..2a2cef2bb 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java @@ -2,7 +2,6 @@ package com.jozufozu.flywheel.backend.instancing.indirect; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.core.layout.BufferLayout; @@ -10,12 +9,12 @@ public class IndirectInstancer extends AbstractInstance public final BufferLayout instanceFormat; public final StructType structType; - public final InstancedModel parent; + public final IndirectModel parent; int instanceCount = 0; boolean anyToUpdate; - public IndirectInstancer(InstancedModel parent, StructType type) { + public IndirectInstancer(IndirectModel parent, StructType type) { super(type); this.parent = parent; this.instanceFormat = type.getLayout(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java index 90bfe5777..264b644b2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java @@ -5,7 +5,6 @@ import static org.lwjgl.opengl.GL46.*; import java.util.ArrayList; import java.util.List; -import org.jetbrains.annotations.NotNull; import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.RenderStage; @@ -27,7 +26,6 @@ public class IndirectList { final StorageBufferWriter storageBufferWriter; final GlProgram compute; final GlProgram draw; - private final StructType structType; private final VertexType vertexType; private final long objectStride; @@ -38,12 +36,11 @@ public class IndirectList { int vertexArray; - final List> batches = new ArrayList<>(); + final List batches = new ArrayList<>(); IndirectList(StructType structType, VertexType vertexType) { - this.structType = structType; this.vertexType = vertexType; - storageBufferWriter = this.structType.getStorageBufferWriter(); + storageBufferWriter = structType.getStorageBufferWriter(); objectStride = storageBufferWriter.getAlignment(); buffers = new IndirectBuffers(objectStride); @@ -59,7 +56,7 @@ public class IndirectList { .quads2Tris(2048).buffer.handle(); setupVertexArray(); - var indirectShader = this.structType.getIndirectShader(); + var indirectShader = structType.getIndirectShader(); compute = ComputeCullerCompiler.INSTANCE.get(indirectShader); draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.CHEST, indirectShader, Components.WORLD, Components.INDIRECT)); } @@ -84,14 +81,14 @@ public class IndirectList { } public void add(IndirectInstancer instancer, Material material, Mesh mesh) { - batches.add(new Batch<>(instancer, material, meshPool.alloc(mesh))); + batches.add(new Batch(instancer, material, meshPool.alloc(mesh))); } void submit(RenderStage stage) { if (batches.isEmpty()) { return; } - int instanceCountThisFrame = calculateTotalInstanceCount(); + int instanceCountThisFrame = calculateTotalInstanceCountAndPrepareBatches(); if (instanceCountThisFrame == 0) { return; @@ -119,7 +116,7 @@ public class IndirectList { final int stride = (int) IndirectBuffers.DRAW_COMMAND_STRIDE; long offset = 0; - for (Batch batch : batches) { + for (var batch : batches) { batch.material.setup(); glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, offset, 1, stride); @@ -139,22 +136,14 @@ public class IndirectList { private void uploadInstanceData() { long objectPtr = buffers.objectPtr; long batchIDPtr = buffers.batchPtr; - int baseInstance = 0; - int batchID = 0; - for (var batch : batches) { - batch.baseInstance = baseInstance; - var instancer = batch.instancer; - for (T t : instancer.getAll()) { - // write object - storageBufferWriter.write(objectPtr, t); - objectPtr += objectStride; - // write batchID - MemoryUtil.memPutInt(batchIDPtr, batchID); - batchIDPtr += IndirectBuffers.INT_SIZE; - } - baseInstance += batch.instancer.instanceCount; - batchID++; + for (int i = 0, batchesSize = batches.size(); i < batchesSize; i++) { + var batch = batches.get(i); + var instanceCount = batch.instancer.getInstanceCount(); + batch.write(objectPtr, batchIDPtr, i); + + objectPtr += instanceCount * objectStride; + batchIDPtr += instanceCount * IndirectBuffers.INT_SIZE; } buffers.flushObjects(objectPtr - buffers.objectPtr); @@ -163,34 +152,85 @@ public class IndirectList { private void uploadIndirectCommands() { long writePtr = buffers.drawPtr; - for (Batch batch : batches) { + for (var batch : batches) { batch.writeIndirectCommand(writePtr); writePtr += IndirectBuffers.DRAW_COMMAND_STRIDE; } buffers.flushDrawCommands(writePtr - buffers.drawPtr); } - private int calculateTotalInstanceCount() { - int total = 0; - for (Batch batch : batches) { - batch.instancer.update(); - total += batch.instancer.instanceCount; + private int calculateTotalInstanceCountAndPrepareBatches() { + int baseInstance = 0; + for (var batch : batches) { + batch.prepare(baseInstance); + baseInstance += batch.instancer.instanceCount; } - return total; + return baseInstance; } - private static final class Batch { + public void delete() { + glDeleteVertexArrays(vertexArray); + buffers.delete(); + meshPool.delete(); + } + + private final class Batch { final IndirectInstancer instancer; final IndirectMeshPool.BufferedMesh mesh; final Material material; int baseInstance = -1; + boolean needsFullWrite = true; + private Batch(IndirectInstancer instancer, Material material, IndirectMeshPool.BufferedMesh mesh) { this.instancer = instancer; this.material = material; this.mesh = mesh; } + public void prepare(int baseInstance) { + instancer.update(); + if (baseInstance == this.baseInstance) { + needsFullWrite = false; + return; + } + this.baseInstance = baseInstance; + needsFullWrite = true; + } + + private void write(long objectPtr, long batchIDPtr, int batchID) { + if (needsFullWrite) { + writeFull(objectPtr, batchIDPtr, batchID); + } else if (instancer.anyToUpdate) { + writeSparse(objectPtr, batchIDPtr, batchID); + } + instancer.anyToUpdate = false; + } + + private void writeSparse(long objectPtr, long batchIDPtr, int batchID) { + var all = instancer.getAll(); + for (int i = 0; i < all.size(); i++) { + final var element = all.get(i); + if (element.checkDirtyAndClear()) { + storageBufferWriter.write(objectPtr + i * objectStride, element); + + MemoryUtil.memPutInt(batchIDPtr + i * IndirectBuffers.INT_SIZE, batchID); + } + } + } + + private void writeFull(long objectPtr, long batchIDPtr, int batchID) { + for (var object : this.instancer.getAll()) { + // write object + storageBufferWriter.write(objectPtr, object); + objectPtr += objectStride; + + // write batchID + MemoryUtil.memPutInt(batchIDPtr, batchID); + batchIDPtr += IndirectBuffers.INT_SIZE; + } + } + public void writeIndirectCommand(long ptr) { var boundingSphere = mesh.mesh.getBoundingSphere(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java similarity index 84% rename from src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java rename to src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java index dfa76f2c8..88e5107ea 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/InstancedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java @@ -4,16 +4,14 @@ import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.core.model.Model; -public class InstancedModel { +public class IndirectModel { private final Model model; - private final StructType type; private final IndirectInstancer instancer; - public InstancedModel(StructType type, Model model) { + public IndirectModel(StructType type, Model model) { this.model = model; this.instancer = new IndirectInstancer<>(this, type); - this.type = type; } public void init(RenderLists renderLists) { From 9b3c4d4992abe31f576c8b351d411407e7a2f83c Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 20 Aug 2022 20:50:26 -0700 Subject: [PATCH 09/13] Dynamically indirect - Indirect engine now only allocates as much memory as it needs --- .../instancing/indirect/IndirectBuffers.java | 66 +++++++++++++++++-- .../instancing/indirect/IndirectEngine.java | 4 -- .../instancing/indirect/IndirectList.java | 4 +- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java index 3fade4ff6..bde48b05a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java @@ -5,6 +5,7 @@ import static org.lwjgl.opengl.GL46.*; import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.Pointer; +import com.jozufozu.flywheel.backend.memory.FlwMemoryTracker; import com.jozufozu.flywheel.backend.memory.MemoryBlock; public class IndirectBuffers { @@ -43,8 +44,11 @@ public class IndirectBuffers { long batchPtr; long drawPtr; - int maxObjectCount; - int maxDrawCount; + int maxObjectCount = 0; + int maxDrawCount = 0; + + float objectGrowthFactor = 2f; + float drawGrowthFactor = 2f; IndirectBuffers(long objectStride) { this.objectStride = objectStride; @@ -63,10 +67,18 @@ public class IndirectBuffers { void updateCounts(int objectCount, int drawCount) { if (objectCount > maxObjectCount) { - createObjectStorage(objectCount); + var newObjectCount = maxObjectCount; + while (newObjectCount <= objectCount) { + newObjectCount *= objectGrowthFactor; + } + createObjectStorage(newObjectCount); } if (drawCount > maxDrawCount) { - createDrawStorage(drawCount); + var newDrawCount = maxDrawCount; + while (newDrawCount <= drawCount) { + newDrawCount *= drawGrowthFactor; + } + createDrawStorage(newDrawCount); } final long objectSize = objectStride * objectCount; @@ -81,24 +93,62 @@ public class IndirectBuffers { } void createObjectStorage(int objectCount) { + freeObjectStogare(); var objectSize = objectStride * objectCount; var targetSize = INT_SIZE * objectCount; - glNamedBufferStorage(object, objectSize, PERSISTENT_BITS); - glNamedBufferStorage(target, targetSize, GPU_ONLY_BITS); - glNamedBufferStorage(batch, targetSize, PERSISTENT_BITS); + if (maxObjectCount > 0) { + var ptr = buffers.ptr(); + nglCreateBuffers(3, ptr); + + int objectNew = MemoryUtil.memGetInt(ptr); + int targetNew = MemoryUtil.memGetInt(ptr + 4); + int batchNew = MemoryUtil.memGetInt(ptr + 8); + + glNamedBufferStorage(objectNew, objectSize, PERSISTENT_BITS); + glNamedBufferStorage(targetNew, targetSize, GPU_ONLY_BITS); + glNamedBufferStorage(batchNew, targetSize, PERSISTENT_BITS); + + glCopyNamedBufferSubData(object, objectNew, 0, 0, objectStride * maxObjectCount); + glCopyNamedBufferSubData(target, targetNew, 0, 0, INT_SIZE * maxObjectCount); + glCopyNamedBufferSubData(batch, batchNew, 0, 0, INT_SIZE * maxObjectCount); + + glDeleteBuffers(object); + glDeleteBuffers(target); + glDeleteBuffers(batch); + + object = objectNew; + target = targetNew; + batch = batchNew; + } else { + glNamedBufferStorage(object, objectSize, PERSISTENT_BITS); + glNamedBufferStorage(target, targetSize, GPU_ONLY_BITS); + glNamedBufferStorage(batch, targetSize, PERSISTENT_BITS); + } objectPtr = nglMapNamedBufferRange(object, 0, objectSize, MAP_BITS); batchPtr = nglMapNamedBufferRange(batch, 0, targetSize, MAP_BITS); maxObjectCount = objectCount; + + FlwMemoryTracker._allocGPUMemory(maxObjectCount * objectStride + maxObjectCount * INT_SIZE); } void createDrawStorage(int drawCount) { + freeDrawStorage(); var drawSize = DRAW_COMMAND_STRIDE * drawCount; glNamedBufferStorage(draw, drawSize, SUB_DATA_BITS); drawPtr = MemoryUtil.nmemAlloc(drawSize); // drawPtr = nglMapNamedBufferRange(draw, 0, drawSize, MAP_BITS); maxDrawCount = drawCount; + FlwMemoryTracker._allocGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE); + } + + private void freeObjectStogare() { + FlwMemoryTracker._freeGPUMemory(maxObjectCount * objectStride + maxObjectCount * INT_SIZE); + } + + private void freeDrawStorage() { + FlwMemoryTracker._freeGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE); } public void bindAll() { @@ -138,5 +188,7 @@ public class IndirectBuffers { public void delete() { nglDeleteBuffers(BUFFER_COUNT, buffers.ptr()); buffers.free(); + freeObjectStogare(); + freeDrawStorage(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java index 3722d7ee1..a48c46954 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -88,10 +88,6 @@ public class IndirectEngine implements Engine { RenderSystem.enableCull(); } - public void clearAll() { - factories.values().forEach(IndirectFactory::clear); - } - @Override public void delete() { factories.values() diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java index 264b644b2..7109e4d83 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java @@ -45,8 +45,8 @@ public class IndirectList { objectStride = storageBufferWriter.getAlignment(); buffers = new IndirectBuffers(objectStride); buffers.createBuffers(); - buffers.createObjectStorage(64 * 64 * 64); - buffers.createDrawStorage(64); + buffers.createObjectStorage(128); + buffers.createDrawStorage(16); meshPool = new IndirectMeshPool(vertexType, 1024); From 564b0996f92e16c64c54eb3fa72589a4882e6378 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 21 Aug 2022 16:47:15 -0700 Subject: [PATCH 10/13] Indirectly packed - Use glsl structs to pack vec/mat data in ssbos - Support RenderStages - Dynamically sized draw storage --- .editorconfig | 6 +- .../instancing/indirect/IndirectBuffers.java | 37 ++- .../indirect/IndirectCullingGroup.java | 194 ++++++++++++++ .../instancing/indirect/IndirectDraw.java | 52 ++++ ...derLists.java => IndirectDrawManager.java} | 9 +- .../instancing/indirect/IndirectDrawSet.java | 54 ++++ .../instancing/indirect/IndirectEngine.java | 22 +- .../indirect/IndirectInstancer.java | 31 ++- .../instancing/indirect/IndirectList.java | 246 ------------------ .../instancing/indirect/IndirectModel.java | 4 +- .../flywheel/core/model/BlockMesh.java | 0 .../oriented/OrientedStorageWriter.java | 20 +- .../transformed/TransformedStorageWriter.java | 14 +- .../core/uniform/FrustumProvider.java | 3 - .../flywheel/instance/oriented_indirect.glsl | 24 +- .../instance/transformed_indirect.glsl | 13 +- .../assets/flywheel/flywheel/util/types.glsl | 48 +++- 17 files changed, 471 insertions(+), 306 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java rename src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/{RenderLists.java => IndirectDrawManager.java} (58%) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/model/BlockMesh.java diff --git a/.editorconfig b/.editorconfig index 1eeca6433..2c468ead4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,7 +4,7 @@ root = true [*] indent_style = space indent_size = 4 -continuation_indent_size = 8 +ij_continuation_indent_size = 8 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true @@ -16,6 +16,10 @@ indent_size = 2 [*.java] indent_style = tab +ij_java_blank_lines_before_class_end = 0 +ij_java_blank_lines_after_anonymous_class_header = 0 +ij_java_blank_lines_after_class_header = 0 +ij_java_blank_lines_before_method_body = 0 ij_java_else_on_new_line = false ij_continuation_indent_size = 8 ij_java_class_count_to_use_import_on_demand = 99 diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java index bde48b05a..dae12ac7b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java @@ -1,6 +1,22 @@ package com.jozufozu.flywheel.backend.instancing.indirect; -import static org.lwjgl.opengl.GL46.*; +import static org.lwjgl.opengl.GL46.GL_DRAW_INDIRECT_BUFFER; +import static org.lwjgl.opengl.GL46.GL_DYNAMIC_STORAGE_BIT; +import static org.lwjgl.opengl.GL46.GL_MAP_FLUSH_EXPLICIT_BIT; +import static org.lwjgl.opengl.GL46.GL_MAP_PERSISTENT_BIT; +import static org.lwjgl.opengl.GL46.GL_MAP_WRITE_BIT; +import static org.lwjgl.opengl.GL46.GL_SHADER_STORAGE_BUFFER; +import static org.lwjgl.opengl.GL46.glBindBuffer; +import static org.lwjgl.opengl.GL46.glCopyNamedBufferSubData; +import static org.lwjgl.opengl.GL46.glDeleteBuffers; +import static org.lwjgl.opengl.GL46.glFlushMappedNamedBufferRange; +import static org.lwjgl.opengl.GL46.glGenBuffers; +import static org.lwjgl.opengl.GL46.glNamedBufferStorage; +import static org.lwjgl.opengl.GL46.nglBindBuffersRange; +import static org.lwjgl.opengl.GL46.nglCreateBuffers; +import static org.lwjgl.opengl.GL46.nglDeleteBuffers; +import static org.lwjgl.opengl.GL46.nglMapNamedBufferRange; +import static org.lwjgl.opengl.GL46.nglNamedBufferSubData; import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.Pointer; @@ -135,10 +151,23 @@ public class IndirectBuffers { void createDrawStorage(int drawCount) { freeDrawStorage(); + var drawSize = DRAW_COMMAND_STRIDE * drawCount; - glNamedBufferStorage(draw, drawSize, SUB_DATA_BITS); - drawPtr = MemoryUtil.nmemAlloc(drawSize); - // drawPtr = nglMapNamedBufferRange(draw, 0, drawSize, MAP_BITS); + if (maxDrawCount > 0) { + int drawNew = glGenBuffers(); + + glNamedBufferStorage(drawNew, drawSize, SUB_DATA_BITS); + + glDeleteBuffers(draw); + + MemoryUtil.memPutInt(buffers.ptr() + INT_SIZE * 3, drawNew); + draw = drawNew; + drawPtr = MemoryUtil.nmemRealloc(drawPtr, drawSize); + } else { + + glNamedBufferStorage(draw, drawSize, SUB_DATA_BITS); + drawPtr = MemoryUtil.nmemAlloc(drawSize); + } maxDrawCount = drawCount; FlwMemoryTracker._allocGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java new file mode 100644 index 000000000..05794682f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java @@ -0,0 +1,194 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import static org.lwjgl.opengl.GL42.GL_COMMAND_BARRIER_BIT; +import static org.lwjgl.opengl.GL42.glMemoryBarrier; +import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT; +import static org.lwjgl.opengl.GL46.glBindVertexArray; +import static org.lwjgl.opengl.GL46.glCreateVertexArrays; +import static org.lwjgl.opengl.GL46.glDeleteVertexArrays; +import static org.lwjgl.opengl.GL46.glDispatchCompute; +import static org.lwjgl.opengl.GL46.glEnableVertexArrayAttrib; +import static org.lwjgl.opengl.GL46.glVertexArrayElementBuffer; +import static org.lwjgl.opengl.GL46.glVertexArrayVertexBuffer; + +import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.struct.StorageBufferWriter; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; +import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.Materials; +import com.jozufozu.flywheel.core.QuadConverter; +import com.jozufozu.flywheel.core.uniform.UniformBuffer; + +public class IndirectCullingGroup { + + private static final int BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT; + + final StorageBufferWriter storageBufferWriter; + final GlProgram compute; + final GlProgram draw; + private final VertexType vertexType; + private final long objectStride; + + final IndirectBuffers buffers; + + final IndirectMeshPool meshPool; + private final int elementBuffer; + + int vertexArray; + + final IndirectDrawSet drawSet = new IndirectDrawSet<>(); + + private boolean hasCulledThisFrame; + private boolean needsMemoryBarrier; + private int instanceCountThisFrame; + + IndirectCullingGroup(StructType structType, VertexType vertexType) { + this.vertexType = vertexType; + storageBufferWriter = structType.getStorageBufferWriter(); + + objectStride = storageBufferWriter.getAlignment(); + buffers = new IndirectBuffers(objectStride); + buffers.createBuffers(); + buffers.createObjectStorage(128); + buffers.createDrawStorage(2); + + meshPool = new IndirectMeshPool(vertexType, 1024); + + vertexArray = glCreateVertexArrays(); + + elementBuffer = QuadConverter.getInstance() + .quads2Tris(2048).buffer.handle(); + setupVertexArray(); + + var indirectShader = structType.getIndirectShader(); + compute = ComputeCullerCompiler.INSTANCE.get(indirectShader); + draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.CHEST, indirectShader, Components.WORLD, Components.INDIRECT)); + } + + private void setupVertexArray() { + glVertexArrayElementBuffer(vertexArray, elementBuffer); + + var meshLayout = vertexType.getLayout(); + var meshAttribs = meshLayout.getAttributeCount(); + + var attributes = meshLayout.getAttributes(); + + long offset = 0; + for (int i = 0; i < meshAttribs; i++) { + var attribute = attributes.get(i); + glEnableVertexArrayAttrib(vertexArray, i); + glVertexArrayVertexBuffer(vertexArray, i, meshPool.vbo, offset, meshLayout.getStride()); + attribute.format(vertexArray, i); + offset += attribute.getByteWidth(); + } + } + + void beginFrame() { + hasCulledThisFrame = false; + needsMemoryBarrier = true; + instanceCountThisFrame = calculateTotalInstanceCountAndPrepareBatches(); + } + + void submit(RenderStage stage) { + if (drawSet.isEmpty()) { + return; + } + + if (instanceCountThisFrame == 0) { + return; + } + + cull(); + dispatchDraw(stage); + } + + private void cull() { + if (hasCulledThisFrame) { + return; + } + + buffers.updateCounts(instanceCountThisFrame, drawSet.size()); + meshPool.uploadAll(); + uploadInstanceData(); + uploadIndirectCommands(); + + UniformBuffer.getInstance() + .sync(); + + compute.bind(); + buffers.bindAll(); + + var groupCount = (instanceCountThisFrame + 31) >> 5; // ceil(instanceCount / 32) + glDispatchCompute(groupCount, 1, 1); + hasCulledThisFrame = true; + } + + private void dispatchDraw(RenderStage stage) { + if (!drawSet.contains(stage)) { + return; + } + + draw.bind(); + glBindVertexArray(vertexArray); + buffers.bindObjectAndTarget(); + buffers.bindIndirectBuffer(); + + memoryBarrier(); + + drawSet.submit(stage); + glBindVertexArray(0); + } + + private void memoryBarrier() { + if (needsMemoryBarrier) { + glMemoryBarrier(BARRIER_BITS); + needsMemoryBarrier = false; + } + } + + private void uploadInstanceData() { + long objectPtr = buffers.objectPtr; + long batchIDPtr = buffers.batchPtr; + + for (int i = 0, batchesSize = drawSet.indirectDraws.size(); i < batchesSize; i++) { + var batch = drawSet.indirectDraws.get(i); + var instanceCount = batch.instancer.getInstanceCount(); + batch.writeObjects(objectPtr, batchIDPtr, i); + + objectPtr += instanceCount * objectStride; + batchIDPtr += instanceCount * IndirectBuffers.INT_SIZE; + } + + buffers.flushObjects(objectPtr - buffers.objectPtr); + buffers.flushBatchIDs(batchIDPtr - buffers.batchPtr); + } + + private void uploadIndirectCommands() { + long writePtr = buffers.drawPtr; + for (var batch : drawSet.indirectDraws) { + batch.writeIndirectCommand(writePtr); + writePtr += IndirectBuffers.DRAW_COMMAND_STRIDE; + } + buffers.flushDrawCommands(writePtr - buffers.drawPtr); + } + + private int calculateTotalInstanceCountAndPrepareBatches() { + int baseInstance = 0; + for (var batch : drawSet.indirectDraws) { + batch.prepare(baseInstance); + baseInstance += batch.instancer.instanceCount; + } + return baseInstance; + } + + public void delete() { + glDeleteVertexArrays(vertexArray); + buffers.delete(); + meshPool.delete(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java new file mode 100644 index 000000000..c4d3cbaea --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java @@ -0,0 +1,52 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; + +public final class IndirectDraw { + final IndirectInstancer instancer; + final IndirectMeshPool.BufferedMesh mesh; + final Material material; + int baseInstance = -1; + + boolean needsFullWrite = true; + + IndirectDraw(IndirectInstancer instancer, Material material, IndirectMeshPool.BufferedMesh mesh) { + this.instancer = instancer; + this.material = material; + this.mesh = mesh; + } + + public void prepare(int baseInstance) { + instancer.update(); + if (baseInstance == this.baseInstance) { + needsFullWrite = false; + return; + } + this.baseInstance = baseInstance; + needsFullWrite = true; + } + + void writeObjects(long objectPtr, long batchIDPtr, int batchID) { + if (needsFullWrite) { + instancer.writeFull(objectPtr, batchIDPtr, batchID); + } else if (instancer.anyToUpdate) { + instancer.writeSparse(objectPtr, batchIDPtr, batchID); + } + instancer.anyToUpdate = false; + } + + public void writeIndirectCommand(long ptr) { + var boundingSphere = mesh.mesh.getBoundingSphere(); + + MemoryUtil.memPutInt(ptr, mesh.getIndexCount()); // count + MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be incremented by the compute shader + MemoryUtil.memPutInt(ptr + 8, 0); // firstIndex - all models share the same index buffer + MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex + MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance + + boundingSphere.getToAddress(ptr + 20); // boundingSphere + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawManager.java similarity index 58% rename from src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java rename to src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawManager.java index 1d823e0fb..956694f59 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/RenderLists.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawManager.java @@ -10,15 +10,14 @@ import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.util.Pair; -public class RenderLists { +public class IndirectDrawManager { - public final Map, VertexType>, IndirectList> lists = new HashMap<>(); + public final Map, VertexType>, IndirectCullingGroup> lists = new HashMap<>(); @SuppressWarnings("unchecked") public void add(IndirectInstancer instancer, Material material, Mesh mesh) { - var indirectList = (IndirectList) lists.computeIfAbsent(Pair.of(instancer.structType, mesh.getVertexType()), - p -> new IndirectList<>(p.first(), p.second())); + var indirectList = (IndirectCullingGroup) lists.computeIfAbsent(Pair.of(instancer.type, mesh.getVertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second())); - indirectList.add(instancer, material, mesh); + indirectList.drawSet.add(instancer, material, indirectList.meshPool.alloc(mesh)); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java new file mode 100644 index 000000000..5bf8c18fc --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java @@ -0,0 +1,54 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import static org.lwjgl.opengl.GL11.GL_TRIANGLES; +import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT; +import static org.lwjgl.opengl.GL43.glMultiDrawElementsIndirect; + +import java.util.ArrayList; +import java.util.List; + +import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; + +public class IndirectDrawSet { + + final List> indirectDraws = new ArrayList<>(); + + public boolean isEmpty() { + return indirectDraws.isEmpty(); + } + + public int size() { + return indirectDraws.size(); + } + + public void add(IndirectInstancer instancer, Material material, IndirectMeshPool.BufferedMesh bufferedMesh) { + indirectDraws.add(new IndirectDraw<>(instancer, material, bufferedMesh)); + } + + public void submit(RenderStage stage) { + final int stride = (int) IndirectBuffers.DRAW_COMMAND_STRIDE; + for (int i = 0, indirectDrawsSize = indirectDraws.size(); i < indirectDrawsSize; i++) { + var batch = indirectDraws.get(i); + var material = batch.material; + + if (material.getRenderStage() != stage) { + continue; + } + material.setup(); + glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, i * stride, 1, stride); + material.clear(); + } + } + + public boolean contains(RenderStage stage) { + for (var draw : indirectDraws) { + if (draw.material.getRenderStage() == stage) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java index a48c46954..f6585868f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull; import org.lwjgl.opengl.GL32; import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.gl.GlTextureUnit; @@ -16,7 +17,6 @@ import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.core.RenderContext; -import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.util.WeakHashSet; import com.mojang.blaze3d.systems.RenderSystem; @@ -38,7 +38,7 @@ public class IndirectEngine implements Engine { protected final Map, IndirectFactory> factories = new HashMap<>(); protected final List> uninitializedModels = new ArrayList<>(); - protected final RenderLists renderLists = new RenderLists(); + protected final IndirectDrawManager indirectDrawManager = new IndirectDrawManager(); /** * The set of instance managers that are attached to this engine. @@ -65,16 +65,11 @@ public class IndirectEngine implements Engine { @Override public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) { - if (stage != RenderStage.AFTER_SOLID_TERRAIN) { - return; - } - setup(); - for (IndirectList list : renderLists.lists.values()) { + for (var list : indirectDrawManager.lists.values()) { list.submit(stage); } - } private void setup() { @@ -93,7 +88,8 @@ public class IndirectEngine implements Engine { factories.values() .forEach(IndirectFactory::delete); - renderLists.lists.values().forEach(IndirectList::delete); + indirectDrawManager.lists.values() + .forEach(IndirectCullingGroup::delete); factories.clear(); } @@ -126,9 +122,13 @@ public class IndirectEngine implements Engine { @Override public void beginFrame(TaskEngine taskEngine, RenderContext context) { for (var model : uninitializedModels) { - model.init(renderLists); + model.init(indirectDrawManager); } uninitializedModels.clear(); + + for (IndirectCullingGroup value : indirectDrawManager.lists.values()) { + value.beginFrame(); + } } private void shiftListeners(int cX, int cY, int cZ) { @@ -141,7 +141,7 @@ public class IndirectEngine implements Engine { @Override public void addDebugInfo(List info) { - info.add("GL33 Instanced Arrays"); + info.add("GL46 Indirect"); info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ()); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java index 2a2cef2bb..c1c24ba46 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.backend.instancing.indirect; +import org.lwjgl.system.MemoryUtil; + import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; @@ -8,7 +10,6 @@ import com.jozufozu.flywheel.core.layout.BufferLayout; public class IndirectInstancer extends AbstractInstancer { public final BufferLayout instanceFormat; - public final StructType structType; public final IndirectModel parent; int instanceCount = 0; @@ -18,7 +19,6 @@ public class IndirectInstancer extends AbstractInstance super(type); this.parent = parent; this.instanceFormat = type.getLayout(); - this.structType = type; } @Override @@ -40,6 +40,33 @@ public class IndirectInstancer extends AbstractInstance anyToRemove = false; } + public void writeSparse(long objectPtr, long batchIDPtr, int batchID) { + var storageBufferWriter = this.type.getStorageBufferWriter(); + long objectStride = storageBufferWriter.getAlignment(); + for (int i = 0, size = data.size(); i < size; i++) { + final var element = data.get(i); + if (element.checkDirtyAndClear()) { + storageBufferWriter.write(objectPtr + i * objectStride, element); + + MemoryUtil.memPutInt(batchIDPtr + i * IndirectBuffers.INT_SIZE, batchID); + } + } + } + + public void writeFull(long objectPtr, long batchIDPtr, int batchID) { + var storageBufferWriter = this.type.getStorageBufferWriter(); + var objectStride = storageBufferWriter.getAlignment(); + for (var object : data) { + // write object + storageBufferWriter.write(objectPtr, object); + objectPtr += objectStride; + + // write batchID + MemoryUtil.memPutInt(batchIDPtr, batchID); + batchIDPtr += IndirectBuffers.INT_SIZE; + } + } + @Override public void delete() { // noop diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java deleted file mode 100644 index 7109e4d83..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectList.java +++ /dev/null @@ -1,246 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.indirect; - -import static org.lwjgl.opengl.GL46.*; - -import java.util.ArrayList; -import java.util.List; - -import org.lwjgl.system.MemoryUtil; - -import com.jozufozu.flywheel.api.RenderStage; -import com.jozufozu.flywheel.api.instancer.InstancedPart; -import com.jozufozu.flywheel.api.material.Material; -import com.jozufozu.flywheel.api.struct.StorageBufferWriter; -import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; -import com.jozufozu.flywheel.core.Components; -import com.jozufozu.flywheel.core.Materials; -import com.jozufozu.flywheel.core.QuadConverter; -import com.jozufozu.flywheel.core.model.Mesh; -import com.jozufozu.flywheel.core.uniform.UniformBuffer; - -public class IndirectList { - - final StorageBufferWriter storageBufferWriter; - final GlProgram compute; - final GlProgram draw; - private final VertexType vertexType; - private final long objectStride; - - final IndirectBuffers buffers; - - final IndirectMeshPool meshPool; - private final int elementBuffer; - - int vertexArray; - - final List batches = new ArrayList<>(); - - IndirectList(StructType structType, VertexType vertexType) { - this.vertexType = vertexType; - storageBufferWriter = structType.getStorageBufferWriter(); - - objectStride = storageBufferWriter.getAlignment(); - buffers = new IndirectBuffers(objectStride); - buffers.createBuffers(); - buffers.createObjectStorage(128); - buffers.createDrawStorage(16); - - meshPool = new IndirectMeshPool(vertexType, 1024); - - vertexArray = glCreateVertexArrays(); - - elementBuffer = QuadConverter.getInstance() - .quads2Tris(2048).buffer.handle(); - setupVertexArray(); - - var indirectShader = structType.getIndirectShader(); - compute = ComputeCullerCompiler.INSTANCE.get(indirectShader); - draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.CHEST, indirectShader, Components.WORLD, Components.INDIRECT)); - } - - private void setupVertexArray() { - glVertexArrayElementBuffer(vertexArray, elementBuffer); - - var meshLayout = vertexType.getLayout(); - var meshAttribs = meshLayout.getAttributeCount(); - - var attributes = meshLayout.getAttributes(); - - long offset = 0; - for (int i = 0; i < meshAttribs; i++) { - var attribute = attributes.get(i); - glEnableVertexArrayAttrib(vertexArray, i); - glVertexArrayVertexBuffer(vertexArray, i, meshPool.vbo, offset, meshLayout.getStride()); - attribute.format(vertexArray, i); - offset += attribute - .getByteWidth(); - } - } - - public void add(IndirectInstancer instancer, Material material, Mesh mesh) { - batches.add(new Batch(instancer, material, meshPool.alloc(mesh))); - } - - void submit(RenderStage stage) { - if (batches.isEmpty()) { - return; - } - int instanceCountThisFrame = calculateTotalInstanceCountAndPrepareBatches(); - - if (instanceCountThisFrame == 0) { - return; - } - - // TODO: Sort meshes by material and draw many contiguous sections of the draw indirect buffer, - // adjusting uniforms/textures accordingly - buffers.updateCounts(instanceCountThisFrame, batches.size()); - meshPool.uploadAll(); - uploadInstanceData(); - uploadIndirectCommands(); - - UniformBuffer.getInstance().sync(); - - dispatchCompute(instanceCountThisFrame); - glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); - dispatchDraw(); - } - - private void dispatchDraw() { - draw.bind(); - glVertexArrayElementBuffer(vertexArray, elementBuffer); - glBindVertexArray(vertexArray); - buffers.bindIndirectBuffer(); - - final int stride = (int) IndirectBuffers.DRAW_COMMAND_STRIDE; - long offset = 0; - for (var batch : batches) { - - batch.material.setup(); - glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, offset, 1, stride); - batch.material.clear(); - offset += stride; - } - } - - private void dispatchCompute(int instanceCount) { - compute.bind(); - buffers.bindAll(); - - var groupCount = (instanceCount + 31) >> 5; // ceil(instanceCount / 32) - glDispatchCompute(groupCount, 1, 1); - } - - private void uploadInstanceData() { - long objectPtr = buffers.objectPtr; - long batchIDPtr = buffers.batchPtr; - - for (int i = 0, batchesSize = batches.size(); i < batchesSize; i++) { - var batch = batches.get(i); - var instanceCount = batch.instancer.getInstanceCount(); - batch.write(objectPtr, batchIDPtr, i); - - objectPtr += instanceCount * objectStride; - batchIDPtr += instanceCount * IndirectBuffers.INT_SIZE; - } - - buffers.flushObjects(objectPtr - buffers.objectPtr); - buffers.flushBatchIDs(batchIDPtr - buffers.batchPtr); - } - - private void uploadIndirectCommands() { - long writePtr = buffers.drawPtr; - for (var batch : batches) { - batch.writeIndirectCommand(writePtr); - writePtr += IndirectBuffers.DRAW_COMMAND_STRIDE; - } - buffers.flushDrawCommands(writePtr - buffers.drawPtr); - } - - private int calculateTotalInstanceCountAndPrepareBatches() { - int baseInstance = 0; - for (var batch : batches) { - batch.prepare(baseInstance); - baseInstance += batch.instancer.instanceCount; - } - return baseInstance; - } - - public void delete() { - glDeleteVertexArrays(vertexArray); - buffers.delete(); - meshPool.delete(); - } - - private final class Batch { - final IndirectInstancer instancer; - final IndirectMeshPool.BufferedMesh mesh; - final Material material; - int baseInstance = -1; - - boolean needsFullWrite = true; - - private Batch(IndirectInstancer instancer, Material material, IndirectMeshPool.BufferedMesh mesh) { - this.instancer = instancer; - this.material = material; - this.mesh = mesh; - } - - public void prepare(int baseInstance) { - instancer.update(); - if (baseInstance == this.baseInstance) { - needsFullWrite = false; - return; - } - this.baseInstance = baseInstance; - needsFullWrite = true; - } - - private void write(long objectPtr, long batchIDPtr, int batchID) { - if (needsFullWrite) { - writeFull(objectPtr, batchIDPtr, batchID); - } else if (instancer.anyToUpdate) { - writeSparse(objectPtr, batchIDPtr, batchID); - } - instancer.anyToUpdate = false; - } - - private void writeSparse(long objectPtr, long batchIDPtr, int batchID) { - var all = instancer.getAll(); - for (int i = 0; i < all.size(); i++) { - final var element = all.get(i); - if (element.checkDirtyAndClear()) { - storageBufferWriter.write(objectPtr + i * objectStride, element); - - MemoryUtil.memPutInt(batchIDPtr + i * IndirectBuffers.INT_SIZE, batchID); - } - } - } - - private void writeFull(long objectPtr, long batchIDPtr, int batchID) { - for (var object : this.instancer.getAll()) { - // write object - storageBufferWriter.write(objectPtr, object); - objectPtr += objectStride; - - // write batchID - MemoryUtil.memPutInt(batchIDPtr, batchID); - batchIDPtr += IndirectBuffers.INT_SIZE; - } - } - - public void writeIndirectCommand(long ptr) { - var boundingSphere = mesh.mesh.getBoundingSphere(); - - MemoryUtil.memPutInt(ptr, mesh.getIndexCount()); // count - MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be incremented by the compute shader - MemoryUtil.memPutInt(ptr + 8, 0); // firstIndex - all models share the same index buffer - MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex - MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance - - boundingSphere.getToAddress(ptr + 20); // boundingSphere - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java index 88e5107ea..29638638b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java @@ -14,12 +14,12 @@ public class IndirectModel { this.instancer = new IndirectInstancer<>(this, type); } - public void init(RenderLists renderLists) { + public void init(IndirectDrawManager indirectDrawManager) { var materialMeshMap = this.model.getMeshes(); for (var entry : materialMeshMap.entrySet()) { var material = entry.getKey(); var mesh = entry.getValue(); - renderLists.add(instancer, material, mesh); + indirectDrawManager.add(instancer, material, mesh); return; // TODO: support multiple meshes per model } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockMesh.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockMesh.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java index ffea61b53..17183e8cc 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java @@ -23,21 +23,21 @@ public class OrientedStorageWriter implements StorageBufferWriter MemoryUtil.memPutFloat(ptr + 20, d.posY); MemoryUtil.memPutFloat(ptr + 24, d.posZ); - MemoryUtil.memPutFloat(ptr + 32, d.pivotX); - MemoryUtil.memPutFloat(ptr + 36, d.pivotY); - MemoryUtil.memPutFloat(ptr + 40, d.pivotZ); + MemoryUtil.memPutFloat(ptr + 28, d.pivotX); + MemoryUtil.memPutFloat(ptr + 32, d.pivotY); + MemoryUtil.memPutFloat(ptr + 36, d.pivotZ); - MemoryUtil.memPutShort(ptr + 44, d.skyLight); - MemoryUtil.memPutShort(ptr + 46, d.blockLight); + MemoryUtil.memPutShort(ptr + 40, d.skyLight); + MemoryUtil.memPutShort(ptr + 42, d.blockLight); - MemoryUtil.memPutByte(ptr + 48, d.r); - MemoryUtil.memPutByte(ptr + 49, d.g); - MemoryUtil.memPutByte(ptr + 50, d.b); - MemoryUtil.memPutByte(ptr + 51, d.a); + MemoryUtil.memPutByte(ptr + 44, d.r); + MemoryUtil.memPutByte(ptr + 45, d.g); + MemoryUtil.memPutByte(ptr + 46, d.b); + MemoryUtil.memPutByte(ptr + 47, d.a); } @Override public int getAlignment() { - return 64; + return 48; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java b/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java index 1f05a3e1d..23894b7ad 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java @@ -16,16 +16,16 @@ public class TransformedStorageWriter implements StorageBufferWriter> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0; } -#endif + #endif diff --git a/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl index 0fbf19511..298a5d5f4 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl +++ b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl @@ -1,22 +1,23 @@ #use "flywheel:api/vertex.glsl" +#use "flywheel:util/types.glsl" #define FLW_INSTANCE_STRUCT Instance struct Instance { - mat4 pose; - mat3 normal; + Mat4F pose; + Mat3F normal; uint color; uint light; }; void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) { - center = (i.pose * vec4(center, 1.0)).xyz; + center = (unpackMat4F(i.pose) * vec4(center, 1.0)).xyz; } #ifdef VERTEX_SHADER void flw_instanceVertex(Instance i) { - flw_vertexPos = i.pose * flw_vertexPos; - flw_vertexNormal = i.normal * flw_vertexNormal; + flw_vertexPos = unpackMat4F(i.pose) * flw_vertexPos; + flw_vertexNormal = unpackMat3F(i.normal) * flw_vertexNormal; flw_vertexColor = unpackUnorm4x8(i.color); flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0; } -#endif + #endif diff --git a/src/main/resources/assets/flywheel/flywheel/util/types.glsl b/src/main/resources/assets/flywheel/flywheel/util/types.glsl index a6f58174b..3a93dbda4 100644 --- a/src/main/resources/assets/flywheel/flywheel/util/types.glsl +++ b/src/main/resources/assets/flywheel/flywheel/util/types.glsl @@ -1,3 +1,4 @@ +// Types intended for use is SSBOs to achieve tighter data packing. struct Vec3F { float x; @@ -5,13 +6,58 @@ struct Vec3F { float z; }; +struct Vec4F { + float x; + float y; + float z; + float w; +}; + +struct Mat4F { + Vec4F c0; + Vec4F c1; + Vec4F c2; + Vec4F c3; +}; + +struct Mat3F { + Vec3F c0; + Vec3F c1; + Vec3F c2; +}; + // 4-aligned instead of a 16-aligned vec4 struct BoundingSphere { Vec3F center; float radius; }; +vec3 unpackVec3F(in Vec3F v) { + return vec3(v.x, v.y, v.z); +} + +vec4 unpackVec4F(in Vec4F v) { + return vec4(v.x, v.y, v.z, v.w); +} + +mat4 unpackMat4F(in Mat4F m) { + return mat4( + unpackVec4F(m.c0), + unpackVec4F(m.c1), + unpackVec4F(m.c2), + unpackVec4F(m.c3) + ); +} + +mat3 unpackMat3F(in Mat3F m) { + return mat3( + unpackVec3F(m.c0), + unpackVec3F(m.c1), + unpackVec3F(m.c2) + ); +} + void unpackBoundingSphere(in BoundingSphere sphere, out vec3 center, out float radius) { - center = vec3(sphere.center.x, sphere.center.y, sphere.center.z); + center = unpackVec3F(sphere.center); radius = sphere.radius; } From 8833181cfe4fc744271ecda4eba997cbe3dbcd0f Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 21 Aug 2022 17:35:43 -0700 Subject: [PATCH 11/13] Cool Command Captures/Pauses - Add frustum debug utility - Fix scaling for transformed bounding spheres --- .../jozufozu/flywheel/config/FlwCommands.java | 18 ++++++++++++++++++ .../flywheel/core/uniform/FrustumProvider.java | 6 +++++- .../flywheel/instance/oriented_indirect.glsl | 2 +- .../instance/transformed_indirect.glsl | 8 ++++++-- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index 800d0a661..5fb8a5032 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -5,6 +5,7 @@ import java.util.function.BiConsumer; import org.jetbrains.annotations.NotNull; import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.core.uniform.FrustumProvider; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.IntegerArgumentType; @@ -109,6 +110,23 @@ public class FlwCommands { return 1; })))); + commandBuilder.command.then(Commands.literal("debugFrustum") + .then(Commands.literal("pause") + .executes(context -> { + FrustumProvider.PAUSED = true; + return 1; + })) + .then(Commands.literal("unpause") + .executes(context -> { + FrustumProvider.PAUSED = false; + return 1; + })) + .then(Commands.literal("capture") + .executes(context -> { + FrustumProvider.CAPTURE = true; + return 1; + }))); + commandBuilder.build(event.getDispatcher()); } diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java b/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java index 3ba00e5c2..0f8346cad 100644 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java @@ -15,6 +15,9 @@ import net.minecraftforge.common.MinecraftForge; public class FrustumProvider extends UniformProvider { + public static boolean PAUSED = false; + public static boolean CAPTURE = false; + public FrustumProvider() { MinecraftForge.EVENT_BUS.addListener(this::beginFrame); } @@ -34,7 +37,7 @@ public class FrustumProvider extends UniformProvider { } public void update(RenderContext context) { - if (ptr == MemoryUtil.NULL) { + if (ptr == MemoryUtil.NULL || (PAUSED && !CAPTURE)) { return; } @@ -51,5 +54,6 @@ public class FrustumProvider extends UniformProvider { shiftedCuller.getJozuPackedPlanes(ptr); notifier.signalChanged(); + CAPTURE = false; } } diff --git a/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl b/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl index 563919005..cd0f90fb2 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl +++ b/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl @@ -19,7 +19,7 @@ void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float r center = rotateVertexByQuat(center - pivot, rotation) + pivot + pos; } - #ifdef VERTEX_SHADER +#ifdef VERTEX_SHADER void flw_instanceVertex(Instance i) { vec4 rotation = unpackVec4F(i.rotation); vec3 pivot = unpackVec3F(i.pivot); diff --git a/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl index 298a5d5f4..f0067c4b9 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl +++ b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl @@ -10,10 +10,14 @@ struct Instance { }; void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) { - center = (unpackMat4F(i.pose) * vec4(center, 1.0)).xyz; + mat4 pose = unpackMat4F(i.pose); + center = (pose * vec4(center, 1.0)).xyz; + + float scale = max(length(pose[0].xyz), max(length(pose[1].xyz), length(pose[2].xyz))); + radius *= scale; } -#ifdef VERTEX_SHADER + #ifdef VERTEX_SHADER void flw_instanceVertex(Instance i) { flw_vertexPos = unpackMat4F(i.pose) * flw_vertexPos; flw_vertexNormal = unpackMat3F(i.normal) * flw_vertexNormal; From 0e6f249cd00d962fd60670460c67571890f434ad Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 21 Aug 2022 23:02:43 -0700 Subject: [PATCH 12/13] One liners - Fix nothing rendering + gl error spam when renderdoc is not attached - Fix fog rendering incorrectly with indirect --- .../flywheel/backend/instancing/indirect/IndirectBuffers.java | 4 ++-- .../backend/instancing/indirect/IndirectCullingGroup.java | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java index dae12ac7b..f99a6d799 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.backend.instancing.indirect; +import static org.lwjgl.opengl.GL45.glCreateBuffers; import static org.lwjgl.opengl.GL46.GL_DRAW_INDIRECT_BUFFER; import static org.lwjgl.opengl.GL46.GL_DYNAMIC_STORAGE_BIT; import static org.lwjgl.opengl.GL46.GL_MAP_FLUSH_EXPLICIT_BIT; @@ -10,7 +11,6 @@ import static org.lwjgl.opengl.GL46.glBindBuffer; import static org.lwjgl.opengl.GL46.glCopyNamedBufferSubData; import static org.lwjgl.opengl.GL46.glDeleteBuffers; import static org.lwjgl.opengl.GL46.glFlushMappedNamedBufferRange; -import static org.lwjgl.opengl.GL46.glGenBuffers; import static org.lwjgl.opengl.GL46.glNamedBufferStorage; import static org.lwjgl.opengl.GL46.nglBindBuffersRange; import static org.lwjgl.opengl.GL46.nglCreateBuffers; @@ -154,7 +154,7 @@ public class IndirectBuffers { var drawSize = DRAW_COMMAND_STRIDE * drawCount; if (maxDrawCount > 0) { - int drawNew = glGenBuffers(); + int drawNew = glCreateBuffers(); glNamedBufferStorage(drawNew, drawSize, SUB_DATA_BITS); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java index 05794682f..4b74170cf 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java @@ -137,6 +137,9 @@ public class IndirectCullingGroup { buffers.bindObjectAndTarget(); buffers.bindIndirectBuffer(); + UniformBuffer.getInstance() + .sync(); + memoryBarrier(); drawSet.submit(stage); From 46bd1a374f2559fbaeeb3f08e07363715b0ef511 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Mon, 22 Aug 2022 13:57:53 -0700 Subject: [PATCH 13/13] Merge ease --- .../backend/instancing/InstanceWorld.java | 2 +- .../batching/BatchDrawingTracker.java | 2 +- .../jozufozu/flywheel/core/RenderContext.java | 3 +- .../transformed/TransformedStorageWriter.java | 6 ++-- .../transformed/TransformedWriter.java | 6 ++-- .../flywheel/core/uniform/ViewProvider.java | 4 +-- .../extension/ClientLevelExtension.java | 2 +- .../extension/Matrix3fExtension.java | 2 +- .../extension/Matrix4fExtension.java | 2 +- .../MatrixWrite.java} | 10 +++--- .../extension/RenderTypeExtension.java | 2 +- .../flywheel/mixin/ClientLevelMixin.java | 2 +- .../flywheel/mixin/RenderTypeMixin.java | 2 +- .../flywheel/mixin/matrix/Matrix3fMixin.java | 33 ++++++++++++------- .../flywheel/mixin/matrix/Matrix4fMixin.java | 33 ++++++++++++------- 15 files changed, 64 insertions(+), 47 deletions(-) rename src/main/java/com/jozufozu/flywheel/{util => }/extension/ClientLevelExtension.java (94%) rename src/main/java/com/jozufozu/flywheel/{util => }/extension/Matrix3fExtension.java (89%) rename src/main/java/com/jozufozu/flywheel/{util => }/extension/Matrix4fExtension.java (89%) rename src/main/java/com/jozufozu/flywheel/{util/extension/MatrixExtension.java => extension/MatrixWrite.java} (68%) rename src/main/java/com/jozufozu/flywheel/{util => }/extension/RenderTypeExtension.java (93%) diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index 79b1c78b7..ed26e1f7b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -14,7 +14,7 @@ import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.event.BeginFrameEvent; -import com.jozufozu.flywheel.util.extension.ClientLevelExtension; +import com.jozufozu.flywheel.extension.ClientLevelExtension; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchDrawingTracker.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchDrawingTracker.java index ef7cea552..b2de5a307 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchDrawingTracker.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchDrawingTracker.java @@ -3,7 +3,7 @@ package com.jozufozu.flywheel.backend.instancing.batching; import java.util.HashSet; import java.util.Set; -import com.jozufozu.flywheel.util.extension.RenderTypeExtension; +import com.jozufozu.flywheel.extension.RenderTypeExtension; import com.mojang.blaze3d.vertex.BufferBuilder; import net.minecraft.client.renderer.RenderType; diff --git a/src/main/java/com/jozufozu/flywheel/core/RenderContext.java b/src/main/java/com/jozufozu/flywheel/core/RenderContext.java index 0f4db1f6f..4664a052a 100644 --- a/src/main/java/com/jozufozu/flywheel/core/RenderContext.java +++ b/src/main/java/com/jozufozu/flywheel/core/RenderContext.java @@ -2,7 +2,7 @@ package com.jozufozu.flywheel.core; import org.jetbrains.annotations.NotNull; -import com.jozufozu.flywheel.util.extension.Matrix4fExtension; +import com.jozufozu.flywheel.extension.Matrix4fExtension; import com.jozufozu.flywheel.util.joml.FrustumIntersection; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Matrix4f; @@ -11,7 +11,6 @@ import net.minecraft.client.Camera; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.RenderBuffers; -import net.minecraft.world.phys.Vec3; public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack stack, Matrix4f viewProjection, Matrix4f projection, RenderBuffers buffers, Camera camera, FrustumIntersection culler) { diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java b/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java index 23894b7ad..a9f370442 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java @@ -3,7 +3,7 @@ package com.jozufozu.flywheel.core.structs.transformed; import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.struct.StorageBufferWriter; -import com.jozufozu.flywheel.util.extension.MatrixExtension; +import com.jozufozu.flywheel.extension.MatrixWrite; public class TransformedStorageWriter implements StorageBufferWriter { @@ -14,8 +14,8 @@ public class TransformedStorageWriter implements StorageBufferWriter { public static final TransformedWriter INSTANCE = new TransformedWriter(); @@ -11,7 +11,7 @@ public class TransformedWriter extends ColoredLitWriter { super.write(ptr, d); ptr += 6; - ((MatrixExtension) (Object) d.model).flywheel$writeUnsafe(ptr); - ((MatrixExtension) (Object) d.normal).flywheel$writeUnsafe(ptr + 4 * 16); + ((MatrixWrite) (Object) d.model).flywheel$writeUnsafe(ptr); + ((MatrixWrite) (Object) d.normal).flywheel$writeUnsafe(ptr + 4 * 16); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java b/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java index 3c8448744..89175710e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java @@ -8,7 +8,7 @@ import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.event.BeginFrameEvent; -import com.jozufozu.flywheel.util.extension.MatrixExtension; +import com.jozufozu.flywheel.extension.MatrixWrite; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.Vec3i; @@ -52,7 +52,7 @@ public class ViewProvider extends UniformProvider { var vp = context.viewProjection().copy(); vp.multiplyWithTranslation(-camX, -camY, -camZ); - MatrixExtension.writeUnsafe(vp, ptr); + MatrixWrite.writeUnsafe(vp, ptr); MemoryUtil.memPutFloat(ptr + 64, camX); MemoryUtil.memPutFloat(ptr + 68, camY); MemoryUtil.memPutFloat(ptr + 72, camZ); diff --git a/src/main/java/com/jozufozu/flywheel/util/extension/ClientLevelExtension.java b/src/main/java/com/jozufozu/flywheel/extension/ClientLevelExtension.java similarity index 94% rename from src/main/java/com/jozufozu/flywheel/util/extension/ClientLevelExtension.java rename to src/main/java/com/jozufozu/flywheel/extension/ClientLevelExtension.java index d6e6e8bd5..79baf9647 100644 --- a/src/main/java/com/jozufozu/flywheel/util/extension/ClientLevelExtension.java +++ b/src/main/java/com/jozufozu/flywheel/extension/ClientLevelExtension.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.util.extension; +package com.jozufozu.flywheel.extension; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.entity.Entity; diff --git a/src/main/java/com/jozufozu/flywheel/util/extension/Matrix3fExtension.java b/src/main/java/com/jozufozu/flywheel/extension/Matrix3fExtension.java similarity index 89% rename from src/main/java/com/jozufozu/flywheel/util/extension/Matrix3fExtension.java rename to src/main/java/com/jozufozu/flywheel/extension/Matrix3fExtension.java index a587ec537..ef8ce3cc6 100644 --- a/src/main/java/com/jozufozu/flywheel/util/extension/Matrix3fExtension.java +++ b/src/main/java/com/jozufozu/flywheel/extension/Matrix3fExtension.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.util.extension; +package com.jozufozu.flywheel.extension; import com.jozufozu.flywheel.util.joml.Matrix3f; diff --git a/src/main/java/com/jozufozu/flywheel/util/extension/Matrix4fExtension.java b/src/main/java/com/jozufozu/flywheel/extension/Matrix4fExtension.java similarity index 89% rename from src/main/java/com/jozufozu/flywheel/util/extension/Matrix4fExtension.java rename to src/main/java/com/jozufozu/flywheel/extension/Matrix4fExtension.java index 7651cf622..ea77ca703 100644 --- a/src/main/java/com/jozufozu/flywheel/util/extension/Matrix4fExtension.java +++ b/src/main/java/com/jozufozu/flywheel/extension/Matrix4fExtension.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.util.extension; +package com.jozufozu.flywheel.extension; import com.jozufozu.flywheel.util.joml.Matrix4f; diff --git a/src/main/java/com/jozufozu/flywheel/util/extension/MatrixExtension.java b/src/main/java/com/jozufozu/flywheel/extension/MatrixWrite.java similarity index 68% rename from src/main/java/com/jozufozu/flywheel/util/extension/MatrixExtension.java rename to src/main/java/com/jozufozu/flywheel/extension/MatrixWrite.java index d1e7253af..15dead550 100644 --- a/src/main/java/com/jozufozu/flywheel/util/extension/MatrixExtension.java +++ b/src/main/java/com/jozufozu/flywheel/extension/MatrixWrite.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.util.extension; +package com.jozufozu.flywheel.extension; import java.nio.ByteBuffer; @@ -9,7 +9,7 @@ import com.mojang.math.Matrix4f; * @see com.jozufozu.flywheel.mixin.matrix.Matrix3fMixin * @see com.jozufozu.flywheel.mixin.matrix.Matrix4fMixin */ -public interface MatrixExtension { +public interface MatrixWrite { /** * Write the contents of this object into sequential memory starting at the given address. @@ -19,14 +19,14 @@ public interface MatrixExtension { void flywheel$write(ByteBuffer buf); static void write(Matrix4f matrix, ByteBuffer buf) { - ((MatrixExtension) (Object) matrix).flywheel$write(buf); + ((MatrixWrite) (Object) matrix).flywheel$write(buf); } static void writeUnsafe(Matrix4f matrix, long ptr) { - ((MatrixExtension) (Object) matrix).flywheel$writeUnsafe(ptr); + ((MatrixWrite) (Object) matrix).flywheel$writeUnsafe(ptr); } static void writeUnsafe(Matrix3f matrix, long ptr) { - ((MatrixExtension) (Object) matrix).flywheel$writeUnsafe(ptr); + ((MatrixWrite) (Object) matrix).flywheel$writeUnsafe(ptr); } } diff --git a/src/main/java/com/jozufozu/flywheel/util/extension/RenderTypeExtension.java b/src/main/java/com/jozufozu/flywheel/extension/RenderTypeExtension.java similarity index 93% rename from src/main/java/com/jozufozu/flywheel/util/extension/RenderTypeExtension.java rename to src/main/java/com/jozufozu/flywheel/extension/RenderTypeExtension.java index 6398bb479..dbf31df06 100644 --- a/src/main/java/com/jozufozu/flywheel/util/extension/RenderTypeExtension.java +++ b/src/main/java/com/jozufozu/flywheel/extension/RenderTypeExtension.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.util.extension; +package com.jozufozu.flywheel.extension; import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer; diff --git a/src/main/java/com/jozufozu/flywheel/mixin/ClientLevelMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/ClientLevelMixin.java index 38ccdda7e..e9208428f 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/ClientLevelMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/ClientLevelMixin.java @@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import com.google.common.collect.Lists; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; -import com.jozufozu.flywheel.util.extension.ClientLevelExtension; +import com.jozufozu.flywheel.extension.ClientLevelExtension; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.entity.Entity; diff --git a/src/main/java/com/jozufozu/flywheel/mixin/RenderTypeMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/RenderTypeMixin.java index 454a11be8..622c9e1b8 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/RenderTypeMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/RenderTypeMixin.java @@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer; -import com.jozufozu.flywheel.util.extension.RenderTypeExtension; +import com.jozufozu.flywheel.extension.RenderTypeExtension; import net.minecraft.client.renderer.RenderType; diff --git a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java index 98fbe36ac..3447d2978 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java @@ -6,22 +6,31 @@ import org.lwjgl.system.MemoryUtil; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import com.jozufozu.flywheel.util.extension.Matrix3fExtension; -import com.jozufozu.flywheel.util.extension.MatrixExtension; +import com.jozufozu.flywheel.extension.Matrix3fExtension; +import com.jozufozu.flywheel.extension.MatrixWrite; import com.mojang.math.Matrix3f; @Mixin(Matrix3f.class) -public abstract class Matrix3fMixin implements MatrixExtension, Matrix3fExtension { +public abstract class Matrix3fMixin implements MatrixWrite, Matrix3fExtension { - @Shadow protected float m00; - @Shadow protected float m01; - @Shadow protected float m02; - @Shadow protected float m10; - @Shadow protected float m11; - @Shadow protected float m12; - @Shadow protected float m20; - @Shadow protected float m21; - @Shadow protected float m22; + @Shadow + protected float m00; + @Shadow + protected float m01; + @Shadow + protected float m02; + @Shadow + protected float m10; + @Shadow + protected float m11; + @Shadow + protected float m12; + @Shadow + protected float m20; + @Shadow + protected float m21; + @Shadow + protected float m22; @Override public void flywheel$writeUnsafe(long ptr) { diff --git a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java index f556f7ce2..008677335 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java @@ -6,22 +6,31 @@ import org.lwjgl.system.MemoryUtil; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import com.jozufozu.flywheel.util.extension.Matrix4fExtension; -import com.jozufozu.flywheel.util.extension.MatrixExtension; +import com.jozufozu.flywheel.extension.Matrix4fExtension; +import com.jozufozu.flywheel.extension.MatrixWrite; import com.mojang.math.Matrix4f; @Mixin(Matrix4f.class) -public abstract class Matrix4fMixin implements MatrixExtension, Matrix4fExtension { +public abstract class Matrix4fMixin implements MatrixWrite, Matrix4fExtension { - @Shadow protected float m00; - @Shadow protected float m01; - @Shadow protected float m02; - @Shadow protected float m03; - @Shadow protected float m10; - @Shadow protected float m11; - @Shadow protected float m12; - @Shadow protected float m13; - @Shadow protected float m20; + @Shadow + protected float m00; + @Shadow + protected float m01; + @Shadow + protected float m02; + @Shadow + protected float m03; + @Shadow + protected float m10; + @Shadow + protected float m11; + @Shadow + protected float m12; + @Shadow + protected float m13; + @Shadow + protected float m20; @Shadow protected float m21; @Shadow protected float m22; @Shadow protected float m23;