From bbd18ab509cf498d67b231d70194bcb3f75d82fa Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Fri, 22 Sep 2023 22:38:36 -0500 Subject: [PATCH 01/30] Move particle vertex processing to the GPU --- .../ShaderBillboardParticleRenderer.java | 56 +++++ .../shader/BillboardParticleVertex.java | 103 ++++++++++ .../shader/ParticleMeshAttribute.java | 10 + .../shader/ParticleShaderBindingPoints.java | 12 ++ .../shader/ParticleShaderInterface.java | 68 ++++++ .../particle/BillboardParticleMixin.java | 79 ++----- .../render/particle/ParticleManagerMixin.java | 193 ++++++++++++++++++ .../shaders/blocks/block_layer_opaque.vsh | 2 +- .../{chunk_matrices.glsl => matrices.glsl} | 0 .../sodium/shaders/particles/particle.fsh | 16 ++ .../sodium/shaders/particles/particle.vsh | 62 ++++++ src/main/resources/sodium.mixins.json | 19 +- 12 files changed, 548 insertions(+), 72 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java rename src/main/resources/assets/sodium/shaders/include/{chunk_matrices.glsl => matrices.glsl} (100%) create mode 100644 src/main/resources/assets/sodium/shaders/particles/particle.fsh create mode 100644 src/main/resources/assets/sodium/shaders/particles/particle.vsh diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java new file mode 100644 index 0000000000..66a7cf2808 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java @@ -0,0 +1,56 @@ +package me.jellysquid.mods.sodium.client.render.particle; + +import me.jellysquid.mods.sodium.client.gl.shader.*; +import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderBindingPoints; +import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; +import net.minecraft.util.Identifier; + +public class ShaderBillboardParticleRenderer { + protected GlProgram activeProgram; + + public ShaderBillboardParticleRenderer() { + this.activeProgram = createShader("particles/particle"); + } + + public GlProgram getActiveProgram() { + return activeProgram; + } + + private GlProgram createShader(String path) { + ShaderConstants constants = ShaderConstants.builder().build(); + + GlShader vertShader = ShaderLoader.loadShader(ShaderType.VERTEX, + new Identifier("sodium", path + ".vsh"), constants); + + GlShader fragShader = ShaderLoader.loadShader(ShaderType.FRAGMENT, + new Identifier("sodium", path + ".fsh"), constants); + + try { + return GlProgram.builder(new Identifier("sodium", "billboard_particle_shader")) + .attachShader(vertShader) + .attachShader(fragShader) + .bindAttribute("in_Position", ParticleShaderBindingPoints.ATTRIBUTE_POSITION) + .bindAttribute("in_Size", ParticleShaderBindingPoints.ATTRIBUTE_SIZE) + .bindAttribute("in_TexCoord", ParticleShaderBindingPoints.ATTRIBUTE_TEXTURE) + .bindAttribute("in_Color", ParticleShaderBindingPoints.ATTRIBUTE_COLOR) + .bindAttribute("in_Light", ParticleShaderBindingPoints.ATTRIBUTE_LIGHT_TEXTURE) + .bindAttribute("in_Angle", ParticleShaderBindingPoints.ATTRIBUTE_ANGLE) + .bindFragmentData("out_FragColor", ParticleShaderBindingPoints.FRAG_COLOR) + .link(ParticleShaderInterface::new); + } finally { + vertShader.delete(); + fragShader.delete(); + } + } + + public void begin() { + // pass.startDrawing(); .. Do I need a pass? + + this.activeProgram.bind(); + this.activeProgram.getInterface().setupState(); + } + + public void end() { + this.activeProgram.unbind(); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java new file mode 100644 index 0000000000..eea729f02e --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java @@ -0,0 +1,103 @@ +package me.jellysquid.mods.sodium.client.render.particle.shader; + +import com.google.common.collect.ImmutableMap; +import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeBinding; +import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeFormat; +import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexFormat; +import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderBindingPoints; +import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkMeshAttribute; +import net.caffeinemc.mods.sodium.api.vertex.attributes.CommonVertexAttribute; +import net.caffeinemc.mods.sodium.api.vertex.attributes.common.ColorAttribute; +import net.caffeinemc.mods.sodium.api.vertex.attributes.common.LightAttribute; +import net.caffeinemc.mods.sodium.api.vertex.attributes.common.PositionAttribute; +import net.caffeinemc.mods.sodium.api.vertex.attributes.common.TextureAttribute; +import net.caffeinemc.mods.sodium.api.vertex.format.VertexFormatDescription; +import net.caffeinemc.mods.sodium.api.vertex.format.VertexFormatRegistry; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormatElement; +import org.lwjgl.opengl.GL20C; +import org.lwjgl.opengl.GL30C; +import org.lwjgl.system.MemoryUtil; + +import java.util.Map; + +import static net.minecraft.client.render.VertexFormats.*; + +public class BillboardParticleVertex { + public static final int POSITION_OFFSET = 0; + public static final int SIZE_OFFSET = 12; + public static final int TEX_UV_OFFSET = 16; + public static final int COLOR_OFFSET = 24; + public static final int LIGHT_UV_OFFSET = 28; + public static final int ANGLE_OFFSET = 32; + public static final int STRIDE = 36; + + public static final GlVertexFormat VERTEX_FORMAT = GlVertexFormat.builder(ParticleMeshAttribute.class, STRIDE) + .addElement(ParticleMeshAttribute.POSITION, POSITION_OFFSET, GlVertexAttributeFormat.FLOAT, 3, false, false) + .addElement(ParticleMeshAttribute.SIZE, SIZE_OFFSET, GlVertexAttributeFormat.FLOAT, 1, false, false) + .addElement(ParticleMeshAttribute.TEX_COORD, TEX_UV_OFFSET, GlVertexAttributeFormat.FLOAT, 2, false, false) + .addElement(ParticleMeshAttribute.COLOR, COLOR_OFFSET, GlVertexAttributeFormat.UNSIGNED_BYTE, 4, true, false) + .addElement(ParticleMeshAttribute.LIGHT_UV, LIGHT_UV_OFFSET, GlVertexAttributeFormat.UNSIGNED_SHORT, 2, false, true) + .addElement(ParticleMeshAttribute.ANGLE, ANGLE_OFFSET, GlVertexAttributeFormat.FLOAT, 1, false, false) + .build(); + + public static final VertexFormat MC_VERTEX_FORMAT = new VertexFormat(ImmutableMap.ofEntries( + Map.entry("in_Position", POSITION_ELEMENT), + Map.entry("in_Size", new VertexFormatElement( + 0, + VertexFormatElement.ComponentType.FLOAT, + VertexFormatElement.Type.GENERIC, + 1 + )), + Map.entry("in_TexCoord", TEXTURE_ELEMENT), + Map.entry("in_Color", COLOR_ELEMENT), + Map.entry("in_Light", LIGHT_ELEMENT), + Map.entry("in_Angle", new VertexFormatElement( + 0, + VertexFormatElement.ComponentType.FLOAT, + VertexFormatElement.Type.GENERIC, + 1 + )) + )); + + public static final GlVertexAttributeBinding[] ATTRIBUTE_BINDINGS = new GlVertexAttributeBinding[] { + new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_POSITION, + VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.POSITION)), + new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_SIZE, + VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.SIZE)), + new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_TEXTURE, + VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.TEX_COORD)), + new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_COLOR, + VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.COLOR)), + new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_LIGHT_TEXTURE, + VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.LIGHT_UV)), + new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_ANGLE, + VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.ANGLE)), + }; + + public static final VertexFormatDescription VERTEX_FORMAT_DESCRIPTION = VertexFormatRegistry.instance() + .get(MC_VERTEX_FORMAT); + + public static void put(long ptr, float x, float y, float z, + float u, float v, int color, int light, float size, float angle) { + PositionAttribute.put(ptr + POSITION_OFFSET, x, y, z); + MemoryUtil.memPutFloat(ptr + SIZE_OFFSET, size); + TextureAttribute.put(ptr + TEX_UV_OFFSET, u, v); + ColorAttribute.set(ptr + COLOR_OFFSET, color); + LightAttribute.set(ptr + LIGHT_UV_OFFSET, light); + MemoryUtil.memPutFloat(ptr + ANGLE_OFFSET, angle); + } + + public static void bindVertexFormat() { + for (GlVertexAttributeBinding attrib : ATTRIBUTE_BINDINGS) { + if (attrib.isIntType()) { + GL30C.glVertexAttribIPointer(attrib.getIndex(), attrib.getCount(), attrib.getFormat(), + attrib.getStride(), attrib.getPointer()); + } else { + GL20C.glVertexAttribPointer(attrib.getIndex(), attrib.getCount(), attrib.getFormat(), attrib.isNormalized(), + attrib.getStride(), attrib.getPointer()); + } + GL20C.glEnableVertexAttribArray(attrib.getIndex()); + } + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java new file mode 100644 index 0000000000..b7fa85a527 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java @@ -0,0 +1,10 @@ +package me.jellysquid.mods.sodium.client.render.particle.shader; + +public enum ParticleMeshAttribute { + POSITION, + SIZE, + TEX_COORD, + COLOR, + LIGHT_UV, + ANGLE, +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java new file mode 100644 index 0000000000..99ae542d91 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java @@ -0,0 +1,12 @@ +package me.jellysquid.mods.sodium.client.render.particle.shader; + +public class ParticleShaderBindingPoints { + public static final int ATTRIBUTE_POSITION = 1; + public static final int ATTRIBUTE_SIZE = 2; + public static final int ATTRIBUTE_TEXTURE = 3; + public static final int ATTRIBUTE_COLOR = 4; + public static final int ATTRIBUTE_LIGHT_TEXTURE = 5; + public static final int ATTRIBUTE_ANGLE = 6; + + public static final int FRAG_COLOR = 0; +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java new file mode 100644 index 0000000000..492da6c891 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java @@ -0,0 +1,68 @@ +package me.jellysquid.mods.sodium.client.render.particle.shader; + +import com.mojang.blaze3d.platform.GlStateManager; +import me.jellysquid.mods.sodium.client.gl.shader.uniform.GlUniformFloat4v; +import me.jellysquid.mods.sodium.client.gl.shader.uniform.GlUniformInt; +import me.jellysquid.mods.sodium.client.gl.shader.uniform.GlUniformMatrix4f; +import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderTextureSlot; +import me.jellysquid.mods.sodium.client.render.chunk.shader.ShaderBindingContext; +import me.jellysquid.mods.sodium.client.util.TextureUtil; +import org.joml.*; +import org.lwjgl.opengl.GL32C; + +public class ParticleShaderInterface { + private final GlUniformInt uniformParticleTexture; + private final GlUniformInt uniformLightTexture; + private final GlUniformMatrix4f uniformModelViewMatrix; + private final GlUniformMatrix4f uniformProjectionMatrix; + private final GlUniformFloat4v uniformCameraRotation; + + public ParticleShaderInterface(ShaderBindingContext context) { + this.uniformParticleTexture = context.bindUniform("u_ParticleTex", GlUniformInt::new); + this.uniformLightTexture = context.bindUniform("u_LightTex", GlUniformInt::new); + this.uniformModelViewMatrix = context.bindUniform("u_ModelViewMatrix", GlUniformMatrix4f::new); + this.uniformProjectionMatrix = context.bindUniform("u_ProjectionMatrix", GlUniformMatrix4f::new); + this.uniformCameraRotation = context.bindUniform("u_CameraRotation", GlUniformFloat4v::new); + } + + public void setProjectionMatrix(Matrix4fc matrix) { + this.uniformProjectionMatrix.set(matrix); + } + + public void setModelViewMatrix(Matrix4fc matrix) { + this.uniformModelViewMatrix.set(matrix); + } + public void setCameraRotation(Quaternionfc quaternion) { + this.uniformCameraRotation.set(new float[] { + quaternion.x(), + quaternion.y(), + quaternion.z(), + quaternion.w(), + }); + } + + public void setupState() { + // "BlockTexture" should represent the particle textures if bound correctly + this.bindParticleTexture(ParticleShaderTextureSlot.TEXTURE, TextureUtil.getBlockTextureId()); + this.bindLightTexture(ParticleShaderTextureSlot.LIGHT, TextureUtil.getLightTextureId()); + } + + private void bindParticleTexture(ParticleShaderTextureSlot slot, int textureId) { + GlStateManager._activeTexture(GL32C.GL_TEXTURE0 + slot.ordinal()); + GlStateManager._bindTexture(textureId); + + uniformParticleTexture.setInt(slot.ordinal()); + } + + private void bindLightTexture(ParticleShaderTextureSlot slot, int textureId) { + GlStateManager._activeTexture(GL32C.GL_TEXTURE0 + slot.ordinal()); + GlStateManager._bindTexture(textureId); + + uniformLightTexture.setInt(slot.ordinal()); + } + + private enum ParticleShaderTextureSlot { + TEXTURE, + LIGHT, + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java index 9e3d4bfff1..4c64e4cb1a 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java @@ -1,6 +1,6 @@ package me.jellysquid.mods.sodium.mixin.features.render.particle; -import net.caffeinemc.mods.sodium.api.vertex.format.common.ParticleVertex; +import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleVertex; import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; import net.caffeinemc.mods.sodium.api.util.ColorABGR; import net.minecraft.client.particle.BillboardParticle; @@ -10,7 +10,6 @@ import net.minecraft.client.world.ClientWorld; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; -import org.joml.Quaternionf; import org.lwjgl.system.MemoryStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; @@ -50,17 +49,6 @@ public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float ti float y = (float) (MathHelper.lerp(tickDelta, this.prevPosY, this.y) - vec3d.getY()); float z = (float) (MathHelper.lerp(tickDelta, this.prevPosZ, this.z) - vec3d.getZ()); - Quaternionf quaternion; - - if (this.angle == 0.0F) { - quaternion = camera.getRotation(); - } else { - float angle = MathHelper.lerp(tickDelta, this.prevAngle, this.angle); - - quaternion = new Quaternionf(camera.getRotation()); - quaternion.rotateZ(angle); - } - float size = this.getSize(tickDelta); int light = this.getBrightness(tickDelta); @@ -71,67 +59,34 @@ public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float ti int color = ColorABGR.pack(this.red , this.green, this.blue, this.alpha); + float angle = MathHelper.lerp(tickDelta, this.prevAngle, this.angle); + var writer = VertexBufferWriter.of(vertexConsumer); try (MemoryStack stack = MemoryStack.stackPush()) { - long buffer = stack.nmalloc(4 * ParticleVertex.STRIDE); + long buffer = stack.nmalloc(4 * BillboardParticleVertex.STRIDE); long ptr = buffer; - writeVertex(ptr, quaternion,-1.0F, -1.0F, x, y, z, maxU, maxV, color, light, size); - ptr += ParticleVertex.STRIDE; + writeVertex(ptr, x, y, z, maxU, maxV, color, light, size, angle); + ptr += BillboardParticleVertex.STRIDE; - writeVertex(ptr, quaternion,-1.0F, 1.0F, x, y, z, maxU, minV, color, light, size); - ptr += ParticleVertex.STRIDE; + writeVertex(ptr, x, y, z, maxU, minV, color, light, size, angle); + ptr += BillboardParticleVertex.STRIDE; - writeVertex(ptr, quaternion,1.0F, 1.0F, x, y, z, minU, minV, color, light, size); - ptr += ParticleVertex.STRIDE; + writeVertex(ptr, x, y, z, minU, minV, color, light, size, angle); + ptr += BillboardParticleVertex.STRIDE; - writeVertex(ptr, quaternion,1.0F, -1.0F, x, y, z, minU, maxV, color, light, size); - ptr += ParticleVertex.STRIDE; + writeVertex(ptr, x, y, z, minU, maxV, color, light, size, angle); + ptr += BillboardParticleVertex.STRIDE; - writer.push(stack, buffer, 4, ParticleVertex.FORMAT); + writer.push(stack, buffer, 4, BillboardParticleVertex.VERTEX_FORMAT_DESCRIPTION); } - } @Unique - @SuppressWarnings("UnnecessaryLocalVariable") - private static void writeVertex(long buffer, - Quaternionf rotation, - float posX, float posY, - float originX, float originY, float originZ, - float u, float v, int color, int light, float size) { - // Quaternion q0 = new Quaternion(rotation); - float q0x = rotation.x(); - float q0y = rotation.y(); - float q0z = rotation.z(); - float q0w = rotation.w(); - - // q0.hamiltonProduct(x, y, 0.0f, 0.0f) - float q1x = (q0w * posX) - (q0z * posY); - float q1y = (q0w * posY) + (q0z * posX); - float q1w = (q0x * posY) - (q0y * posX); - float q1z = -(q0x * posX) - (q0y * posY); - - // Quaternion q2 = new Quaternion(rotation); - // q2.conjugate() - float q2x = -q0x; - float q2y = -q0y; - float q2z = -q0z; - float q2w = q0w; - - // q2.hamiltonProduct(q1) - float q3x = q1z * q2x + q1x * q2w + q1y * q2z - q1w * q2y; - float q3y = q1z * q2y - q1x * q2z + q1y * q2w + q1w * q2x; - float q3z = q1z * q2z + q1x * q2y - q1y * q2x + q1w * q2w; - - // Vector3f f = new Vector3f(q2.getX(), q2.getY(), q2.getZ()) - // f.multiply(size) - // f.add(pos) - float fx = (q3x * size) + originX; - float fy = (q3y * size) + originY; - float fz = (q3z * size) + originZ; - - ParticleVertex.put(buffer, fx, fy, fz, u, v, color, light); + private static void writeVertex(long buffer, float originX, float originY, float originZ, + float u, float v, int color, int light, float size, float angle) { + + BillboardParticleVertex.put(buffer, originX, originY, originZ, u, v, color, light, size, angle); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java new file mode 100644 index 0000000000..0a2c24c292 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -0,0 +1,193 @@ +package me.jellysquid.mods.sodium.mixin.features.render.particle; + +import com.google.common.collect.EvictingQueue; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mojang.blaze3d.platform.GlConst; +import com.mojang.blaze3d.systems.RenderSystem; +import me.jellysquid.mods.sodium.client.render.particle.ShaderBillboardParticleRenderer; +import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleVertex; +import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.gl.VertexBuffer; +import net.minecraft.client.particle.*; +import net.minecraft.client.render.*; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.client.texture.TextureManager; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.util.crash.CrashException; +import net.minecraft.util.crash.CrashReport; +import net.minecraft.util.crash.CrashReportSection; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.*; + +@Mixin(ParticleManager.class) +public abstract class ParticleManagerMixin { + @Unique + private final BufferBuilder bufferBuilder = new BufferBuilder(1); + + @Shadow + protected ClientWorld world; + + @Shadow + @Final + private static List PARTICLE_TEXTURE_SHEETS; + + @Shadow + @Final + private Queue newEmitterParticles; + + @Shadow + @Final + private Queue newParticles; + + @Shadow + @Final + private Map> particles; + + @Unique + private final Map> billboardParticles = Maps.newIdentityHashMap(); + + @Unique + private final ShaderBillboardParticleRenderer particleRenderer = new ShaderBillboardParticleRenderer(); + + @Unique + private static final Map, Boolean> classOverridesBuild = Maps.newIdentityHashMap(); + + @Unique + private static final String BUILD_GEOMETRY_METHOD = FabricLoader.getInstance().getMappingResolver().mapMethodName( + "intermediary", + "net.minecraft.class_703", + "method_3074", + "(Lnet/minecraft/class_4588;Lnet/minecraft/class_4184;F)V" + ); + + @Shadow + protected abstract void tickParticles(Collection particles); + + /** + * @author BeljihnWahfl + * @reason Could not feasibly inject all needed functionality + */ + @Overwrite + public void tick() { + this.particles.forEach((sheet, queue) -> { + this.world.getProfiler().push(sheet.toString()); + this.tickParticles(queue); + this.world.getProfiler().pop(); + }); + + this.billboardParticles.forEach((sheet, queue) -> { + this.world.getProfiler().push(sheet.toString()); + // This is safe because tickParticles never adds to the collection. + this.tickParticles((Collection) queue); + this.world.getProfiler().pop(); + }); + + if (!this.newEmitterParticles.isEmpty()) { + List list = Lists.newArrayList(); + + for(EmitterParticle emitterParticle : this.newEmitterParticles) { + emitterParticle.tick(); + if (!emitterParticle.isAlive()) { + list.add(emitterParticle); + } + } + + this.newEmitterParticles.removeAll(list); + } + + Particle particle; + if (!this.newParticles.isEmpty()) { + while((particle = this.newParticles.poll()) != null) { + if (particle instanceof BillboardParticle bParticle && !classOverridesBuild.computeIfAbsent( + bParticle.getClass(), + this::testClassOverrides + )) { + this.billboardParticles + .computeIfAbsent(particle.getType(), sheet -> EvictingQueue.create(16384)) + .add((BillboardParticle) particle); + } else { + this.particles + .computeIfAbsent(particle.getType(), sheet -> EvictingQueue.create(16384)) + .add(particle); + } + } + } + } + + @Unique + private boolean testClassOverrides(Class particleClass) { + try { + return particleClass.getDeclaredMethod( + BUILD_GEOMETRY_METHOD, + VertexConsumer.class, + Camera.class, + float.class + ).getDeclaringClass() != BillboardParticle.class; + } catch (NoSuchMethodException e) { + return false; + } + } + + @Inject(method = "clearParticles", at = @At("TAIL")) + private void clearParticles(CallbackInfo ci) { + this.billboardParticles.clear(); + } + + @Inject(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;pop()V"), locals = LocalCapture.CAPTURE_FAILHARD) + public void renderParticles( + MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers, + LightmapTextureManager lightmapTextureManager, Camera camera, float tickDelta, + CallbackInfo ci, MatrixStack matrixStack + ) { + for(ParticleTextureSheet particleTextureSheet : PARTICLE_TEXTURE_SHEETS) { + Queue iterable = this.billboardParticles.get(particleTextureSheet); + if (iterable != null && !iterable.isEmpty()) { + bindParticleTextureSheet(particleTextureSheet); + bufferBuilder.begin(VertexFormat.DrawMode.QUADS, BillboardParticleVertex.MC_VERTEX_FORMAT); + + particleRenderer.begin(); + ParticleShaderInterface shader = this.particleRenderer.getActiveProgram().getInterface(); + shader.setProjectionMatrix(RenderSystem.getProjectionMatrix()); + shader.setModelViewMatrix(RenderSystem.getModelViewMatrix()); + shader.setCameraRotation(camera.getRotation()); + + for(BillboardParticle particle : iterable) { + particle.buildGeometry(bufferBuilder, camera, tickDelta); + } + + BufferBuilder.BuiltBuffer built = bufferBuilder.end(); + VertexBuffer buffer = built.getParameters().format().getBuffer(); + + buffer.bind(); + buffer.upload(built); + BillboardParticleVertex.bindVertexFormat(); + + int numParticles = iterable.size(); + int indexType = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS).getIndexType().glType; + RenderSystem.drawElements(4, numParticles * 6, indexType); + + particleRenderer.end(); + } + } + } + + @Unique + private static void bindParticleTextureSheet(ParticleTextureSheet sheet) { + if (sheet == ParticleTextureSheet.PARTICLE_SHEET_LIT || + sheet == ParticleTextureSheet.PARTICLE_SHEET_OPAQUE || + sheet == ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT + ) { + RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE); + } else if (sheet == ParticleTextureSheet.TERRAIN_SHEET) { + RenderSystem.setShaderTexture(0, SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE); + } + } +} diff --git a/src/main/resources/assets/sodium/shaders/blocks/block_layer_opaque.vsh b/src/main/resources/assets/sodium/shaders/blocks/block_layer_opaque.vsh index 0cc39d32b8..68e7c02461 100644 --- a/src/main/resources/assets/sodium/shaders/blocks/block_layer_opaque.vsh +++ b/src/main/resources/assets/sodium/shaders/blocks/block_layer_opaque.vsh @@ -2,7 +2,7 @@ #import #import -#import +#import #import out vec3 v_ColorModulator; diff --git a/src/main/resources/assets/sodium/shaders/include/chunk_matrices.glsl b/src/main/resources/assets/sodium/shaders/include/matrices.glsl similarity index 100% rename from src/main/resources/assets/sodium/shaders/include/chunk_matrices.glsl rename to src/main/resources/assets/sodium/shaders/include/matrices.glsl diff --git a/src/main/resources/assets/sodium/shaders/particles/particle.fsh b/src/main/resources/assets/sodium/shaders/particles/particle.fsh new file mode 100644 index 0000000000..540c96879e --- /dev/null +++ b/src/main/resources/assets/sodium/shaders/particles/particle.fsh @@ -0,0 +1,16 @@ +#version 330 core + +uniform sampler2D u_ParticleTex; + +in vec2 texCoord0; +in vec4 vertexColor; + +out vec4 out_FragColor; + +void main() { + vec4 color = texture(u_ParticleTex, texCoord0) * vertexColor; + if (color.a < 0.1) { + discard; + } + out_FragColor = color; +} diff --git a/src/main/resources/assets/sodium/shaders/particles/particle.vsh b/src/main/resources/assets/sodium/shaders/particles/particle.vsh new file mode 100644 index 0000000000..9729da9e3a --- /dev/null +++ b/src/main/resources/assets/sodium/shaders/particles/particle.vsh @@ -0,0 +1,62 @@ +#version 330 + +#import + +const vec2[] OFFSETS = vec2[]( + vec2(-1.0, -1.0), + vec2(-1.0, 1.0), + vec2( 1.0, 1.0), + vec2( 1.0, -1.0) +); + +in vec3 in_Position; +in float in_Size; +in vec2 in_TexCoord; +in vec4 in_Color; +in ivec2 in_Light; +in float in_Angle; + +uniform vec4 u_CameraRotation; +uniform sampler2D u_LightTex; + +out vec2 texCoord0; +out vec4 vertexColor; + +void main() { + int vertexId = gl_VertexID & 3; + vec2 pos = OFFSETS[vertexId]; + + vec4 q0; + if (in_Angle == 0.0) { + q0 = u_CameraRotation; + } else { + float sin = sin(in_Angle * 0.5); + float cos = cos(in_Angle * 0.5); + q0 = vec4( + u_CameraRotation.x * cos + u_CameraRotation.y * sin, + u_CameraRotation.y * cos - u_CameraRotation.x * sin, + u_CameraRotation.w * sin + u_CameraRotation.z * cos, + u_CameraRotation.w * cos - u_CameraRotation.z * sin + ); + } + + vec4 q2 = vec4(-q0.xyz, q0.w); + vec4 q1 = vec4( + (q0.w * pos.x) - (q0.z * pos.y), + (q0.w * pos.y) + (q0.z * pos.x), + -(q0.x * pos.x) - (q0.y * pos.y), + (q0.x * pos.y) - (q0.y * pos.x) + ); + + vec3 q3 = vec3( + q1.z * q2.x + q1.x * q2.w + q1.y * q2.z - q1.w * q2.y, + q1.z * q2.y - q1.x * q2.z + q1.y * q2.w + q1.w * q2.x, + q1.z * q2.z + q1.x * q2.y - q1.y * q2.x + q1.w * q2.w + ); + + vec3 f = q3 * in_Size + in_Position; + + gl_Position = u_ProjectionMatrix * u_ModelViewMatrix * vec4(f, 1.0); + texCoord0 = in_TexCoord; + vertexColor = in_Color * texelFetch(u_LightTex, in_Light / 16, 0); +} diff --git a/src/main/resources/sodium.mixins.json b/src/main/resources/sodium.mixins.json index b1f1d6d052..4dbb5aebe2 100644 --- a/src/main/resources/sodium.mixins.json +++ b/src/main/resources/sodium.mixins.json @@ -1,15 +1,15 @@ { - "package" : "me.jellysquid.mods.sodium.mixin", - "required" : true, - "compatibilityLevel" : "JAVA_17", - "plugin" : "me.jellysquid.mods.sodium.mixin.SodiumMixinPlugin", - "injectors" : { - "defaultRequire" : 1 + "package": "me.jellysquid.mods.sodium.mixin", + "required": true, + "compatibilityLevel": "JAVA_17", + "plugin": "me.jellysquid.mods.sodium.mixin.SodiumMixinPlugin", + "injectors": { + "defaultRequire": 1 }, - "overwrites" : { - "conformVisibility" : true + "overwrites": { + "conformVisibility": true }, - "client" : [ + "client": [ "core.MinecraftClientMixin", "core.WindowMixin", "core.model.colors.BlockColorsMixin", @@ -59,6 +59,7 @@ "features.render.model.block.BlockModelRendererMixin", "features.render.model.item.ItemRendererMixin", "features.render.particle.BillboardParticleMixin", + "features.render.particle.ParticleManagerMixin", "features.render.world.clouds.BackgroundRendererInvoker", "features.render.world.clouds.WorldRendererMixin", "features.render.world.sky.BackgroundRendererMixin", From 17c3c2ed805c16dab41a3dbe188e4357c024720b Mon Sep 17 00:00:00 2001 From: IMS212 Date: Sat, 23 Sep 2023 07:49:02 -0700 Subject: [PATCH 02/30] Optimize code; fix translucent particles The item pickup animation is not working for some reason. --- .../shader/ParticleShaderInterface.java | 10 ---- .../render/particle/ParticleManagerMixin.java | 22 +++++--- .../sodium/shaders/particles/particle.vsh | 53 +++++-------------- 3 files changed, 29 insertions(+), 56 deletions(-) diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java index 492da6c891..f39d555edf 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java @@ -15,14 +15,12 @@ public class ParticleShaderInterface { private final GlUniformInt uniformLightTexture; private final GlUniformMatrix4f uniformModelViewMatrix; private final GlUniformMatrix4f uniformProjectionMatrix; - private final GlUniformFloat4v uniformCameraRotation; public ParticleShaderInterface(ShaderBindingContext context) { this.uniformParticleTexture = context.bindUniform("u_ParticleTex", GlUniformInt::new); this.uniformLightTexture = context.bindUniform("u_LightTex", GlUniformInt::new); this.uniformModelViewMatrix = context.bindUniform("u_ModelViewMatrix", GlUniformMatrix4f::new); this.uniformProjectionMatrix = context.bindUniform("u_ProjectionMatrix", GlUniformMatrix4f::new); - this.uniformCameraRotation = context.bindUniform("u_CameraRotation", GlUniformFloat4v::new); } public void setProjectionMatrix(Matrix4fc matrix) { @@ -32,14 +30,6 @@ public void setProjectionMatrix(Matrix4fc matrix) { public void setModelViewMatrix(Matrix4fc matrix) { this.uniformModelViewMatrix.set(matrix); } - public void setCameraRotation(Quaternionfc quaternion) { - this.uniformCameraRotation.set(new float[] { - quaternion.x(), - quaternion.y(), - quaternion.z(), - quaternion.w(), - }); - } public void setupState() { // "BlockTexture" should represent the particle textures if bound correctly diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index 0a2c24c292..a146afe2df 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -141,12 +141,13 @@ private void clearParticles(CallbackInfo ci) { this.billboardParticles.clear(); } - @Inject(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;pop()V"), locals = LocalCapture.CAPTURE_FAILHARD) + @Inject(method = "renderParticles", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;applyModelViewMatrix()V", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) public void renderParticles( MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers, LightmapTextureManager lightmapTextureManager, Camera camera, float tickDelta, CallbackInfo ci, MatrixStack matrixStack ) { + ci.cancel(); for(ParticleTextureSheet particleTextureSheet : PARTICLE_TEXTURE_SHEETS) { Queue iterable = this.billboardParticles.get(particleTextureSheet); if (iterable != null && !iterable.isEmpty()) { @@ -157,7 +158,6 @@ public void renderParticles( ParticleShaderInterface shader = this.particleRenderer.getActiveProgram().getInterface(); shader.setProjectionMatrix(RenderSystem.getProjectionMatrix()); shader.setModelViewMatrix(RenderSystem.getModelViewMatrix()); - shader.setCameraRotation(camera.getRotation()); for(BillboardParticle particle : iterable) { particle.buildGeometry(bufferBuilder, camera, tickDelta); @@ -177,16 +177,26 @@ public void renderParticles( particleRenderer.end(); } } + + matrixStack.pop(); + RenderSystem.applyModelViewMatrix(); + RenderSystem.depthMask(true); + RenderSystem.disableBlend(); + lightmapTextureManager.disable(); } @Unique private static void bindParticleTextureSheet(ParticleTextureSheet sheet) { - if (sheet == ParticleTextureSheet.PARTICLE_SHEET_LIT || - sheet == ParticleTextureSheet.PARTICLE_SHEET_OPAQUE || - sheet == ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT - ) { + RenderSystem.depthMask(true); + if (sheet == ParticleTextureSheet.PARTICLE_SHEET_LIT || sheet == ParticleTextureSheet.PARTICLE_SHEET_OPAQUE) { + RenderSystem.disableBlend(); + RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE); + } else if (sheet == ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT) { + RenderSystem.enableBlend(); RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE); } else if (sheet == ParticleTextureSheet.TERRAIN_SHEET) { + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); RenderSystem.setShaderTexture(0, SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE); } } diff --git a/src/main/resources/assets/sodium/shaders/particles/particle.vsh b/src/main/resources/assets/sodium/shaders/particles/particle.vsh index 9729da9e3a..091a5e06a3 100644 --- a/src/main/resources/assets/sodium/shaders/particles/particle.vsh +++ b/src/main/resources/assets/sodium/shaders/particles/particle.vsh @@ -2,11 +2,11 @@ #import -const vec2[] OFFSETS = vec2[]( - vec2(-1.0, -1.0), - vec2(-1.0, 1.0), - vec2( 1.0, 1.0), - vec2( 1.0, -1.0) +const vec2 OFFSETS[] = vec2[]( + vec2(-1, -1), + vec2(1, -1), + vec2(1, 1), + vec2(-1, 1) ); in vec3 in_Position; @@ -22,41 +22,14 @@ uniform sampler2D u_LightTex; out vec2 texCoord0; out vec4 vertexColor; -void main() { - int vertexId = gl_VertexID & 3; - vec2 pos = OFFSETS[vertexId]; - - vec4 q0; - if (in_Angle == 0.0) { - q0 = u_CameraRotation; - } else { - float sin = sin(in_Angle * 0.5); - float cos = cos(in_Angle * 0.5); - q0 = vec4( - u_CameraRotation.x * cos + u_CameraRotation.y * sin, - u_CameraRotation.y * cos - u_CameraRotation.x * sin, - u_CameraRotation.w * sin + u_CameraRotation.z * cos, - u_CameraRotation.w * cos - u_CameraRotation.z * sin - ); - } - - vec4 q2 = vec4(-q0.xyz, q0.w); - vec4 q1 = vec4( - (q0.w * pos.x) - (q0.z * pos.y), - (q0.w * pos.y) + (q0.z * pos.x), - -(q0.x * pos.x) - (q0.y * pos.y), - (q0.x * pos.y) - (q0.y * pos.x) - ); +// The following code is an optimized variant of Minecraft's quaternion code by Zombye and Balint. - vec3 q3 = vec3( - q1.z * q2.x + q1.x * q2.w + q1.y * q2.z - q1.w * q2.y, - q1.z * q2.y - q1.x * q2.z + q1.y * q2.w + q1.w * q2.x, - q1.z * q2.z + q1.x * q2.y - q1.y * q2.x + q1.w * q2.w - ); - - vec3 f = q3 * in_Size + in_Position; - - gl_Position = u_ProjectionMatrix * u_ModelViewMatrix * vec4(f, 1.0); +void main() { + gl_Position = u_ModelViewMatrix * vec4(in_Position, 1.0); + float s = sin(in_Angle); + float c = cos(in_Angle); + gl_Position.xy += mat2(c,-s, s, c) * OFFSETS[gl_VertexID & 3] * in_Size; + gl_Position = u_ProjectionMatrix * gl_Position; texCoord0 = in_TexCoord; vertexColor = in_Color * texelFetch(u_LightTex, in_Light / 16, 0); -} +} \ No newline at end of file From 10e35477e73c9c1506b65736e8603a4e690d9176 Mon Sep 17 00:00:00 2001 From: IMS212 Date: Sat, 23 Sep 2023 08:10:47 -0700 Subject: [PATCH 03/30] Some changes --- .../render/particle/ParticleManagerMixin.java | 64 +++++++++++-------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index a146afe2df..e7ab4fd9a2 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -5,6 +5,8 @@ import com.google.common.collect.Maps; import com.mojang.blaze3d.platform.GlConst; import com.mojang.blaze3d.systems.RenderSystem; +import it.unimi.dsi.fastutil.objects.Object2BooleanMap; +import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; import me.jellysquid.mods.sodium.client.render.particle.ShaderBillboardParticleRenderer; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleVertex; import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; @@ -58,7 +60,7 @@ public abstract class ParticleManagerMixin { private final ShaderBillboardParticleRenderer particleRenderer = new ShaderBillboardParticleRenderer(); @Unique - private static final Map, Boolean> classOverridesBuild = Maps.newIdentityHashMap(); + private static final Object2BooleanMap> classOverridesBuild = new Object2BooleanOpenHashMap<>(); @Unique private static final String BUILD_GEOMETRY_METHOD = FabricLoader.getInstance().getMappingResolver().mapMethodName( @@ -141,48 +143,40 @@ private void clearParticles(CallbackInfo ci) { this.billboardParticles.clear(); } - @Inject(method = "renderParticles", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;applyModelViewMatrix()V", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) + @Inject(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;pop()V"), locals = LocalCapture.CAPTURE_FAILHARD) public void renderParticles( MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers, LightmapTextureManager lightmapTextureManager, Camera camera, float tickDelta, CallbackInfo ci, MatrixStack matrixStack ) { - ci.cancel(); - for(ParticleTextureSheet particleTextureSheet : PARTICLE_TEXTURE_SHEETS) { + + RenderSystem.depthMask(true); + RenderSystem.disableBlend(); + + particleRenderer.begin(); + ParticleShaderInterface shader = this.particleRenderer.getActiveProgram().getInterface(); + shader.setProjectionMatrix(RenderSystem.getProjectionMatrix()); + shader.setModelViewMatrix(RenderSystem.getModelViewMatrix()); + + for (ParticleTextureSheet particleTextureSheet : PARTICLE_TEXTURE_SHEETS) { + if (particleTextureSheet == ParticleTextureSheet.NO_RENDER) continue; + Queue iterable = this.billboardParticles.get(particleTextureSheet); if (iterable != null && !iterable.isEmpty()) { bindParticleTextureSheet(particleTextureSheet); - bufferBuilder.begin(VertexFormat.DrawMode.QUADS, BillboardParticleVertex.MC_VERTEX_FORMAT); - particleRenderer.begin(); - ParticleShaderInterface shader = this.particleRenderer.getActiveProgram().getInterface(); - shader.setProjectionMatrix(RenderSystem.getProjectionMatrix()); - shader.setModelViewMatrix(RenderSystem.getModelViewMatrix()); + bufferBuilder.begin(VertexFormat.DrawMode.QUADS, BillboardParticleVertex.MC_VERTEX_FORMAT); - for(BillboardParticle particle : iterable) { + for (BillboardParticle particle : iterable) { particle.buildGeometry(bufferBuilder, camera, tickDelta); } - BufferBuilder.BuiltBuffer built = bufferBuilder.end(); - VertexBuffer buffer = built.getParameters().format().getBuffer(); - - buffer.bind(); - buffer.upload(built); - BillboardParticleVertex.bindVertexFormat(); - - int numParticles = iterable.size(); - int indexType = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS).getIndexType().glType; - RenderSystem.drawElements(4, numParticles * 6, indexType); + drawParticleTextureSheet(particleTextureSheet, bufferBuilder, iterable.size()); - particleRenderer.end(); } } - matrixStack.pop(); - RenderSystem.applyModelViewMatrix(); - RenderSystem.depthMask(true); - RenderSystem.disableBlend(); - lightmapTextureManager.disable(); + particleRenderer.end(); } @Unique @@ -198,6 +192,24 @@ private static void bindParticleTextureSheet(ParticleTextureSheet sheet) { RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); RenderSystem.setShaderTexture(0, SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE); + } else if (sheet == ParticleTextureSheet.CUSTOM) { + RenderSystem.depthMask(true); + RenderSystem.disableBlend(); + } + } + + @Unique + private static void drawParticleTextureSheet(ParticleTextureSheet sheet, BufferBuilder builder, int numParticles) { + if (sheet == ParticleTextureSheet.TERRAIN_SHEET || sheet == ParticleTextureSheet.PARTICLE_SHEET_LIT || sheet == ParticleTextureSheet.PARTICLE_SHEET_OPAQUE || sheet == ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT) { + BufferBuilder.BuiltBuffer built = builder.end(); + VertexBuffer buffer = built.getParameters().format().getBuffer(); + + buffer.bind(); + buffer.upload(built); + BillboardParticleVertex.bindVertexFormat(); + + int indexType = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS).getIndexType().glType; + RenderSystem.drawElements(4, numParticles * 6, indexType); } } } From 99ee29b46442708ae2fb30a6fa5d1ad5aa7de3af Mon Sep 17 00:00:00 2001 From: IMS212 Date: Sat, 23 Sep 2023 08:35:12 -0700 Subject: [PATCH 04/30] Fix multiple issues --- .../particle/ShaderBillboardParticleRenderer.java | 3 +++ .../render/particle/ParticleManagerMixin.java | 13 ++++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java index 66a7cf2808..aba3023ff6 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java @@ -47,6 +47,9 @@ public void begin() { // pass.startDrawing(); .. Do I need a pass? this.activeProgram.bind(); + } + + public void setupState() { this.activeProgram.getInterface().setupState(); } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index e7ab4fd9a2..92bd0b245e 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -110,7 +110,7 @@ public void tick() { while((particle = this.newParticles.poll()) != null) { if (particle instanceof BillboardParticle bParticle && !classOverridesBuild.computeIfAbsent( bParticle.getClass(), - this::testClassOverrides + (pClass) -> this.testClassOverrides(bParticle.getClass()) )) { this.billboardParticles .computeIfAbsent(particle.getType(), sheet -> EvictingQueue.create(16384)) @@ -143,16 +143,12 @@ private void clearParticles(CallbackInfo ci) { this.billboardParticles.clear(); } - @Inject(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;pop()V"), locals = LocalCapture.CAPTURE_FAILHARD) + @Inject(method = "renderParticles", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;applyModelViewMatrix()V", ordinal = 0, shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD) public void renderParticles( MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers, LightmapTextureManager lightmapTextureManager, Camera camera, float tickDelta, CallbackInfo ci, MatrixStack matrixStack ) { - - RenderSystem.depthMask(true); - RenderSystem.disableBlend(); - particleRenderer.begin(); ParticleShaderInterface shader = this.particleRenderer.getActiveProgram().getInterface(); shader.setProjectionMatrix(RenderSystem.getProjectionMatrix()); @@ -164,7 +160,7 @@ public void renderParticles( Queue iterable = this.billboardParticles.get(particleTextureSheet); if (iterable != null && !iterable.isEmpty()) { bindParticleTextureSheet(particleTextureSheet); - + particleRenderer.setupState(); bufferBuilder.begin(VertexFormat.DrawMode.QUADS, BillboardParticleVertex.MC_VERTEX_FORMAT); for (BillboardParticle particle : iterable) { @@ -208,8 +204,7 @@ private static void drawParticleTextureSheet(ParticleTextureSheet sheet, BufferB buffer.upload(built); BillboardParticleVertex.bindVertexFormat(); - int indexType = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS).getIndexType().glType; - RenderSystem.drawElements(4, numParticles * 6, indexType); + buffer.draw(); } } } From 941189c9f3601dcbbab9cc302848bbdc489e28ff Mon Sep 17 00:00:00 2001 From: IMS212 Date: Sat, 23 Sep 2023 09:14:59 -0700 Subject: [PATCH 05/30] Introduce new test buffer system --- .../render/particle/ExtendedParticle.java | 5 +++ .../particle/BillboardParticleMixin.java | 16 +++++++++- .../render/particle/ParticleManagerMixin.java | 31 +++++++------------ 3 files changed, 32 insertions(+), 20 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/ExtendedParticle.java diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ExtendedParticle.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ExtendedParticle.java new file mode 100644 index 0000000000..6c27ceca80 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ExtendedParticle.java @@ -0,0 +1,5 @@ +package me.jellysquid.mods.sodium.client.render.particle; + +public interface ExtendedParticle { + boolean sodium$reachedBillboardDraw(); +} diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java index 4c64e4cb1a..ce909925e6 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java @@ -1,5 +1,6 @@ package me.jellysquid.mods.sodium.mixin.features.render.particle; +import me.jellysquid.mods.sodium.client.render.particle.ExtendedParticle; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleVertex; import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; import net.caffeinemc.mods.sodium.api.util.ColorABGR; @@ -17,7 +18,7 @@ import org.spongepowered.asm.mixin.Unique; @Mixin(BillboardParticle.class) -public abstract class BillboardParticleMixin extends Particle { +public abstract class BillboardParticleMixin extends Particle implements ExtendedParticle { @Shadow public abstract float getSize(float tickDelta); @@ -33,6 +34,14 @@ public abstract class BillboardParticleMixin extends Particle { @Shadow protected abstract float getMaxV(); + @Unique + private boolean reachedBillboardDraw; + + @Override + public boolean sodium$reachedBillboardDraw() { + return reachedBillboardDraw; + } + protected BillboardParticleMixin(ClientWorld world, double x, double y, double z) { super(world, x, y, z); } @@ -43,6 +52,11 @@ protected BillboardParticleMixin(ClientWorld world, double x, double y, double z */ @Overwrite public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float tickDelta) { + if (!reachedBillboardDraw) { + reachedBillboardDraw = true; + return; + } + Vec3d vec3d = camera.getPos(); float x = (float) (MathHelper.lerp(tickDelta, this.prevPosX, this.x) - vec3d.getX()); diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index 92bd0b245e..7a1a63d332 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -3,24 +3,21 @@ import com.google.common.collect.EvictingQueue; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.mojang.blaze3d.platform.GlConst; import com.mojang.blaze3d.systems.RenderSystem; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; +import me.jellysquid.mods.sodium.client.render.particle.ExtendedParticle; import me.jellysquid.mods.sodium.client.render.particle.ShaderBillboardParticleRenderer; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleVertex; import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.VertexBuffer; import net.minecraft.client.particle.*; import net.minecraft.client.render.*; import net.minecraft.client.texture.SpriteAtlasTexture; -import net.minecraft.client.texture.TextureManager; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; -import net.minecraft.util.crash.CrashException; -import net.minecraft.util.crash.CrashReport; -import net.minecraft.util.crash.CrashReportSection; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -34,6 +31,9 @@ public abstract class ParticleManagerMixin { @Unique private final BufferBuilder bufferBuilder = new BufferBuilder(1); + @Unique + private final BufferBuilder testBuffer = new BufferBuilder(1); + @Shadow protected ClientWorld world; @@ -79,6 +79,7 @@ public abstract class ParticleManagerMixin { */ @Overwrite public void tick() { + testBuffer.begin(VertexFormat.DrawMode.QUADS, BillboardParticleVertex.MC_VERTEX_FORMAT); this.particles.forEach((sheet, queue) -> { this.world.getProfiler().push(sheet.toString()); this.tickParticles(queue); @@ -110,7 +111,7 @@ public void tick() { while((particle = this.newParticles.poll()) != null) { if (particle instanceof BillboardParticle bParticle && !classOverridesBuild.computeIfAbsent( bParticle.getClass(), - (pClass) -> this.testClassOverrides(bParticle.getClass()) + (pClass) -> this.testClassOverrides(bParticle) )) { this.billboardParticles .computeIfAbsent(particle.getType(), sheet -> EvictingQueue.create(16384)) @@ -122,20 +123,14 @@ public void tick() { } } } + + testBuffer.end().release(); } @Unique - private boolean testClassOverrides(Class particleClass) { - try { - return particleClass.getDeclaredMethod( - BUILD_GEOMETRY_METHOD, - VertexConsumer.class, - Camera.class, - float.class - ).getDeclaringClass() != BillboardParticle.class; - } catch (NoSuchMethodException e) { - return false; - } + private boolean testClassOverrides(BillboardParticle particle) { + particle.buildGeometry(testBuffer, MinecraftClient.getInstance().gameRenderer.getCamera(), 0); + return !((ExtendedParticle) particle).sodium$reachedBillboardDraw(); } @Inject(method = "clearParticles", at = @At("TAIL")) @@ -155,8 +150,6 @@ public void renderParticles( shader.setModelViewMatrix(RenderSystem.getModelViewMatrix()); for (ParticleTextureSheet particleTextureSheet : PARTICLE_TEXTURE_SHEETS) { - if (particleTextureSheet == ParticleTextureSheet.NO_RENDER) continue; - Queue iterable = this.billboardParticles.get(particleTextureSheet); if (iterable != null && !iterable.isEmpty()) { bindParticleTextureSheet(particleTextureSheet); From c7b7901077fc639846e9db8a7d233cc565b070c7 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Mon, 25 Sep 2023 18:25:06 -0500 Subject: [PATCH 06/30] Cache world lighting for particles (patch by jelly) --- .../render/particle/ParticleExtended.java | 5 ++ .../render/particle/ParticleRenderView.java | 83 +++++++++++++++++++ .../render/particle/ParticleManagerMixin.java | 45 ++++++++++ .../render/particle/ParticleMixin.java | 39 +++++++++ src/main/resources/sodium.mixins.json | 19 +++-- 5 files changed, 182 insertions(+), 9 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleRenderView.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleMixin.java diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java new file mode 100644 index 0000000000..731e6b439a --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java @@ -0,0 +1,5 @@ +package me.jellysquid.mods.sodium.client.render.particle; + +public interface ParticleExtended { + void sodium$configure(ParticleRenderView renderView); +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleRenderView.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleRenderView.java new file mode 100644 index 0000000000..7ce3146bf8 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleRenderView.java @@ -0,0 +1,83 @@ +package me.jellysquid.mods.sodium.client.render.particle; + +import it.unimi.dsi.fastutil.longs.Long2ReferenceLinkedOpenHashMap; +import net.minecraft.client.render.LightmapTextureManager; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.world.LightType; +import net.minecraft.world.World; +import net.minecraft.world.chunk.ChunkNibbleArray; +import org.jetbrains.annotations.NotNull; + +public class ParticleRenderView { + private static final int CACHE_SIZE = 2048; + + private final Long2ReferenceLinkedOpenHashMap lightCache = new Long2ReferenceLinkedOpenHashMap<>(CACHE_SIZE); + private final World world; + + public ParticleRenderView(World world) { + this.world = world; + } + + public int getBrightness(int x, int y, int z) { + var arrays = this.getCachedLightArrays(x >> 4, y >> 4, z >> 4); + + int skyLight = getLightValue(arrays.skyLight, x, y, z); + int blockLight = getLightValue(arrays.blockLight, x, y, z); + + return LightmapTextureManager.pack(blockLight, skyLight); + } + + private static int getLightValue(ChunkNibbleArray array, int x, int y, int z) { + return array == null ? 0 : array.get(x & 15, y & 15, z & 15); + } + + private LightArrays getCachedLightArrays(int x, int y, int z) { + var position = ChunkSectionPos.asLong(x, y, z); + + LightArrays entry = this.lightCache.get(position); + + if (entry == null) { + entry = this.fetchLightArrays(x, y, z, position); + } + + return entry; + } + + private ParticleRenderView.@NotNull LightArrays fetchLightArrays(int x, int y, int z, long packed) { + LightArrays arrays = LightArrays.load(this.world, ChunkSectionPos.from(x, y, z)); + + if (this.lightCache.size() >= CACHE_SIZE) { + this.lightCache.removeLast(); + } + + this.lightCache.putAndMoveToFirst(packed, arrays); + + return arrays; + } + + public void resetCache() { + this.lightCache.clear(); + } + + private static class LightArrays { + private final ChunkNibbleArray blockLight; + private final ChunkNibbleArray skyLight; + + private LightArrays(ChunkNibbleArray blockLight, ChunkNibbleArray skyLight) { + this.blockLight = blockLight; + this.skyLight = skyLight; + } + + public static LightArrays load(World world, ChunkSectionPos pos) { + var lightingProvider = world.getLightingProvider(); + + var blockLight = lightingProvider.get(LightType.BLOCK) + .getLightSection(pos); + + var skyLight = lightingProvider.get(LightType.SKY) + .getLightSection(pos); + + return new LightArrays(blockLight, skyLight); + } + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index 7a1a63d332..19d3f44625 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -7,6 +7,8 @@ import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; import me.jellysquid.mods.sodium.client.render.particle.ExtendedParticle; +import me.jellysquid.mods.sodium.client.render.particle.ParticleExtended; +import me.jellysquid.mods.sodium.client.render.particle.ParticleRenderView; import me.jellysquid.mods.sodium.client.render.particle.ShaderBillboardParticleRenderer; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleVertex; import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; @@ -16,12 +18,16 @@ import net.minecraft.client.particle.*; import net.minecraft.client.render.*; import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.client.texture.TextureManager; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; +import net.minecraft.particle.ParticleEffect; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import java.util.*; @@ -200,4 +206,43 @@ private static void drawParticleTextureSheet(ParticleTextureSheet sheet, BufferB buffer.draw(); } } + + @Unique + private ParticleRenderView renderView; + + @Inject(method = "", at = @At("RETURN")) + private void postInit(ClientWorld world, TextureManager textureManager, CallbackInfo ci) { + this.renderView = new ParticleRenderView(world); + } + + @Inject(method = "setWorld", at = @At("RETURN")) + private void postSetWorld(ClientWorld world, CallbackInfo ci) { + this.renderView = new ParticleRenderView(world); + } + + @Inject(method = "renderParticles", at = @At("HEAD")) + private void preRenderParticles(MatrixStack matrices, + VertexConsumerProvider.Immediate vertexConsumers, + LightmapTextureManager lightmapTextureManager, + Camera camera, + float tickDelta, + CallbackInfo ci) { + this.renderView.resetCache(); + } + + @Inject(method = "createParticle", at = @At("RETURN")) + private void postCreateParticle(T parameters, + double x, + double y, + double z, + double velocityX, + double velocityY, + double velocityZ, + CallbackInfoReturnable<@Nullable Particle> cir) { + var particle = cir.getReturnValue(); + + if (particle instanceof ParticleExtended extension) { + extension.sodium$configure(this.renderView); + } + } } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleMixin.java new file mode 100644 index 0000000000..f76ccd0ee3 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleMixin.java @@ -0,0 +1,39 @@ +package me.jellysquid.mods.sodium.mixin.features.render.particle; + +import me.jellysquid.mods.sodium.client.render.particle.ParticleExtended; +import me.jellysquid.mods.sodium.client.render.particle.ParticleRenderView; +import net.minecraft.client.particle.Particle; +import net.minecraft.util.math.MathHelper; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(Particle.class) +public class ParticleMixin implements ParticleExtended { + @Shadow + protected double x; + @Shadow + protected double y; + @Shadow + protected double z; + + @Unique + private ParticleRenderView renderView; + + @Override + public void sodium$configure(ParticleRenderView renderView) { + this.renderView = renderView; + } + + /** + * @author JellySquid + * @reason Use render cache + */ + @Overwrite + public int getBrightness(float tickDelta) { + return this.renderView.getBrightness(MathHelper.floor(this.x), + MathHelper.floor(this.y), + MathHelper.floor(this.z)); + } +} diff --git a/src/main/resources/sodium.mixins.json b/src/main/resources/sodium.mixins.json index 4dbb5aebe2..6e06f2c312 100644 --- a/src/main/resources/sodium.mixins.json +++ b/src/main/resources/sodium.mixins.json @@ -1,15 +1,15 @@ { - "package": "me.jellysquid.mods.sodium.mixin", - "required": true, - "compatibilityLevel": "JAVA_17", - "plugin": "me.jellysquid.mods.sodium.mixin.SodiumMixinPlugin", - "injectors": { - "defaultRequire": 1 + "package" : "me.jellysquid.mods.sodium.mixin", + "required" : true, + "compatibilityLevel" : "JAVA_17", + "plugin" : "me.jellysquid.mods.sodium.mixin.SodiumMixinPlugin", + "injectors" : { + "defaultRequire" : 1 }, - "overwrites": { - "conformVisibility": true + "overwrites" : { + "conformVisibility" : true }, - "client": [ + "client" : [ "core.MinecraftClientMixin", "core.WindowMixin", "core.model.colors.BlockColorsMixin", @@ -60,6 +60,7 @@ "features.render.model.item.ItemRendererMixin", "features.render.particle.BillboardParticleMixin", "features.render.particle.ParticleManagerMixin", + "features.render.particle.ParticleMixin", "features.render.world.clouds.BackgroundRendererInvoker", "features.render.world.clouds.WorldRendererMixin", "features.render.world.sky.BackgroundRendererMixin", From dec37f6a1439260c125dc7279c66c697d78083c2 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Mon, 25 Sep 2023 19:09:19 -0500 Subject: [PATCH 07/30] Optimize so we are less memory bound (we are still memory bound) --- .../render/particle/ParticleManagerMixin.java | 128 +++++++++++++----- 1 file changed, 93 insertions(+), 35 deletions(-) diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index 19d3f44625..5adba33a77 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -3,18 +3,17 @@ import com.google.common.collect.EvictingQueue; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.mojang.blaze3d.platform.GlConst; +import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; -import me.jellysquid.mods.sodium.client.render.particle.ExtendedParticle; import me.jellysquid.mods.sodium.client.render.particle.ParticleExtended; import me.jellysquid.mods.sodium.client.render.particle.ParticleRenderView; import me.jellysquid.mods.sodium.client.render.particle.ShaderBillboardParticleRenderer; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleVertex; import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gl.VertexBuffer; import net.minecraft.client.particle.*; import net.minecraft.client.render.*; import net.minecraft.client.texture.SpriteAtlasTexture; @@ -22,7 +21,9 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; import net.minecraft.particle.ParticleEffect; +import net.minecraft.util.Identifier; import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL20; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -30,7 +31,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import java.util.*; +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Queue; @Mixin(ParticleManager.class) public abstract class ParticleManagerMixin { @@ -68,6 +73,9 @@ public abstract class ParticleManagerMixin { @Unique private static final Object2BooleanMap> classOverridesBuild = new Object2BooleanOpenHashMap<>(); + @Unique + private ParticleRenderView renderView; + @Unique private static final String BUILD_GEOMETRY_METHOD = FabricLoader.getInstance().getMappingResolver().mapMethodName( "intermediary", @@ -76,6 +84,28 @@ public abstract class ParticleManagerMixin { "(Lnet/minecraft/class_4588;Lnet/minecraft/class_4184;F)V" ); + @Unique + private int bufferSize = 0; + + @Unique + private int glVertexBuffer; + + @Unique + private int glVertexArray; + + @Unique + private RenderSystem.ShapeIndexBuffer sharedSequentialIndexBuffer; + + @Unique + private Identifier prevTexture = null; + + @Inject(method = "", at = @At("RETURN")) + private void postInit(ClientWorld world, TextureManager textureManager, CallbackInfo ci) { + this.glVertexBuffer = GlStateManager._glGenBuffers(); + this.glVertexArray = GlStateManager._glGenVertexArrays(); + this.renderView = new ParticleRenderView(world); + } + @Shadow protected abstract void tickParticles(Collection particles); @@ -117,7 +147,7 @@ public void tick() { while((particle = this.newParticles.poll()) != null) { if (particle instanceof BillboardParticle bParticle && !classOverridesBuild.computeIfAbsent( bParticle.getClass(), - (pClass) -> this.testClassOverrides(bParticle) + this::testClassOverrides )) { this.billboardParticles .computeIfAbsent(particle.getType(), sheet -> EvictingQueue.create(16384)) @@ -134,9 +164,17 @@ public void tick() { } @Unique - private boolean testClassOverrides(BillboardParticle particle) { - particle.buildGeometry(testBuffer, MinecraftClient.getInstance().gameRenderer.getCamera(), 0); - return !((ExtendedParticle) particle).sodium$reachedBillboardDraw(); + private boolean testClassOverrides(Class particleClass) { + try { + return particleClass.getDeclaredMethod( + BUILD_GEOMETRY_METHOD, + VertexConsumer.class, + Camera.class, + float.class + ).getDeclaringClass() != BillboardParticle.class; + } catch (NoSuchMethodException e) { + return false; + } } @Inject(method = "clearParticles", at = @At("TAIL")) @@ -144,6 +182,16 @@ private void clearParticles(CallbackInfo ci) { this.billboardParticles.clear(); } + @Inject(method = "renderParticles", at = @At("HEAD")) + private void preRenderParticles(MatrixStack matrices, + VertexConsumerProvider.Immediate vertexConsumers, + LightmapTextureManager lightmapTextureManager, + Camera camera, + float tickDelta, + CallbackInfo ci) { + this.renderView.resetCache(); + } + @Inject(method = "renderParticles", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;applyModelViewMatrix()V", ordinal = 0, shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD) public void renderParticles( MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers, @@ -158,6 +206,7 @@ public void renderParticles( for (ParticleTextureSheet particleTextureSheet : PARTICLE_TEXTURE_SHEETS) { Queue iterable = this.billboardParticles.get(particleTextureSheet); if (iterable != null && !iterable.isEmpty()) { + int numParticles = iterable.size(); bindParticleTextureSheet(particleTextureSheet); particleRenderer.setupState(); bufferBuilder.begin(VertexFormat.DrawMode.QUADS, BillboardParticleVertex.MC_VERTEX_FORMAT); @@ -166,53 +215,72 @@ public void renderParticles( particle.buildGeometry(bufferBuilder, camera, tickDelta); } - drawParticleTextureSheet(particleTextureSheet, bufferBuilder, iterable.size()); - + drawParticleTextureSheet(particleTextureSheet, bufferBuilder, numParticles); + bufferBuilder.clear(); } } + prevTexture = null; particleRenderer.end(); } @Unique - private static void bindParticleTextureSheet(ParticleTextureSheet sheet) { + private void bindParticleTextureSheet(ParticleTextureSheet sheet) { RenderSystem.depthMask(true); + Identifier texture = null; if (sheet == ParticleTextureSheet.PARTICLE_SHEET_LIT || sheet == ParticleTextureSheet.PARTICLE_SHEET_OPAQUE) { RenderSystem.disableBlend(); - RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE); + texture = SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE; } else if (sheet == ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT) { RenderSystem.enableBlend(); - RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE); + texture = SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE; } else if (sheet == ParticleTextureSheet.TERRAIN_SHEET) { RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); - RenderSystem.setShaderTexture(0, SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE); + texture = SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE; } else if (sheet == ParticleTextureSheet.CUSTOM) { RenderSystem.depthMask(true); RenderSystem.disableBlend(); } + + if (texture != null && !texture.equals(prevTexture)) { + RenderSystem.setShaderTexture(0, texture); + this.prevTexture = texture; + } } @Unique - private static void drawParticleTextureSheet(ParticleTextureSheet sheet, BufferBuilder builder, int numParticles) { + private void drawParticleTextureSheet(ParticleTextureSheet sheet, BufferBuilder builder, int numParticles) { if (sheet == ParticleTextureSheet.TERRAIN_SHEET || sheet == ParticleTextureSheet.PARTICLE_SHEET_LIT || sheet == ParticleTextureSheet.PARTICLE_SHEET_OPAQUE || sheet == ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT) { BufferBuilder.BuiltBuffer built = builder.end(); - VertexBuffer buffer = built.getParameters().format().getBuffer(); + BufferBuilder.DrawParameters parameters = built.getParameters(); + ByteBuffer vertexBuffer = built.getVertexBuffer(); + int neededSize = parameters.vertexCount() * BillboardParticleVertex.STRIDE; + + GlStateManager._glBindVertexArray(this.glVertexArray); + GlStateManager._glBindBuffer(GlConst.GL_ARRAY_BUFFER, this.glVertexBuffer); + if (neededSize > this.bufferSize) { + RenderSystem.glBufferData(GlConst.GL_ARRAY_BUFFER, vertexBuffer, GlConst.GL_DYNAMIC_DRAW); + this.bufferSize = neededSize; + } else { + GL20.glBufferSubData(GlConst.GL_ARRAY_BUFFER, 0, vertexBuffer); + } - buffer.bind(); - buffer.upload(built); BillboardParticleVertex.bindVertexFormat(); - - buffer.draw(); + uploadIndexBuffer(parameters); + int indexType = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS).getIndexType().glType; + RenderSystem.drawElements(VertexFormat.DrawMode.QUADS.glMode, parameters.indexCount(), indexType); + built.release(); } } @Unique - private ParticleRenderView renderView; - - @Inject(method = "", at = @At("RETURN")) - private void postInit(ClientWorld world, TextureManager textureManager, CallbackInfo ci) { - this.renderView = new ParticleRenderView(world); + private void uploadIndexBuffer(BufferBuilder.DrawParameters parameters) { + RenderSystem.ShapeIndexBuffer shapeIndexBuffer = RenderSystem.getSequentialBuffer(parameters.mode()); + if (shapeIndexBuffer != this.sharedSequentialIndexBuffer || !shapeIndexBuffer.isLargeEnough(parameters.indexCount())) { + shapeIndexBuffer.bindAndGrow(parameters.indexCount()); + this.sharedSequentialIndexBuffer = shapeIndexBuffer; + } } @Inject(method = "setWorld", at = @At("RETURN")) @@ -220,16 +288,6 @@ private void postSetWorld(ClientWorld world, CallbackInfo ci) { this.renderView = new ParticleRenderView(world); } - @Inject(method = "renderParticles", at = @At("HEAD")) - private void preRenderParticles(MatrixStack matrices, - VertexConsumerProvider.Immediate vertexConsumers, - LightmapTextureManager lightmapTextureManager, - Camera camera, - float tickDelta, - CallbackInfo ci) { - this.renderView.resetCache(); - } - @Inject(method = "createParticle", at = @At("RETURN")) private void postCreateParticle(T parameters, double x, From 5c6884f8a5cd5c03b0a4a9b68b09ff50bb5adad2 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Tue, 26 Sep 2023 00:25:54 -0500 Subject: [PATCH 08/30] Begin buffer texture vertex pulling impl --- .../mods/sodium/client/SodiumPreLaunch.java | 1 + .../client/gl/buffer/GlBufferTexture.java | 40 ++++++++++++ .../shader/ParticleShaderInterface.java | 15 +++++ .../sodium/shaders/particles/particle.vsh | 62 +++++++++++++++---- 4 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java diff --git a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java index 839105a391..bb725462aa 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java @@ -11,5 +11,6 @@ public void onPreLaunch() { GraphicsAdapterProbe.findAdapters(); PreLaunchChecks.checkDrivers(); Workarounds.init(); +// System.loadLibrary("renderdoc"); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java new file mode 100644 index 0000000000..a43e22992f --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java @@ -0,0 +1,40 @@ +package me.jellysquid.mods.sodium.client.gl.buffer; + +import com.mojang.blaze3d.platform.GlConst; +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL31; + +import java.nio.ByteBuffer; + +public class GlBufferTexture { + private final int glTexHandle; + + private final int glBufferHandle; + + private int bufferSize = 0; + + public GlBufferTexture() { + this.glTexHandle = GlStateManager._genTexture(); + this.glBufferHandle = GlStateManager._glGenBuffers(); + } + + public void uploadData(ByteBuffer data) { + GL31.glBindBuffer(GL31.GL_TEXTURE_BUFFER, this.glBufferHandle); + int neededSize = data.position(); + if (neededSize > this.bufferSize) { + RenderSystem.glBufferData(GL31.GL_TEXTURE_BUFFER, data, GlConst.GL_DYNAMIC_DRAW); + this.bufferSize = neededSize; + } else { + GL15.glBufferSubData(GL31.GL_TEXTURE_BUFFER, 0, data); + } + } + + public void bind() { + GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, this.glTexHandle); + GL31.glTexBuffer(GL31.GL_TEXTURE_BUFFER, GL31.GL_R32UI, this.glBufferHandle); + RenderSystem.setShaderTexture(3, this.glTexHandle); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java index f39d555edf..03ec923b6d 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java @@ -1,6 +1,7 @@ package me.jellysquid.mods.sodium.client.render.particle.shader; import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; import me.jellysquid.mods.sodium.client.gl.shader.uniform.GlUniformFloat4v; import me.jellysquid.mods.sodium.client.gl.shader.uniform.GlUniformInt; import me.jellysquid.mods.sodium.client.gl.shader.uniform.GlUniformMatrix4f; @@ -8,6 +9,9 @@ import me.jellysquid.mods.sodium.client.render.chunk.shader.ShaderBindingContext; import me.jellysquid.mods.sodium.client.util.TextureUtil; import org.joml.*; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL31; +import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32C; public class ParticleShaderInterface { @@ -15,12 +19,14 @@ public class ParticleShaderInterface { private final GlUniformInt uniformLightTexture; private final GlUniformMatrix4f uniformModelViewMatrix; private final GlUniformMatrix4f uniformProjectionMatrix; + private final GlUniformInt uniformBufferTexture; public ParticleShaderInterface(ShaderBindingContext context) { this.uniformParticleTexture = context.bindUniform("u_ParticleTex", GlUniformInt::new); this.uniformLightTexture = context.bindUniform("u_LightTex", GlUniformInt::new); this.uniformModelViewMatrix = context.bindUniform("u_ModelViewMatrix", GlUniformMatrix4f::new); this.uniformProjectionMatrix = context.bindUniform("u_ProjectionMatrix", GlUniformMatrix4f::new); + this.uniformBufferTexture = context.bindUniform("u_BufferTexture", GlUniformInt::new); } public void setProjectionMatrix(Matrix4fc matrix) { @@ -35,6 +41,7 @@ public void setupState() { // "BlockTexture" should represent the particle textures if bound correctly this.bindParticleTexture(ParticleShaderTextureSlot.TEXTURE, TextureUtil.getBlockTextureId()); this.bindLightTexture(ParticleShaderTextureSlot.LIGHT, TextureUtil.getLightTextureId()); + this.bindBufferTexture(ParticleShaderTextureSlot.STORAGE, RenderSystem.getShaderTexture(3)); } private void bindParticleTexture(ParticleShaderTextureSlot slot, int textureId) { @@ -51,8 +58,16 @@ private void bindLightTexture(ParticleShaderTextureSlot slot, int textureId) { uniformLightTexture.setInt(slot.ordinal()); } + private void bindBufferTexture(ParticleShaderTextureSlot slot, int textureId) { + GlStateManager._activeTexture(GL32C.GL_TEXTURE0 + slot.ordinal()); + GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, textureId); + + uniformBufferTexture.setInt(slot.ordinal()); + } + private enum ParticleShaderTextureSlot { TEXTURE, LIGHT, + STORAGE, } } diff --git a/src/main/resources/assets/sodium/shaders/particles/particle.vsh b/src/main/resources/assets/sodium/shaders/particles/particle.vsh index 091a5e06a3..152356f57d 100644 --- a/src/main/resources/assets/sodium/shaders/particles/particle.vsh +++ b/src/main/resources/assets/sodium/shaders/particles/particle.vsh @@ -1,5 +1,8 @@ #version 330 +#define COLOR_SCALE 1.0 / 255.0 +#define PARTICLE_STRIDE 7 + #import const vec2 OFFSETS[] = vec2[]( @@ -9,27 +12,62 @@ const vec2 OFFSETS[] = vec2[]( vec2(-1, 1) ); -in vec3 in_Position; -in float in_Size; in vec2 in_TexCoord; -in vec4 in_Color; -in ivec2 in_Light; -in float in_Angle; -uniform vec4 u_CameraRotation; uniform sampler2D u_LightTex; +uniform usamplerBuffer u_BufferTexture; // R_32UI out vec2 texCoord0; out vec4 vertexColor; -// The following code is an optimized variant of Minecraft's quaternion code by Zombye and Balint. +// 7 x 4 = 28 bytes stride +vec3 position; +float size; +vec4 color; +ivec2 light; +float angle; + +// Returns a collection of 4 bytes +// ptr is essentially multiplied by 4 since u_BufferTexture is R_32UI +uint readBuffer(uint ptr) { + return texelFetch(u_BufferTexture, ptr).x; +} + +float readBufferF(uint ptr) { + return uintBitsToFloat(readBuffer(ptr)); +} + +vec3 readBufferPos(uint ptr) { + return uintBitsToFloat(uvec3(readBuffer(ptr), readBuffer(ptr + 1), readBuffer(ptr + 2))); +} + +vec4 readBufferColor(uint ptr) { + return vec4((uvec4(readBuffer(ptr)) >> uvec4(0, 8, 16, 24)) & uvec4(0xFFu)) * COLOR_SCALE; +} +ivec2 readBufferLight(uint ptr) { + return ivec2((uvec2(readBuffer(ptr)) >> uvec2(0, 16)) & uvec2(0xFFFFu)); +} + +void init() { + uint base = PARTICLE_STRIDE * (gl_VertexID >> 2); + + position = readBufferPos(base); + size = readBufferF(base + 3); + color = readBufferColor(base + 4); + light = readBufferLight(base + 5); + angle = readBufferF(base + 6); +} + +// The following code is an optimized variant of Minecraft's quaternion code by Zombye and Balint. void main() { - gl_Position = u_ModelViewMatrix * vec4(in_Position, 1.0); - float s = sin(in_Angle); - float c = cos(in_Angle); - gl_Position.xy += mat2(c,-s, s, c) * OFFSETS[gl_VertexID & 3] * in_Size; + init(); + + gl_Position = u_ModelViewMatrix * vec4(position, 1.0); + float s = sin(angle); + float c = cos(angle); + gl_Position.xy += mat2(c,-s, s, c) * OFFSETS[gl_VertexID & 3] * size; gl_Position = u_ProjectionMatrix * gl_Position; texCoord0 = in_TexCoord; - vertexColor = in_Color * texelFetch(u_LightTex, in_Light / 16, 0); + vertexColor = color * texelFetch(u_LightTex, light / 16, 0); } \ No newline at end of file From a800faaf620582dd51ef439d054be987a9701f11 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Tue, 26 Sep 2023 11:45:34 -0500 Subject: [PATCH 09/30] Implement vertex pulling via a buffer texture --- .../api/buffer/UnmanagedBufferBuilder.java | 61 ++++++++++++++++++ .../mods/sodium/client/SodiumPreLaunch.java | 2 +- .../client/gl/buffer/GlBufferTexture.java | 7 +-- .../render/particle/BillboardExtended.java | 8 +++ .../render/particle/ExtendedParticle.java | 5 -- .../render/particle/ParticleExtended.java | 3 + .../ShaderBillboardParticleRenderer.java | 7 --- .../shader/BillboardParticleData.java | 24 +++++++ .../shader/BillboardParticleVertex.java | 49 ++------------- .../shader/ParticleMeshAttribute.java | 5 -- .../shader/ParticleShaderBindingPoints.java | 7 +-- .../particle/BillboardParticleMixin.java | 62 +++++++++---------- .../render/particle/ParticleManagerMixin.java | 27 +++++++- .../sodium/shaders/particles/particle.vsh | 12 ++-- 14 files changed, 166 insertions(+), 113 deletions(-) create mode 100644 src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java delete mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/ExtendedParticle.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java diff --git a/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java b/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java new file mode 100644 index 0000000000..793c445ec5 --- /dev/null +++ b/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java @@ -0,0 +1,61 @@ +package net.caffeinemc.mods.sodium.api.buffer; + +import net.caffeinemc.mods.sodium.api.memory.MemoryIntrinsics; +import net.minecraft.client.util.GlAllocationUtils; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; + +/** + * Provides a growing buffer with a push method for convenience. + * Otherwise, this class is very un-managed. + */ +public class UnmanagedBufferBuilder { + private ByteBuffer buffer; + private int byteOffset = 0; + + public UnmanagedBufferBuilder(int initialCapacity) { + this.buffer = GlAllocationUtils.allocateByteBuffer(initialCapacity); + } + + public void ensureCapacity(int capacity) { + if (capacity > this.buffer.capacity()) { + ByteBuffer byteBuffer = GlAllocationUtils.resizeByteBuffer(this.buffer, capacity); + byteBuffer.rewind(); + this.buffer = byteBuffer; + } + } + + /** + * Copies memory from the stack onto the end of this buffer builder. + */ + public void push(MemoryStack ignoredStack, long src, int size) { + ensureCapacity(byteOffset + size); + + long dst = MemoryUtil.memAddress(this.buffer, this.byteOffset); + MemoryIntrinsics.copyMemory(src, dst, size); + byteOffset += size; + } + + /** + * Resets this builder. + * Make sure to use/upload the return value before pushing more data. + * @return a ByteBuffer containing all the data pushed to this builder + */ + public Built end() { + int endOffset = this.byteOffset; + this.byteOffset = 0; + return new Built(endOffset, MemoryUtil.memSlice(this.buffer, 0, endOffset)); + } + + public static class Built { + public int size; + public ByteBuffer buffer; + + Built(int size, ByteBuffer buffer) { + this.size = size; + this.buffer = buffer; + } + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java index bb725462aa..014b905e8c 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java @@ -11,6 +11,6 @@ public void onPreLaunch() { GraphicsAdapterProbe.findAdapters(); PreLaunchChecks.checkDrivers(); Workarounds.init(); -// System.loadLibrary("renderdoc"); + System.loadLibrary("renderdoc"); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java index a43e22992f..37253eea80 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java @@ -21,12 +21,11 @@ public GlBufferTexture() { this.glBufferHandle = GlStateManager._glGenBuffers(); } - public void uploadData(ByteBuffer data) { + public void uploadData(ByteBuffer data, int size) { GL31.glBindBuffer(GL31.GL_TEXTURE_BUFFER, this.glBufferHandle); - int neededSize = data.position(); - if (neededSize > this.bufferSize) { + if (size > this.bufferSize) { RenderSystem.glBufferData(GL31.GL_TEXTURE_BUFFER, data, GlConst.GL_DYNAMIC_DRAW); - this.bufferSize = neededSize; + this.bufferSize = size; } else { GL15.glBufferSubData(GL31.GL_TEXTURE_BUFFER, 0, data); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java new file mode 100644 index 0000000000..f863721bf0 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java @@ -0,0 +1,8 @@ +package me.jellysquid.mods.sodium.client.render.particle; + +import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; +import net.minecraft.client.render.Camera; + +public interface BillboardExtended { + void sodium$buildParticleData(UnmanagedBufferBuilder builder, Camera camera, float tickDelta); +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ExtendedParticle.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ExtendedParticle.java deleted file mode 100644 index 6c27ceca80..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ExtendedParticle.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.jellysquid.mods.sodium.client.render.particle; - -public interface ExtendedParticle { - boolean sodium$reachedBillboardDraw(); -} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java index 731e6b439a..a5609aeaca 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java @@ -1,5 +1,8 @@ package me.jellysquid.mods.sodium.client.render.particle; +import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; +import net.minecraft.client.render.Camera; + public interface ParticleExtended { void sodium$configure(ParticleRenderView renderView); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java index aba3023ff6..d8724a8d83 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java @@ -29,12 +29,7 @@ private GlProgram createShader(String path) { return GlProgram.builder(new Identifier("sodium", "billboard_particle_shader")) .attachShader(vertShader) .attachShader(fragShader) - .bindAttribute("in_Position", ParticleShaderBindingPoints.ATTRIBUTE_POSITION) - .bindAttribute("in_Size", ParticleShaderBindingPoints.ATTRIBUTE_SIZE) .bindAttribute("in_TexCoord", ParticleShaderBindingPoints.ATTRIBUTE_TEXTURE) - .bindAttribute("in_Color", ParticleShaderBindingPoints.ATTRIBUTE_COLOR) - .bindAttribute("in_Light", ParticleShaderBindingPoints.ATTRIBUTE_LIGHT_TEXTURE) - .bindAttribute("in_Angle", ParticleShaderBindingPoints.ATTRIBUTE_ANGLE) .bindFragmentData("out_FragColor", ParticleShaderBindingPoints.FRAG_COLOR) .link(ParticleShaderInterface::new); } finally { @@ -44,8 +39,6 @@ private GlProgram createShader(String path) { } public void begin() { - // pass.startDrawing(); .. Do I need a pass? - this.activeProgram.bind(); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java new file mode 100644 index 0000000000..f9f54094b8 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java @@ -0,0 +1,24 @@ +package me.jellysquid.mods.sodium.client.render.particle.shader; + +import net.caffeinemc.mods.sodium.api.vertex.attributes.common.ColorAttribute; +import net.caffeinemc.mods.sodium.api.vertex.attributes.common.LightAttribute; +import net.caffeinemc.mods.sodium.api.vertex.attributes.common.PositionAttribute; +import org.lwjgl.system.MemoryUtil; + +public class BillboardParticleData { + public static final int POSITION_OFFSET = 0; + public static final int SIZE_OFFSET = 12; + public static final int COLOR_OFFSET = 16; + public static final int LIGHT_UV_OFFSET = 20; + public static final int ANGLE_OFFSET = 24; + public static final int STRIDE = 28; + + public static void put(long ptr, float x, float y, float z, + int color, int light, float size, float angle) { + PositionAttribute.put(ptr + POSITION_OFFSET, x, y, z); + MemoryUtil.memPutFloat(ptr + SIZE_OFFSET, size); + ColorAttribute.set(ptr + COLOR_OFFSET, color); + LightAttribute.set(ptr + LIGHT_UV_OFFSET, light); + MemoryUtil.memPutFloat(ptr + ANGLE_OFFSET, angle); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java index eea729f02e..ffe10f6361 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java @@ -24,68 +24,27 @@ import static net.minecraft.client.render.VertexFormats.*; public class BillboardParticleVertex { - public static final int POSITION_OFFSET = 0; - public static final int SIZE_OFFSET = 12; - public static final int TEX_UV_OFFSET = 16; - public static final int COLOR_OFFSET = 24; - public static final int LIGHT_UV_OFFSET = 28; - public static final int ANGLE_OFFSET = 32; - public static final int STRIDE = 36; + public static final int TEX_UV_OFFSET = 0; + public static final int STRIDE = 8; public static final GlVertexFormat VERTEX_FORMAT = GlVertexFormat.builder(ParticleMeshAttribute.class, STRIDE) - .addElement(ParticleMeshAttribute.POSITION, POSITION_OFFSET, GlVertexAttributeFormat.FLOAT, 3, false, false) - .addElement(ParticleMeshAttribute.SIZE, SIZE_OFFSET, GlVertexAttributeFormat.FLOAT, 1, false, false) .addElement(ParticleMeshAttribute.TEX_COORD, TEX_UV_OFFSET, GlVertexAttributeFormat.FLOAT, 2, false, false) - .addElement(ParticleMeshAttribute.COLOR, COLOR_OFFSET, GlVertexAttributeFormat.UNSIGNED_BYTE, 4, true, false) - .addElement(ParticleMeshAttribute.LIGHT_UV, LIGHT_UV_OFFSET, GlVertexAttributeFormat.UNSIGNED_SHORT, 2, false, true) - .addElement(ParticleMeshAttribute.ANGLE, ANGLE_OFFSET, GlVertexAttributeFormat.FLOAT, 1, false, false) .build(); public static final VertexFormat MC_VERTEX_FORMAT = new VertexFormat(ImmutableMap.ofEntries( - Map.entry("in_Position", POSITION_ELEMENT), - Map.entry("in_Size", new VertexFormatElement( - 0, - VertexFormatElement.ComponentType.FLOAT, - VertexFormatElement.Type.GENERIC, - 1 - )), - Map.entry("in_TexCoord", TEXTURE_ELEMENT), - Map.entry("in_Color", COLOR_ELEMENT), - Map.entry("in_Light", LIGHT_ELEMENT), - Map.entry("in_Angle", new VertexFormatElement( - 0, - VertexFormatElement.ComponentType.FLOAT, - VertexFormatElement.Type.GENERIC, - 1 - )) + Map.entry("in_TexCoord", TEXTURE_ELEMENT) )); public static final GlVertexAttributeBinding[] ATTRIBUTE_BINDINGS = new GlVertexAttributeBinding[] { - new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_POSITION, - VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.POSITION)), - new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_SIZE, - VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.SIZE)), new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_TEXTURE, VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.TEX_COORD)), - new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_COLOR, - VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.COLOR)), - new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_LIGHT_TEXTURE, - VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.LIGHT_UV)), - new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_ANGLE, - VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.ANGLE)), }; public static final VertexFormatDescription VERTEX_FORMAT_DESCRIPTION = VertexFormatRegistry.instance() .get(MC_VERTEX_FORMAT); - public static void put(long ptr, float x, float y, float z, - float u, float v, int color, int light, float size, float angle) { - PositionAttribute.put(ptr + POSITION_OFFSET, x, y, z); - MemoryUtil.memPutFloat(ptr + SIZE_OFFSET, size); + public static void put(long ptr, float u, float v) { TextureAttribute.put(ptr + TEX_UV_OFFSET, u, v); - ColorAttribute.set(ptr + COLOR_OFFSET, color); - LightAttribute.set(ptr + LIGHT_UV_OFFSET, light); - MemoryUtil.memPutFloat(ptr + ANGLE_OFFSET, angle); } public static void bindVertexFormat() { diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java index b7fa85a527..c151822bf1 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java @@ -1,10 +1,5 @@ package me.jellysquid.mods.sodium.client.render.particle.shader; public enum ParticleMeshAttribute { - POSITION, - SIZE, TEX_COORD, - COLOR, - LIGHT_UV, - ANGLE, } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java index 99ae542d91..bff2fc3742 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java @@ -1,12 +1,7 @@ package me.jellysquid.mods.sodium.client.render.particle.shader; public class ParticleShaderBindingPoints { - public static final int ATTRIBUTE_POSITION = 1; - public static final int ATTRIBUTE_SIZE = 2; - public static final int ATTRIBUTE_TEXTURE = 3; - public static final int ATTRIBUTE_COLOR = 4; - public static final int ATTRIBUTE_LIGHT_TEXTURE = 5; - public static final int ATTRIBUTE_ANGLE = 6; + public static final int ATTRIBUTE_TEXTURE = 1; public static final int FRAG_COLOR = 0; } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java index ce909925e6..788cd8ccf9 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java @@ -1,7 +1,9 @@ package me.jellysquid.mods.sodium.mixin.features.render.particle; -import me.jellysquid.mods.sodium.client.render.particle.ExtendedParticle; +import me.jellysquid.mods.sodium.client.render.particle.BillboardExtended; +import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleData; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleVertex; +import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; import net.caffeinemc.mods.sodium.api.util.ColorABGR; import net.minecraft.client.particle.BillboardParticle; @@ -18,7 +20,7 @@ import org.spongepowered.asm.mixin.Unique; @Mixin(BillboardParticle.class) -public abstract class BillboardParticleMixin extends Particle implements ExtendedParticle { +public abstract class BillboardParticleMixin extends Particle implements BillboardExtended { @Shadow public abstract float getSize(float tickDelta); @@ -34,29 +36,12 @@ public abstract class BillboardParticleMixin extends Particle implements Extende @Shadow protected abstract float getMaxV(); - @Unique - private boolean reachedBillboardDraw; - - @Override - public boolean sodium$reachedBillboardDraw() { - return reachedBillboardDraw; - } - protected BillboardParticleMixin(ClientWorld world, double x, double y, double z) { super(world, x, y, z); } - /** - * @reason Optimize function - * @author JellySquid - */ - @Overwrite - public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float tickDelta) { - if (!reachedBillboardDraw) { - reachedBillboardDraw = true; - return; - } - + @Override + public void sodium$buildParticleData(UnmanagedBufferBuilder builder, Camera camera, float tickDelta) { Vec3d vec3d = camera.getPos(); float x = (float) (MathHelper.lerp(tickDelta, this.prevPosX, this.x) - vec3d.getX()); @@ -66,31 +51,44 @@ public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float ti float size = this.getSize(tickDelta); int light = this.getBrightness(tickDelta); + int color = ColorABGR.pack(this.red , this.green, this.blue, this.alpha); + + float angle = MathHelper.lerp(tickDelta, this.prevAngle, this.angle); + + try (MemoryStack stack = MemoryStack.stackPush()) { + long ptr = stack.nmalloc(BillboardParticleData.STRIDE); + BillboardParticleData.put(ptr, x, y, z, color, light, size, angle); + builder.push(stack, ptr, BillboardParticleData.STRIDE); + } + } + + /** + * @reason Optimize function + * @author JellySquid + */ + @Overwrite + public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float tickDelta) { float minU = this.getMinU(); float maxU = this.getMaxU(); float minV = this.getMinV(); float maxV = this.getMaxV(); - int color = ColorABGR.pack(this.red , this.green, this.blue, this.alpha); - - float angle = MathHelper.lerp(tickDelta, this.prevAngle, this.angle); - var writer = VertexBufferWriter.of(vertexConsumer); try (MemoryStack stack = MemoryStack.stackPush()) { long buffer = stack.nmalloc(4 * BillboardParticleVertex.STRIDE); long ptr = buffer; - writeVertex(ptr, x, y, z, maxU, maxV, color, light, size, angle); + writeVertex(ptr, maxU, maxV); ptr += BillboardParticleVertex.STRIDE; - writeVertex(ptr, x, y, z, maxU, minV, color, light, size, angle); + writeVertex(ptr, maxU, minV); ptr += BillboardParticleVertex.STRIDE; - writeVertex(ptr, x, y, z, minU, minV, color, light, size, angle); + writeVertex(ptr, minU, minV); ptr += BillboardParticleVertex.STRIDE; - writeVertex(ptr, x, y, z, minU, maxV, color, light, size, angle); + writeVertex(ptr, minU, maxV); ptr += BillboardParticleVertex.STRIDE; writer.push(stack, buffer, 4, BillboardParticleVertex.VERTEX_FORMAT_DESCRIPTION); @@ -98,9 +96,7 @@ public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float ti } @Unique - private static void writeVertex(long buffer, float originX, float originY, float originZ, - float u, float v, int color, int light, float size, float angle) { - - BillboardParticleVertex.put(buffer, originX, originY, originZ, u, v, color, light, size, angle); + private static void writeVertex(long buffer, float u, float v) { + BillboardParticleVertex.put(buffer, u, v); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index 5adba33a77..1f74bcad75 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -8,11 +8,14 @@ import com.mojang.blaze3d.systems.RenderSystem; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; +import me.jellysquid.mods.sodium.client.gl.buffer.GlBufferTexture; +import me.jellysquid.mods.sodium.client.render.particle.BillboardExtended; import me.jellysquid.mods.sodium.client.render.particle.ParticleExtended; import me.jellysquid.mods.sodium.client.render.particle.ParticleRenderView; import me.jellysquid.mods.sodium.client.render.particle.ShaderBillboardParticleRenderer; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleVertex; import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; +import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.particle.*; import net.minecraft.client.render.*; @@ -93,6 +96,12 @@ public abstract class ParticleManagerMixin { @Unique private int glVertexArray; + @Unique + private UnmanagedBufferBuilder particleBuffer; + + @Unique + private GlBufferTexture bufferTexture; + @Unique private RenderSystem.ShapeIndexBuffer sharedSequentialIndexBuffer; @@ -103,6 +112,8 @@ public abstract class ParticleManagerMixin { private void postInit(ClientWorld world, TextureManager textureManager, CallbackInfo ci) { this.glVertexBuffer = GlStateManager._glGenBuffers(); this.glVertexArray = GlStateManager._glGenVertexArrays(); + this.particleBuffer = new UnmanagedBufferBuilder(1); + this.bufferTexture = new GlBufferTexture(); this.renderView = new ParticleRenderView(world); } @@ -192,7 +203,11 @@ private void preRenderParticles(MatrixStack matrices, this.renderView.resetCache(); } - @Inject(method = "renderParticles", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;applyModelViewMatrix()V", ordinal = 0, shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD) + @Inject( + method = "renderParticles", + at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;applyModelViewMatrix()V", ordinal = 0, shift = At.Shift.AFTER), + locals = LocalCapture.CAPTURE_FAILHARD + ) public void renderParticles( MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers, LightmapTextureManager lightmapTextureManager, Camera camera, float tickDelta, @@ -208,11 +223,13 @@ public void renderParticles( if (iterable != null && !iterable.isEmpty()) { int numParticles = iterable.size(); bindParticleTextureSheet(particleTextureSheet); + this.bufferTexture.bind(); particleRenderer.setupState(); bufferBuilder.begin(VertexFormat.DrawMode.QUADS, BillboardParticleVertex.MC_VERTEX_FORMAT); for (BillboardParticle particle : iterable) { particle.buildGeometry(bufferBuilder, camera, tickDelta); + ((BillboardExtended) particle).sodium$buildParticleData(particleBuffer, camera, tickDelta); } drawParticleTextureSheet(particleTextureSheet, bufferBuilder, numParticles); @@ -225,6 +242,7 @@ public void renderParticles( } @Unique + @SuppressWarnings("deprecation") private void bindParticleTextureSheet(ParticleTextureSheet sheet) { RenderSystem.depthMask(true); Identifier texture = null; @@ -268,6 +286,7 @@ private void drawParticleTextureSheet(ParticleTextureSheet sheet, BufferBuilder BillboardParticleVertex.bindVertexFormat(); uploadIndexBuffer(parameters); + uploadParticleBuffer(); int indexType = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS).getIndexType().glType; RenderSystem.drawElements(VertexFormat.DrawMode.QUADS.glMode, parameters.indexCount(), indexType); built.release(); @@ -283,6 +302,12 @@ private void uploadIndexBuffer(BufferBuilder.DrawParameters parameters) { } } + @Unique + private void uploadParticleBuffer() { + UnmanagedBufferBuilder.Built particleData = this.particleBuffer.end(); + this.bufferTexture.uploadData(particleData.buffer, particleData.size); + } + @Inject(method = "setWorld", at = @At("RETURN")) private void postSetWorld(ClientWorld world, CallbackInfo ci) { this.renderView = new ParticleRenderView(world); diff --git a/src/main/resources/assets/sodium/shaders/particles/particle.vsh b/src/main/resources/assets/sodium/shaders/particles/particle.vsh index 152356f57d..1cb8044cff 100644 --- a/src/main/resources/assets/sodium/shaders/particles/particle.vsh +++ b/src/main/resources/assets/sodium/shaders/particles/particle.vsh @@ -29,28 +29,28 @@ float angle; // Returns a collection of 4 bytes // ptr is essentially multiplied by 4 since u_BufferTexture is R_32UI -uint readBuffer(uint ptr) { +uint readBuffer(int ptr) { return texelFetch(u_BufferTexture, ptr).x; } -float readBufferF(uint ptr) { +float readBufferF(int ptr) { return uintBitsToFloat(readBuffer(ptr)); } -vec3 readBufferPos(uint ptr) { +vec3 readBufferPos(int ptr) { return uintBitsToFloat(uvec3(readBuffer(ptr), readBuffer(ptr + 1), readBuffer(ptr + 2))); } -vec4 readBufferColor(uint ptr) { +vec4 readBufferColor(int ptr) { return vec4((uvec4(readBuffer(ptr)) >> uvec4(0, 8, 16, 24)) & uvec4(0xFFu)) * COLOR_SCALE; } -ivec2 readBufferLight(uint ptr) { +ivec2 readBufferLight(int ptr) { return ivec2((uvec2(readBuffer(ptr)) >> uvec2(0, 16)) & uvec2(0xFFFFu)); } void init() { - uint base = PARTICLE_STRIDE * (gl_VertexID >> 2); + int base = PARTICLE_STRIDE * (gl_VertexID >> 2); position = readBufferPos(base); size = readBufferF(base + 3); From 06cbfd8ad86ecfb988760051c5003a9f80a960e6 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Tue, 26 Sep 2023 18:12:33 -0500 Subject: [PATCH 10/30] Transfer to full vertex pulling --- .../mods/sodium/client/SodiumPreLaunch.java | 2 +- .../ShaderBillboardParticleRenderer.java | 1 - .../shader/BillboardParticleData.java | 11 +++- .../shader/BillboardParticleVertex.java | 62 ------------------- .../shader/ParticleMeshAttribute.java | 5 -- .../shader/ParticleShaderBindingPoints.java | 2 - .../particle/BillboardParticleMixin.java | 50 ++++----------- .../render/particle/ParticleManagerMixin.java | 60 ++++-------------- .../sodium/shaders/particles/particle.vsh | 34 +++++++--- 9 files changed, 61 insertions(+), 166 deletions(-) delete mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java delete mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java diff --git a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java index 014b905e8c..bb725462aa 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java @@ -11,6 +11,6 @@ public void onPreLaunch() { GraphicsAdapterProbe.findAdapters(); PreLaunchChecks.checkDrivers(); Workarounds.init(); - System.loadLibrary("renderdoc"); +// System.loadLibrary("renderdoc"); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java index d8724a8d83..95a3c2b943 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ShaderBillboardParticleRenderer.java @@ -29,7 +29,6 @@ private GlProgram createShader(String path) { return GlProgram.builder(new Identifier("sodium", "billboard_particle_shader")) .attachShader(vertShader) .attachShader(fragShader) - .bindAttribute("in_TexCoord", ParticleShaderBindingPoints.ATTRIBUTE_TEXTURE) .bindFragmentData("out_FragColor", ParticleShaderBindingPoints.FRAG_COLOR) .link(ParticleShaderInterface::new); } finally { diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java index f9f54094b8..a29e062b2c 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java @@ -3,6 +3,7 @@ import net.caffeinemc.mods.sodium.api.vertex.attributes.common.ColorAttribute; import net.caffeinemc.mods.sodium.api.vertex.attributes.common.LightAttribute; import net.caffeinemc.mods.sodium.api.vertex.attributes.common.PositionAttribute; +import net.caffeinemc.mods.sodium.api.vertex.attributes.common.TextureAttribute; import org.lwjgl.system.MemoryUtil; public class BillboardParticleData { @@ -11,14 +12,20 @@ public class BillboardParticleData { public static final int COLOR_OFFSET = 16; public static final int LIGHT_UV_OFFSET = 20; public static final int ANGLE_OFFSET = 24; - public static final int STRIDE = 28; + public static final int MIN_UV_OFFSET = 28; + public static final int MAX_UV_OFFSET = 36; + public static final int STRIDE = 44; public static void put(long ptr, float x, float y, float z, - int color, int light, float size, float angle) { + int color, int light, float size, float angle, + float minU, float minV, float maxU, float maxV + ) { PositionAttribute.put(ptr + POSITION_OFFSET, x, y, z); MemoryUtil.memPutFloat(ptr + SIZE_OFFSET, size); ColorAttribute.set(ptr + COLOR_OFFSET, color); LightAttribute.set(ptr + LIGHT_UV_OFFSET, light); MemoryUtil.memPutFloat(ptr + ANGLE_OFFSET, angle); + TextureAttribute.put(ptr + MIN_UV_OFFSET, minU, minV); + TextureAttribute.put(ptr + MAX_UV_OFFSET, maxU, maxV); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java deleted file mode 100644 index ffe10f6361..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleVertex.java +++ /dev/null @@ -1,62 +0,0 @@ -package me.jellysquid.mods.sodium.client.render.particle.shader; - -import com.google.common.collect.ImmutableMap; -import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeBinding; -import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeFormat; -import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexFormat; -import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderBindingPoints; -import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkMeshAttribute; -import net.caffeinemc.mods.sodium.api.vertex.attributes.CommonVertexAttribute; -import net.caffeinemc.mods.sodium.api.vertex.attributes.common.ColorAttribute; -import net.caffeinemc.mods.sodium.api.vertex.attributes.common.LightAttribute; -import net.caffeinemc.mods.sodium.api.vertex.attributes.common.PositionAttribute; -import net.caffeinemc.mods.sodium.api.vertex.attributes.common.TextureAttribute; -import net.caffeinemc.mods.sodium.api.vertex.format.VertexFormatDescription; -import net.caffeinemc.mods.sodium.api.vertex.format.VertexFormatRegistry; -import net.minecraft.client.render.VertexFormat; -import net.minecraft.client.render.VertexFormatElement; -import org.lwjgl.opengl.GL20C; -import org.lwjgl.opengl.GL30C; -import org.lwjgl.system.MemoryUtil; - -import java.util.Map; - -import static net.minecraft.client.render.VertexFormats.*; - -public class BillboardParticleVertex { - public static final int TEX_UV_OFFSET = 0; - public static final int STRIDE = 8; - - public static final GlVertexFormat VERTEX_FORMAT = GlVertexFormat.builder(ParticleMeshAttribute.class, STRIDE) - .addElement(ParticleMeshAttribute.TEX_COORD, TEX_UV_OFFSET, GlVertexAttributeFormat.FLOAT, 2, false, false) - .build(); - - public static final VertexFormat MC_VERTEX_FORMAT = new VertexFormat(ImmutableMap.ofEntries( - Map.entry("in_TexCoord", TEXTURE_ELEMENT) - )); - - public static final GlVertexAttributeBinding[] ATTRIBUTE_BINDINGS = new GlVertexAttributeBinding[] { - new GlVertexAttributeBinding(ParticleShaderBindingPoints.ATTRIBUTE_TEXTURE, - VERTEX_FORMAT.getAttribute(ParticleMeshAttribute.TEX_COORD)), - }; - - public static final VertexFormatDescription VERTEX_FORMAT_DESCRIPTION = VertexFormatRegistry.instance() - .get(MC_VERTEX_FORMAT); - - public static void put(long ptr, float u, float v) { - TextureAttribute.put(ptr + TEX_UV_OFFSET, u, v); - } - - public static void bindVertexFormat() { - for (GlVertexAttributeBinding attrib : ATTRIBUTE_BINDINGS) { - if (attrib.isIntType()) { - GL30C.glVertexAttribIPointer(attrib.getIndex(), attrib.getCount(), attrib.getFormat(), - attrib.getStride(), attrib.getPointer()); - } else { - GL20C.glVertexAttribPointer(attrib.getIndex(), attrib.getCount(), attrib.getFormat(), attrib.isNormalized(), - attrib.getStride(), attrib.getPointer()); - } - GL20C.glEnableVertexAttribArray(attrib.getIndex()); - } - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java deleted file mode 100644 index c151822bf1..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleMeshAttribute.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.jellysquid.mods.sodium.client.render.particle.shader; - -public enum ParticleMeshAttribute { - TEX_COORD, -} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java index bff2fc3742..1d21f3beb3 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderBindingPoints.java @@ -1,7 +1,5 @@ package me.jellysquid.mods.sodium.client.render.particle.shader; public class ParticleShaderBindingPoints { - public static final int ATTRIBUTE_TEXTURE = 1; - public static final int FRAG_COLOR = 0; } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java index 788cd8ccf9..0e6e99f782 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java @@ -2,9 +2,7 @@ import me.jellysquid.mods.sodium.client.render.particle.BillboardExtended; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleData; -import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleVertex; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; -import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; import net.caffeinemc.mods.sodium.api.util.ColorABGR; import net.minecraft.client.particle.BillboardParticle; import net.minecraft.client.particle.Particle; @@ -17,7 +15,6 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; @Mixin(BillboardParticle.class) public abstract class BillboardParticleMixin extends Particle implements BillboardExtended { @@ -44,6 +41,11 @@ protected BillboardParticleMixin(ClientWorld world, double x, double y, double z public void sodium$buildParticleData(UnmanagedBufferBuilder builder, Camera camera, float tickDelta) { Vec3d vec3d = camera.getPos(); + float minU = this.getMinU(); + float maxU = this.getMaxU(); + float minV = this.getMinV(); + float maxV = this.getMaxV(); + float x = (float) (MathHelper.lerp(tickDelta, this.prevPosX, this.x) - vec3d.getX()); float y = (float) (MathHelper.lerp(tickDelta, this.prevPosY, this.y) - vec3d.getY()); float z = (float) (MathHelper.lerp(tickDelta, this.prevPosZ, this.z) - vec3d.getZ()); @@ -57,46 +59,18 @@ protected BillboardParticleMixin(ClientWorld world, double x, double y, double z try (MemoryStack stack = MemoryStack.stackPush()) { long ptr = stack.nmalloc(BillboardParticleData.STRIDE); - BillboardParticleData.put(ptr, x, y, z, color, light, size, angle); + BillboardParticleData.put( + ptr, x, y, z, color, light, size, angle, + minU, minV, maxU, maxV + ); builder.push(stack, ptr, BillboardParticleData.STRIDE); } } /** - * @reason Optimize function - * @author JellySquid + * @reason Remove function + * @author BeljihnWahfl */ @Overwrite - public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float tickDelta) { - float minU = this.getMinU(); - float maxU = this.getMaxU(); - float minV = this.getMinV(); - float maxV = this.getMaxV(); - - var writer = VertexBufferWriter.of(vertexConsumer); - - try (MemoryStack stack = MemoryStack.stackPush()) { - long buffer = stack.nmalloc(4 * BillboardParticleVertex.STRIDE); - long ptr = buffer; - - writeVertex(ptr, maxU, maxV); - ptr += BillboardParticleVertex.STRIDE; - - writeVertex(ptr, maxU, minV); - ptr += BillboardParticleVertex.STRIDE; - - writeVertex(ptr, minU, minV); - ptr += BillboardParticleVertex.STRIDE; - - writeVertex(ptr, minU, maxV); - ptr += BillboardParticleVertex.STRIDE; - - writer.push(stack, buffer, 4, BillboardParticleVertex.VERTEX_FORMAT_DESCRIPTION); - } - } - - @Unique - private static void writeVertex(long buffer, float u, float v) { - BillboardParticleVertex.put(buffer, u, v); - } + public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float tickDelta) {} } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index 1f74bcad75..21707a861c 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -8,12 +8,13 @@ import com.mojang.blaze3d.systems.RenderSystem; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; +import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeBinding; +import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeFormat; import me.jellysquid.mods.sodium.client.gl.buffer.GlBufferTexture; import me.jellysquid.mods.sodium.client.render.particle.BillboardExtended; import me.jellysquid.mods.sodium.client.render.particle.ParticleExtended; import me.jellysquid.mods.sodium.client.render.particle.ParticleRenderView; import me.jellysquid.mods.sodium.client.render.particle.ShaderBillboardParticleRenderer; -import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleVertex; import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.fabricmc.loader.api.FabricLoader; @@ -26,7 +27,8 @@ import net.minecraft.particle.ParticleEffect; import net.minecraft.util.Identifier; import org.jetbrains.annotations.Nullable; -import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL20C; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -34,7 +36,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import java.nio.ByteBuffer; import java.util.Collection; import java.util.List; import java.util.Map; @@ -42,12 +43,6 @@ @Mixin(ParticleManager.class) public abstract class ParticleManagerMixin { - @Unique - private final BufferBuilder bufferBuilder = new BufferBuilder(1); - - @Unique - private final BufferBuilder testBuffer = new BufferBuilder(1); - @Shadow protected ClientWorld world; @@ -87,9 +82,6 @@ public abstract class ParticleManagerMixin { "(Lnet/minecraft/class_4588;Lnet/minecraft/class_4184;F)V" ); - @Unique - private int bufferSize = 0; - @Unique private int glVertexBuffer; @@ -102,9 +94,6 @@ public abstract class ParticleManagerMixin { @Unique private GlBufferTexture bufferTexture; - @Unique - private RenderSystem.ShapeIndexBuffer sharedSequentialIndexBuffer; - @Unique private Identifier prevTexture = null; @@ -126,7 +115,6 @@ private void postInit(ClientWorld world, TextureManager textureManager, Callback */ @Overwrite public void tick() { - testBuffer.begin(VertexFormat.DrawMode.QUADS, BillboardParticleVertex.MC_VERTEX_FORMAT); this.particles.forEach((sheet, queue) -> { this.world.getProfiler().push(sheet.toString()); this.tickParticles(queue); @@ -170,8 +158,6 @@ public void tick() { } } } - - testBuffer.end().release(); } @Unique @@ -225,15 +211,12 @@ public void renderParticles( bindParticleTextureSheet(particleTextureSheet); this.bufferTexture.bind(); particleRenderer.setupState(); - bufferBuilder.begin(VertexFormat.DrawMode.QUADS, BillboardParticleVertex.MC_VERTEX_FORMAT); for (BillboardParticle particle : iterable) { - particle.buildGeometry(bufferBuilder, camera, tickDelta); ((BillboardExtended) particle).sodium$buildParticleData(particleBuffer, camera, tickDelta); } - drawParticleTextureSheet(particleTextureSheet, bufferBuilder, numParticles); - bufferBuilder.clear(); + drawParticleTextureSheet(particleTextureSheet, numParticles); } } @@ -268,38 +251,19 @@ private void bindParticleTextureSheet(ParticleTextureSheet sheet) { } @Unique - private void drawParticleTextureSheet(ParticleTextureSheet sheet, BufferBuilder builder, int numParticles) { + private void drawParticleTextureSheet(ParticleTextureSheet sheet, int numParticles) { if (sheet == ParticleTextureSheet.TERRAIN_SHEET || sheet == ParticleTextureSheet.PARTICLE_SHEET_LIT || sheet == ParticleTextureSheet.PARTICLE_SHEET_OPAQUE || sheet == ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT) { - BufferBuilder.BuiltBuffer built = builder.end(); - BufferBuilder.DrawParameters parameters = built.getParameters(); - ByteBuffer vertexBuffer = built.getVertexBuffer(); - int neededSize = parameters.vertexCount() * BillboardParticleVertex.STRIDE; - - GlStateManager._glBindVertexArray(this.glVertexArray); - GlStateManager._glBindBuffer(GlConst.GL_ARRAY_BUFFER, this.glVertexBuffer); - if (neededSize > this.bufferSize) { - RenderSystem.glBufferData(GlConst.GL_ARRAY_BUFFER, vertexBuffer, GlConst.GL_DYNAMIC_DRAW); - this.bufferSize = neededSize; - } else { - GL20.glBufferSubData(GlConst.GL_ARRAY_BUFFER, 0, vertexBuffer); - } - - BillboardParticleVertex.bindVertexFormat(); - uploadIndexBuffer(parameters); uploadParticleBuffer(); - int indexType = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS).getIndexType().glType; - RenderSystem.drawElements(VertexFormat.DrawMode.QUADS.glMode, parameters.indexCount(), indexType); - built.release(); + bindDummyVao(); + GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, numParticles * 6); } } @Unique - private void uploadIndexBuffer(BufferBuilder.DrawParameters parameters) { - RenderSystem.ShapeIndexBuffer shapeIndexBuffer = RenderSystem.getSequentialBuffer(parameters.mode()); - if (shapeIndexBuffer != this.sharedSequentialIndexBuffer || !shapeIndexBuffer.isLargeEnough(parameters.indexCount())) { - shapeIndexBuffer.bindAndGrow(parameters.indexCount()); - this.sharedSequentialIndexBuffer = shapeIndexBuffer; - } + private void bindDummyVao() { + GlStateManager._glBindVertexArray(this.glVertexArray); + GL20C.glVertexAttribPointer(0, 1, GlVertexAttributeFormat.UNSIGNED_BYTE.typeId(), false, 1, 0); + GL20C.glEnableVertexAttribArray(0); } @Unique diff --git a/src/main/resources/assets/sodium/shaders/particles/particle.vsh b/src/main/resources/assets/sodium/shaders/particles/particle.vsh index 1cb8044cff..9d5a380403 100644 --- a/src/main/resources/assets/sodium/shaders/particles/particle.vsh +++ b/src/main/resources/assets/sodium/shaders/particles/particle.vsh @@ -1,7 +1,7 @@ #version 330 #define COLOR_SCALE 1.0 / 255.0 -#define PARTICLE_STRIDE 7 +#define PARTICLE_STRIDE 11 #import @@ -12,7 +12,10 @@ const vec2 OFFSETS[] = vec2[]( vec2(-1, 1) ); -in vec2 in_TexCoord; +const int INDICES[] = int[]( + 0, 1, 2, + 0, 2, 3 +); uniform sampler2D u_LightTex; uniform usamplerBuffer u_BufferTexture; // R_32UI @@ -26,6 +29,8 @@ float size; vec4 color; ivec2 light; float angle; +vec2 minTexUV; +vec2 maxTexUV; // Returns a collection of 4 bytes // ptr is essentially multiplied by 4 since u_BufferTexture is R_32UI @@ -49,25 +54,40 @@ ivec2 readBufferLight(int ptr) { return ivec2((uvec2(readBuffer(ptr)) >> uvec2(0, 16)) & uvec2(0xFFFFu)); } +vec2 readBufferTex(int ptr) { + return vec2(readBufferF(ptr), readBufferF(ptr + 1)); +} + void init() { - int base = PARTICLE_STRIDE * (gl_VertexID >> 2); + int base = PARTICLE_STRIDE * (gl_VertexID / 6); position = readBufferPos(base); size = readBufferF(base + 3); color = readBufferColor(base + 4); light = readBufferLight(base + 5); angle = readBufferF(base + 6); + minTexUV = readBufferTex(base + 7); + maxTexUV = readBufferTex(base + 9); } -// The following code is an optimized variant of Minecraft's quaternion code by Zombye and Balint. void main() { init(); - + int vertexIndex = INDICES[gl_VertexID % 6]; + vec2 texUVs[] = vec2[]( + maxTexUV, + vec2(maxTexUV.x, minTexUV.y), + minTexUV, + vec2(minTexUV.x, maxTexUV.y) + ); + + // The following code is an optimized variant of Minecraft's quaternion code by Zombye and Balint. gl_Position = u_ModelViewMatrix * vec4(position, 1.0); float s = sin(angle); float c = cos(angle); - gl_Position.xy += mat2(c,-s, s, c) * OFFSETS[gl_VertexID & 3] * size; + gl_Position.xy += mat2(c,-s, s, c) * OFFSETS[vertexIndex] * size; gl_Position = u_ProjectionMatrix * gl_Position; - texCoord0 = in_TexCoord; + + texCoord0 = texUVs[vertexIndex]; + vertexColor = color * texelFetch(u_LightTex, light / 16, 0); } \ No newline at end of file From c278c882d300aa62c43615539c51ff9234ff0a0d Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Tue, 26 Sep 2023 19:20:53 -0500 Subject: [PATCH 11/30] Fix particle addition crash --- .../render/particle/ParticleExtended.java | 3 --- .../render/particle/ParticleManagerMixin.java | 17 ++--------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java index a5609aeaca..731e6b439a 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleExtended.java @@ -1,8 +1,5 @@ package me.jellysquid.mods.sodium.client.render.particle; -import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; -import net.minecraft.client.render.Camera; - public interface ParticleExtended { void sodium$configure(ParticleRenderView renderView); } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index 21707a861c..f9f502f488 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -82,9 +82,6 @@ public abstract class ParticleManagerMixin { "(Lnet/minecraft/class_4588;Lnet/minecraft/class_4184;F)V" ); - @Unique - private int glVertexBuffer; - @Unique private int glVertexArray; @@ -99,7 +96,6 @@ public abstract class ParticleManagerMixin { @Inject(method = "", at = @At("RETURN")) private void postInit(ClientWorld world, TextureManager textureManager, CallbackInfo ci) { - this.glVertexBuffer = GlStateManager._glGenBuffers(); this.glVertexArray = GlStateManager._glGenVertexArrays(); this.particleBuffer = new UnmanagedBufferBuilder(1); this.bufferTexture = new GlBufferTexture(); @@ -277,17 +273,8 @@ private void postSetWorld(ClientWorld world, CallbackInfo ci) { this.renderView = new ParticleRenderView(world); } - @Inject(method = "createParticle", at = @At("RETURN")) - private void postCreateParticle(T parameters, - double x, - double y, - double z, - double velocityX, - double velocityY, - double velocityZ, - CallbackInfoReturnable<@Nullable Particle> cir) { - var particle = cir.getReturnValue(); - + @Inject(method = "addParticle(Lnet/minecraft/client/particle/Particle;)V", at = @At("HEAD")) + private void preAddParticle(Particle particle, CallbackInfo ci) { if (particle instanceof ParticleExtended extension) { extension.sodium$configure(this.renderView); } From fe371662f60cb0664b40015925198ff30ead0878 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Tue, 26 Sep 2023 19:24:12 -0500 Subject: [PATCH 12/30] Fix comment --- src/main/resources/assets/sodium/shaders/particles/particle.vsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/sodium/shaders/particles/particle.vsh b/src/main/resources/assets/sodium/shaders/particles/particle.vsh index 9d5a380403..4c4a48f6d8 100644 --- a/src/main/resources/assets/sodium/shaders/particles/particle.vsh +++ b/src/main/resources/assets/sodium/shaders/particles/particle.vsh @@ -23,7 +23,7 @@ uniform usamplerBuffer u_BufferTexture; // R_32UI out vec2 texCoord0; out vec4 vertexColor; -// 7 x 4 = 28 bytes stride +// 11 x 4 = 44 bytes stride vec3 position; float size; vec4 color; From ed09a668947ae80ac8a33172fab5e1c46716b080 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Tue, 26 Sep 2023 23:23:02 -0500 Subject: [PATCH 13/30] Begin particle registry impl --- .../sodium/api/buffer/UnmanagedBufferBuilder.java | 10 +++++++++- .../sodium/client/gl/buffer/GlBufferTexture.java | 10 ++++++---- .../render/texture/ParticleTextureRegistry.java | 12 ++++++++++++ .../render/particle/ParticleManagerMixin.java | 6 +++++- 4 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java diff --git a/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java b/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java index 793c445ec5..3678d9a590 100644 --- a/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java +++ b/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java @@ -38,8 +38,16 @@ public void push(MemoryStack ignoredStack, long src, int size) { byteOffset += size; } + public Built build() { + return new Built(this.byteOffset, MemoryUtil.memSlice(this.buffer, 0, this.byteOffset)); + } + + public void reset() { + this.byteOffset = 0; + } + /** - * Resets this builder. + * Builds and resets this builder. * Make sure to use/upload the return value before pushing more data. * @return a ByteBuffer containing all the data pushed to this builder */ diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java index 37253eea80..408f69c353 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java @@ -21,13 +21,15 @@ public GlBufferTexture() { this.glBufferHandle = GlStateManager._glGenBuffers(); } - public void uploadData(ByteBuffer data, int size) { + public void putData(ByteBuffer data, int offset, int size) { + int neededSize = offset + size; GL31.glBindBuffer(GL31.GL_TEXTURE_BUFFER, this.glBufferHandle); - if (size > this.bufferSize) { + + if (neededSize > this.bufferSize) { RenderSystem.glBufferData(GL31.GL_TEXTURE_BUFFER, data, GlConst.GL_DYNAMIC_DRAW); - this.bufferSize = size; + this.bufferSize = neededSize; } else { - GL15.glBufferSubData(GL31.GL_TEXTURE_BUFFER, 0, data); + GL15.glBufferSubData(GL31.GL_TEXTURE_BUFFER, offset, data); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java b/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java new file mode 100644 index 0000000000..16b4412c85 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java @@ -0,0 +1,12 @@ +package me.jellysquid.mods.sodium.client.render.texture; + +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; + +public class ParticleTextureRegistry { + private final Long2IntOpenHashMap uvToIndex = new Long2IntOpenHashMap(); + + public int get(float u, float v) { + long key = ((long) Float.floatToRawIntBits(u)) << 32 | ((long) Float.floatToRawIntBits(v)); + return uvToIndex.get(key); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index f9f502f488..e1f4e6238a 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -88,6 +88,9 @@ public abstract class ParticleManagerMixin { @Unique private UnmanagedBufferBuilder particleBuffer; + @Unique + private UnmanagedBufferBuilder particleTextureBuffer; + @Unique private GlBufferTexture bufferTexture; @@ -98,6 +101,7 @@ public abstract class ParticleManagerMixin { private void postInit(ClientWorld world, TextureManager textureManager, CallbackInfo ci) { this.glVertexArray = GlStateManager._glGenVertexArrays(); this.particleBuffer = new UnmanagedBufferBuilder(1); + this.particleTextureBuffer = new UnmanagedBufferBuilder(1); this.bufferTexture = new GlBufferTexture(); this.renderView = new ParticleRenderView(world); } @@ -265,7 +269,7 @@ private void bindDummyVao() { @Unique private void uploadParticleBuffer() { UnmanagedBufferBuilder.Built particleData = this.particleBuffer.end(); - this.bufferTexture.uploadData(particleData.buffer, particleData.size); + this.bufferTexture.putData(particleData.buffer, 0, particleData.size); } @Inject(method = "setWorld", at = @At("RETURN")) From 2b84a1ee7d42a44580cd5cf1e676f09e9d2e266c Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Tue, 26 Sep 2023 23:23:02 -0500 Subject: [PATCH 14/30] Begin particle registry impl --- .../api/buffer/UnmanagedBufferBuilder.java | 10 +++++- .../client/gl/buffer/GlBufferTexture.java | 10 +++--- .../texture/ParticleTextureRegistry.java | 35 +++++++++++++++++++ .../render/particle/ParticleManagerMixin.java | 6 +++- 4 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java diff --git a/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java b/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java index 793c445ec5..3678d9a590 100644 --- a/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java +++ b/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java @@ -38,8 +38,16 @@ public void push(MemoryStack ignoredStack, long src, int size) { byteOffset += size; } + public Built build() { + return new Built(this.byteOffset, MemoryUtil.memSlice(this.buffer, 0, this.byteOffset)); + } + + public void reset() { + this.byteOffset = 0; + } + /** - * Resets this builder. + * Builds and resets this builder. * Make sure to use/upload the return value before pushing more data. * @return a ByteBuffer containing all the data pushed to this builder */ diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java index 37253eea80..408f69c353 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java @@ -21,13 +21,15 @@ public GlBufferTexture() { this.glBufferHandle = GlStateManager._glGenBuffers(); } - public void uploadData(ByteBuffer data, int size) { + public void putData(ByteBuffer data, int offset, int size) { + int neededSize = offset + size; GL31.glBindBuffer(GL31.GL_TEXTURE_BUFFER, this.glBufferHandle); - if (size > this.bufferSize) { + + if (neededSize > this.bufferSize) { RenderSystem.glBufferData(GL31.GL_TEXTURE_BUFFER, data, GlConst.GL_DYNAMIC_DRAW); - this.bufferSize = size; + this.bufferSize = neededSize; } else { - GL15.glBufferSubData(GL31.GL_TEXTURE_BUFFER, 0, data); + GL15.glBufferSubData(GL31.GL_TEXTURE_BUFFER, offset, data); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java b/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java new file mode 100644 index 0000000000..8a80a07bc0 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java @@ -0,0 +1,35 @@ +package me.jellysquid.mods.sodium.client.render.texture; + +import it.unimi.dsi.fastutil.floats.FloatFloatImmutablePair; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +public class ParticleTextureRegistry { + private final Object2IntOpenHashMap uvToIndex = new Object2IntOpenHashMap<>(); + + private ArrayList toAdd = new ArrayList<>(); + + private int currentIndex = 0; + + public int get(float u, float v) { + FloatFloatImmutablePair key = new FloatFloatImmutablePair(u, v); + return uvToIndex.computeIfAbsent(key, pairKey -> { + toAdd.add((FloatFloatImmutablePair) pairKey); + return currentIndex++; + }); + } + + public boolean isDirty() { + return !toAdd.isEmpty(); + } + + public List flushUpdates() { + var ret = this.toAdd; + this.toAdd = new ArrayList<>(); + return ret; + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index f9f502f488..e1f4e6238a 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -88,6 +88,9 @@ public abstract class ParticleManagerMixin { @Unique private UnmanagedBufferBuilder particleBuffer; + @Unique + private UnmanagedBufferBuilder particleTextureBuffer; + @Unique private GlBufferTexture bufferTexture; @@ -98,6 +101,7 @@ public abstract class ParticleManagerMixin { private void postInit(ClientWorld world, TextureManager textureManager, CallbackInfo ci) { this.glVertexArray = GlStateManager._glGenVertexArrays(); this.particleBuffer = new UnmanagedBufferBuilder(1); + this.particleTextureBuffer = new UnmanagedBufferBuilder(1); this.bufferTexture = new GlBufferTexture(); this.renderView = new ParticleRenderView(world); } @@ -265,7 +269,7 @@ private void bindDummyVao() { @Unique private void uploadParticleBuffer() { UnmanagedBufferBuilder.Built particleData = this.particleBuffer.end(); - this.bufferTexture.uploadData(particleData.buffer, particleData.size); + this.bufferTexture.putData(particleData.buffer, 0, particleData.size); } @Inject(method = "setWorld", at = @At("RETURN")) From 87538049ffb381c2a5336c0c26e31da26fbb78fa Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Thu, 28 Sep 2023 22:20:59 -0500 Subject: [PATCH 15/30] Begin particle texture cache --- .../mods/sodium/api/util/RawUVs.java | 39 ++++++++++++++++++ .../render/particle/BillboardExtended.java | 3 +- .../shader/BillboardParticleData.java | 13 +++--- .../shader/ParticleShaderInterface.java | 2 + .../texture/ParticleTextureRegistry.java | 17 ++++---- .../particle/BillboardParticleMixin.java | 20 ++++----- .../render/particle/ParticleManagerMixin.java | 41 +++++++++++++++---- .../sodium/shaders/particles/particle.vsh | 18 +++++--- 8 files changed, 111 insertions(+), 42 deletions(-) create mode 100644 src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java diff --git a/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java b/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java new file mode 100644 index 0000000000..bb3dbe266a --- /dev/null +++ b/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java @@ -0,0 +1,39 @@ +package net.caffeinemc.mods.sodium.api.util; + +import org.lwjgl.system.MemoryUtil; + +public record RawUVs(float minU, float minV, float maxU, float maxV) { + public static final int STRIDE = 16; + + public void put(long ptr) { + MemoryUtil.memPutFloat(ptr + 0, minU); + MemoryUtil.memPutFloat(ptr + 4, minV); + MemoryUtil.memPutFloat(ptr + 8, maxU); + MemoryUtil.memPutFloat(ptr + 12, maxV); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o instanceof RawUVs other) { + return minU == other.minU() && + minV == other.minV() && + maxU == other.maxU() && + maxV == other.maxV(); + } else { + return false; + } + } + + @Override + public int hashCode() { + int result = 1; + + result = 31 * result + Float.floatToRawIntBits(minU); + result = 31 * result + Float.floatToRawIntBits(minV); + result = 31 * result + Float.floatToRawIntBits(maxU); + result = 31 * result + Float.floatToRawIntBits(maxV); + + return result; + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java index f863721bf0..1845e9cb4d 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java @@ -1,8 +1,9 @@ package me.jellysquid.mods.sodium.client.render.particle; +import me.jellysquid.mods.sodium.client.render.texture.ParticleTextureRegistry; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.minecraft.client.render.Camera; public interface BillboardExtended { - void sodium$buildParticleData(UnmanagedBufferBuilder builder, Camera camera, float tickDelta); + void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureRegistry registry, Camera camera, float tickDelta); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java index a29e062b2c..b577ca1f12 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/BillboardParticleData.java @@ -12,20 +12,17 @@ public class BillboardParticleData { public static final int COLOR_OFFSET = 16; public static final int LIGHT_UV_OFFSET = 20; public static final int ANGLE_OFFSET = 24; - public static final int MIN_UV_OFFSET = 28; - public static final int MAX_UV_OFFSET = 36; - public static final int STRIDE = 44; + public static final int TEXTURE_INDEX_OFFSET = 28; + public static final int STRIDE = 32; - public static void put(long ptr, float x, float y, float z, - int color, int light, float size, float angle, - float minU, float minV, float maxU, float maxV + public static void put(long ptr, float x, float y, float z, int color, + int light, float size, float angle, int textureIndex ) { PositionAttribute.put(ptr + POSITION_OFFSET, x, y, z); MemoryUtil.memPutFloat(ptr + SIZE_OFFSET, size); ColorAttribute.set(ptr + COLOR_OFFSET, color); LightAttribute.set(ptr + LIGHT_UV_OFFSET, light); MemoryUtil.memPutFloat(ptr + ANGLE_OFFSET, angle); - TextureAttribute.put(ptr + MIN_UV_OFFSET, minU, minV); - TextureAttribute.put(ptr + MAX_UV_OFFSET, maxU, maxV); + MemoryUtil.memPutInt(ptr + TEXTURE_INDEX_OFFSET, textureIndex); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java index 03ec923b6d..3bd7de7c36 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java @@ -15,6 +15,7 @@ import org.lwjgl.opengl.GL32C; public class ParticleShaderInterface { + private final GlUniformInt uniformDataOffset; private final GlUniformInt uniformParticleTexture; private final GlUniformInt uniformLightTexture; private final GlUniformMatrix4f uniformModelViewMatrix; @@ -22,6 +23,7 @@ public class ParticleShaderInterface { private final GlUniformInt uniformBufferTexture; public ParticleShaderInterface(ShaderBindingContext context) { + this.uniformDataOffset = context.bindUniform("u_DataOffset", GlUniformInt::new); this.uniformParticleTexture = context.bindUniform("u_ParticleTex", GlUniformInt::new); this.uniformLightTexture = context.bindUniform("u_LightTex", GlUniformInt::new); this.uniformModelViewMatrix = context.bindUniform("u_ModelViewMatrix", GlUniformMatrix4f::new); diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java b/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java index e227e10696..d0f7eeae99 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java @@ -1,24 +1,21 @@ package me.jellysquid.mods.sodium.client.render.texture; -import it.unimi.dsi.fastutil.floats.FloatFloatImmutablePair; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.caffeinemc.mods.sodium.api.util.RawUVs; import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; import java.util.List; public class ParticleTextureRegistry { - private final Object2IntOpenHashMap uvToIndex = new Object2IntOpenHashMap<>(); + private final Object2IntOpenHashMap uvToIndex = new Object2IntOpenHashMap<>(); - private ArrayList toAdd = new ArrayList<>(); + private ArrayList toAdd = new ArrayList<>(); private int currentIndex = 0; - public int get(float u, float v) { - FloatFloatImmutablePair key = new FloatFloatImmutablePair(u, v); - return uvToIndex.computeIfAbsent(key, pairKey -> { - toAdd.add((FloatFloatImmutablePair) pairKey); + public int get(RawUVs uvs) { + return uvToIndex.computeIfAbsent(uvs, pairKey -> { + toAdd.add((RawUVs) pairKey); return currentIndex++; }); } @@ -27,7 +24,7 @@ public boolean isDirty() { return !toAdd.isEmpty(); } - public List drainUpdates() { + public List drainUpdates() { var ret = this.toAdd; this.toAdd = new ArrayList<>(); return ret; diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java index 0e6e99f782..64ad1e3306 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java @@ -2,8 +2,10 @@ import me.jellysquid.mods.sodium.client.render.particle.BillboardExtended; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleData; +import me.jellysquid.mods.sodium.client.render.texture.ParticleTextureRegistry; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.caffeinemc.mods.sodium.api.util.ColorABGR; +import net.caffeinemc.mods.sodium.api.util.RawUVs; import net.minecraft.client.particle.BillboardParticle; import net.minecraft.client.particle.Particle; import net.minecraft.client.render.Camera; @@ -38,13 +40,14 @@ protected BillboardParticleMixin(ClientWorld world, double x, double y, double z } @Override - public void sodium$buildParticleData(UnmanagedBufferBuilder builder, Camera camera, float tickDelta) { + public void sodium$buildParticleData( + UnmanagedBufferBuilder builder, + ParticleTextureRegistry registry, + Camera camera, float tickDelta + ) { Vec3d vec3d = camera.getPos(); - - float minU = this.getMinU(); - float maxU = this.getMaxU(); - float minV = this.getMinV(); - float maxV = this.getMaxV(); + RawUVs uvs = new RawUVs(getMinU(), getMinV(), getMaxU(), getMaxV()); + int textureIndex = registry.get(uvs); float x = (float) (MathHelper.lerp(tickDelta, this.prevPosX, this.x) - vec3d.getX()); float y = (float) (MathHelper.lerp(tickDelta, this.prevPosY, this.y) - vec3d.getY()); @@ -59,10 +62,7 @@ protected BillboardParticleMixin(ClientWorld world, double x, double y, double z try (MemoryStack stack = MemoryStack.stackPush()) { long ptr = stack.nmalloc(BillboardParticleData.STRIDE); - BillboardParticleData.put( - ptr, x, y, z, color, light, size, angle, - minU, minV, maxU, maxV - ); + BillboardParticleData.put(ptr, x, y, z, color, light, size, angle, textureIndex); builder.push(stack, ptr, BillboardParticleData.STRIDE); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index e1f4e6238a..d8f287c6df 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -3,12 +3,10 @@ import com.google.common.collect.EvictingQueue; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.mojang.blaze3d.platform.GlConst; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; -import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeBinding; import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeFormat; import me.jellysquid.mods.sodium.client.gl.buffer.GlBufferTexture; import me.jellysquid.mods.sodium.client.render.particle.BillboardExtended; @@ -16,24 +14,27 @@ import me.jellysquid.mods.sodium.client.render.particle.ParticleRenderView; import me.jellysquid.mods.sodium.client.render.particle.ShaderBillboardParticleRenderer; import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; +import me.jellysquid.mods.sodium.client.render.texture.ParticleTextureRegistry; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; +import net.caffeinemc.mods.sodium.api.util.RawUVs; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.particle.*; -import net.minecraft.client.render.*; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.LightmapTextureManager; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.texture.SpriteAtlasTexture; import net.minecraft.client.texture.TextureManager; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; -import net.minecraft.particle.ParticleEffect; import net.minecraft.util.Identifier; -import org.jetbrains.annotations.Nullable; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL20C; +import org.lwjgl.system.MemoryStack; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import java.util.Collection; @@ -68,6 +69,9 @@ public abstract class ParticleManagerMixin { @Unique private final ShaderBillboardParticleRenderer particleRenderer = new ShaderBillboardParticleRenderer(); + @Unique + private final ParticleTextureRegistry particleTexRegistry = new ParticleTextureRegistry(); + @Unique private static final Object2BooleanMap> classOverridesBuild = new Object2BooleanOpenHashMap<>(); @@ -213,7 +217,11 @@ public void renderParticles( particleRenderer.setupState(); for (BillboardParticle particle : iterable) { - ((BillboardExtended) particle).sodium$buildParticleData(particleBuffer, camera, tickDelta); + ((BillboardExtended) particle).sodium$buildParticleData( + particleBuffer, + particleTexRegistry, + camera, tickDelta + ); } drawParticleTextureSheet(particleTextureSheet, numParticles); @@ -268,8 +276,25 @@ private void bindDummyVao() { @Unique private void uploadParticleBuffer() { + if (this.particleTexRegistry.isDirty()) { + List updates = this.particleTexRegistry.drainUpdates(); + int size = RawUVs.STRIDE * updates.size(); + try (MemoryStack stack = MemoryStack.stackPush()) { + long buffer = stack.nmalloc(size); + long ptr = buffer; + for (RawUVs uvs : updates) { + uvs.put(ptr); + ptr += RawUVs.STRIDE; + } + particleTextureBuffer.push(stack, buffer, size); + } + } + + UnmanagedBufferBuilder.Built registry = this.particleTextureBuffer.build(); UnmanagedBufferBuilder.Built particleData = this.particleBuffer.end(); - this.bufferTexture.putData(particleData.buffer, 0, particleData.size); + + this.bufferTexture.putData(particleData.buffer, registry.size, particleData.size); + this.bufferTexture.putData(registry.buffer, 0, registry.size); } @Inject(method = "setWorld", at = @At("RETURN")) diff --git a/src/main/resources/assets/sodium/shaders/particles/particle.vsh b/src/main/resources/assets/sodium/shaders/particles/particle.vsh index 4c4a48f6d8..09aed82672 100644 --- a/src/main/resources/assets/sodium/shaders/particles/particle.vsh +++ b/src/main/resources/assets/sodium/shaders/particles/particle.vsh @@ -1,7 +1,8 @@ #version 330 #define COLOR_SCALE 1.0 / 255.0 -#define PARTICLE_STRIDE 11 +#define PARTICLE_STRIDE 8 +#define TEX_STRIDE 4 #import @@ -17,18 +18,21 @@ const int INDICES[] = int[]( 0, 2, 3 ); +uniform int u_DataOffset; uniform sampler2D u_LightTex; uniform usamplerBuffer u_BufferTexture; // R_32UI out vec2 texCoord0; out vec4 vertexColor; -// 11 x 4 = 44 bytes stride +// 8 x 4 = 32 bytes stride vec3 position; float size; vec4 color; ivec2 light; float angle; +// int textureIndex; Not necessary to store here + vec2 minTexUV; vec2 maxTexUV; @@ -59,20 +63,24 @@ vec2 readBufferTex(int ptr) { } void init() { - int base = PARTICLE_STRIDE * (gl_VertexID / 6); + int base = (PARTICLE_STRIDE * (gl_VertexID / 6)) + u_DataOffset; position = readBufferPos(base); size = readBufferF(base + 3); color = readBufferColor(base + 4); light = readBufferLight(base + 5); angle = readBufferF(base + 6); - minTexUV = readBufferTex(base + 7); - maxTexUV = readBufferTex(base + 9); + int textureIndex = int(readBuffer(base + 7)); + + int texturePtr = textureIndex * TEX_STRIDE; + minTexUV = readBufferTex(texturePtr); + maxTexUV = readBufferTex(texturePtr + 2); } void main() { init(); int vertexIndex = INDICES[gl_VertexID % 6]; + vec2 texUVs[] = vec2[]( maxTexUV, vec2(maxTexUV.x, minTexUV.y), From abf35c70df25b1e932e80c1a848830b8dbec1714 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Fri, 29 Sep 2023 17:09:42 -0500 Subject: [PATCH 16/30] Particle cache implementation --- .../mods/sodium/api/util/RawUVs.java | 7 ++ .../mods/sodium/client/SodiumPreLaunch.java | 2 +- .../render/particle/BillboardExtended.java | 4 +- .../particle/cache/ParticleTextureCache.java | 74 +++++++++++++++++++ .../particle/cache/TextureUsageMonitor.java | 64 ++++++++++++++++ .../shader/ParticleShaderInterface.java | 4 + .../texture/ParticleTextureRegistry.java | 32 -------- .../particle/BillboardParticleMixin.java | 6 +- .../render/particle/ParticleManagerMixin.java | 39 ++++++---- 9 files changed, 179 insertions(+), 53 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/ParticleTextureCache.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/TextureUsageMonitor.java delete mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java diff --git a/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java b/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java index bb3dbe266a..0c9c33ed8b 100644 --- a/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java +++ b/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java @@ -12,6 +12,13 @@ public void put(long ptr) { MemoryUtil.memPutFloat(ptr + 12, maxV); } + public static void putNull(long ptr) { + MemoryUtil.memPutFloat(ptr + 0, Float.NaN); + MemoryUtil.memPutFloat(ptr + 4, Float.NaN); + MemoryUtil.memPutFloat(ptr + 8, Float.NaN); + MemoryUtil.memPutFloat(ptr + 12, Float.NaN); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java index bb725462aa..014b905e8c 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java @@ -11,6 +11,6 @@ public void onPreLaunch() { GraphicsAdapterProbe.findAdapters(); PreLaunchChecks.checkDrivers(); Workarounds.init(); -// System.loadLibrary("renderdoc"); + System.loadLibrary("renderdoc"); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java index 1845e9cb4d..d331110dc6 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java @@ -1,9 +1,9 @@ package me.jellysquid.mods.sodium.client.render.particle; -import me.jellysquid.mods.sodium.client.render.texture.ParticleTextureRegistry; +import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.minecraft.client.render.Camera; public interface BillboardExtended { - void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureRegistry registry, Camera camera, float tickDelta); + void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/ParticleTextureCache.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/ParticleTextureCache.java new file mode 100644 index 0000000000..e04e8e5c18 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/ParticleTextureCache.java @@ -0,0 +1,74 @@ +package me.jellysquid.mods.sodium.client.render.particle.cache; + +import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.caffeinemc.mods.sodium.api.util.RawUVs; + +import java.util.*; + +public class ParticleTextureCache { + private static final int DEFAULT_CAPACITY = 64; + + private RawUVs[] rawUVs; + private final Object2IntOpenHashMap uvToIndex = new Object2IntOpenHashMap<>(); + private final TextureUsageMonitor usageMonitor = new TextureUsageMonitor(); + + private final IntArrayFIFOQueue freeIndices = new IntArrayFIFOQueue(); + private int topIndex = 0; + + public ParticleTextureCache() { + this.rawUVs = new RawUVs[DEFAULT_CAPACITY]; + } + + public int getTopIndex() { + return topIndex; + } + + public int getUvIndex(RawUVs uvs) { + int use = uvToIndex.computeIfAbsent(uvs, key -> { + RawUVs uvsKey = (RawUVs) key; + int index = freeIndices.isEmpty() ? topIndex++ : freeIndices.dequeueInt(); + + ensureCapacity(index); + rawUVs[index] = uvsKey; + return index; + }); + + usageMonitor.markUsed(use); + return use; + } + + /** + * @return The array of RawUVs that should be uploaded + */ + public RawUVs[] update() { + IntList toRemove = this.usageMonitor.update(); + for (int i = 0; i < toRemove.size(); i++) { + int index = toRemove.getInt(i); + + RawUVs uvs = rawUVs[index]; + rawUVs[index] = null; + + this.freeIndices.enqueue(index); + uvToIndex.removeInt(uvs); + } + return this.rawUVs; + } + + private void ensureCapacity(int high) { + if (high >= this.rawUVs.length) { + reallocRawUVs(high); + } + } + + private void reallocRawUVs(int high) { + int newCapacity = this.rawUVs.length; + while (high >= newCapacity) { + newCapacity += (newCapacity >> 1); + } + RawUVs[] newArray = new RawUVs[newCapacity]; + System.arraycopy(rawUVs, 0, newArray, 0, rawUVs.length); + this.rawUVs = newArray; + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/TextureUsageMonitor.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/TextureUsageMonitor.java new file mode 100644 index 0000000000..9f54f4ec2a --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/TextureUsageMonitor.java @@ -0,0 +1,64 @@ +package me.jellysquid.mods.sodium.client.render.particle.cache; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntCollection; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; + +import java.util.Arrays; + +public class TextureUsageMonitor { + private static final int DEFAULT_INITIAL_CAPACITY = 64; + + private static final byte REMOVE_AFTER = 4; + + private byte[] lastUsed; + + public TextureUsageMonitor(int initialCapacity) { + this.lastUsed = new byte[initialCapacity]; + Arrays.fill(lastUsed, (byte) -1); + } + + public TextureUsageMonitor() { + this(DEFAULT_INITIAL_CAPACITY); + } + + public void markUsed(int index) { + ensureCapacity(index); + lastUsed[index] = 0; + } + + /** + * @return a List of the index of the UVs that should be removed + */ + public IntList update() { + IntList toRemove = new IntArrayList(); + for (int i = 0; i < lastUsed.length; ++i) { + byte v = lastUsed[i]; + if (v < 0) continue; + + byte framesUnused = ++lastUsed[i]; + if (framesUnused > REMOVE_AFTER) { + lastUsed[i] = -1; + toRemove.add(i); + } + } + + return toRemove; + } + + private void ensureCapacity(int high) { + if (high >= this.lastUsed.length) reallocLastUsed(high); + } + + private void reallocLastUsed(int high) { + int newCapacity = this.lastUsed.length; + while (high >= newCapacity) { + newCapacity += (newCapacity >> 1); + } + byte[] newArray = new byte[newCapacity]; + System.arraycopy(lastUsed, 0, newArray, 0, lastUsed.length); + Arrays.fill(newArray, lastUsed.length, newCapacity, (byte) -1); + this.lastUsed = newArray; + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java index 3bd7de7c36..7c93ffd133 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java @@ -39,6 +39,10 @@ public void setModelViewMatrix(Matrix4fc matrix) { this.uniformModelViewMatrix.set(matrix); } + public void setDataOffset(int dataOffset) { + this.uniformDataOffset.setInt(dataOffset); + } + public void setupState() { // "BlockTexture" should represent the particle textures if bound correctly this.bindParticleTexture(ParticleShaderTextureSlot.TEXTURE, TextureUtil.getBlockTextureId()); diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java b/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java deleted file mode 100644 index d0f7eeae99..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/texture/ParticleTextureRegistry.java +++ /dev/null @@ -1,32 +0,0 @@ -package me.jellysquid.mods.sodium.client.render.texture; - -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import net.caffeinemc.mods.sodium.api.util.RawUVs; - -import java.util.ArrayList; -import java.util.List; - -public class ParticleTextureRegistry { - private final Object2IntOpenHashMap uvToIndex = new Object2IntOpenHashMap<>(); - - private ArrayList toAdd = new ArrayList<>(); - - private int currentIndex = 0; - - public int get(RawUVs uvs) { - return uvToIndex.computeIfAbsent(uvs, pairKey -> { - toAdd.add((RawUVs) pairKey); - return currentIndex++; - }); - } - - public boolean isDirty() { - return !toAdd.isEmpty(); - } - - public List drainUpdates() { - var ret = this.toAdd; - this.toAdd = new ArrayList<>(); - return ret; - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java index 64ad1e3306..4bad4ec272 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java @@ -2,7 +2,7 @@ import me.jellysquid.mods.sodium.client.render.particle.BillboardExtended; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleData; -import me.jellysquid.mods.sodium.client.render.texture.ParticleTextureRegistry; +import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.caffeinemc.mods.sodium.api.util.ColorABGR; import net.caffeinemc.mods.sodium.api.util.RawUVs; @@ -42,12 +42,12 @@ protected BillboardParticleMixin(ClientWorld world, double x, double y, double z @Override public void sodium$buildParticleData( UnmanagedBufferBuilder builder, - ParticleTextureRegistry registry, + ParticleTextureCache registry, Camera camera, float tickDelta ) { Vec3d vec3d = camera.getPos(); RawUVs uvs = new RawUVs(getMinU(), getMinV(), getMaxU(), getMaxV()); - int textureIndex = registry.get(uvs); + int textureIndex = registry.getUvIndex(uvs); float x = (float) (MathHelper.lerp(tickDelta, this.prevPosX, this.x) - vec3d.getX()); float y = (float) (MathHelper.lerp(tickDelta, this.prevPosY, this.y) - vec3d.getY()); diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index d8f287c6df..4e5246e8f2 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -14,7 +14,7 @@ import me.jellysquid.mods.sodium.client.render.particle.ParticleRenderView; import me.jellysquid.mods.sodium.client.render.particle.ShaderBillboardParticleRenderer; import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; -import me.jellysquid.mods.sodium.client.render.texture.ParticleTextureRegistry; +import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.caffeinemc.mods.sodium.api.util.RawUVs; import net.fabricmc.loader.api.FabricLoader; @@ -70,7 +70,7 @@ public abstract class ParticleManagerMixin { private final ShaderBillboardParticleRenderer particleRenderer = new ShaderBillboardParticleRenderer(); @Unique - private final ParticleTextureRegistry particleTexRegistry = new ParticleTextureRegistry(); + private final ParticleTextureCache particleTexCache = new ParticleTextureCache(); @Unique private static final Object2BooleanMap> classOverridesBuild = new Object2BooleanOpenHashMap<>(); @@ -219,7 +219,7 @@ public void renderParticles( for (BillboardParticle particle : iterable) { ((BillboardExtended) particle).sodium$buildParticleData( particleBuffer, - particleTexRegistry, + particleTexCache, camera, tickDelta ); } @@ -276,25 +276,34 @@ private void bindDummyVao() { @Unique private void uploadParticleBuffer() { - if (this.particleTexRegistry.isDirty()) { - List updates = this.particleTexRegistry.drainUpdates(); - int size = RawUVs.STRIDE * updates.size(); - try (MemoryStack stack = MemoryStack.stackPush()) { - long buffer = stack.nmalloc(size); - long ptr = buffer; - for (RawUVs uvs : updates) { + RawUVs[] toUpload = this.particleTexCache.update(); + int maxUploadIndex = this.particleTexCache.getTopIndex(); + int size = RawUVs.STRIDE * maxUploadIndex; + + try (MemoryStack stack = MemoryStack.stackPush()) { + long buffer = stack.nmalloc(size); + long ptr = buffer; + for (int i = 0; i < maxUploadIndex; i++) { + RawUVs uvs = toUpload[i]; + if (uvs == null) { + RawUVs.putNull(ptr); + } else { uvs.put(ptr); - ptr += RawUVs.STRIDE; } - particleTextureBuffer.push(stack, buffer, size); + ptr += RawUVs.STRIDE; } + particleTextureBuffer.push(stack, buffer, size); } - UnmanagedBufferBuilder.Built registry = this.particleTextureBuffer.build(); + particleRenderer.getActiveProgram() + .getInterface() + .setDataOffset(size / 4); + + UnmanagedBufferBuilder.Built cache = this.particleTextureBuffer.end(); UnmanagedBufferBuilder.Built particleData = this.particleBuffer.end(); - this.bufferTexture.putData(particleData.buffer, registry.size, particleData.size); - this.bufferTexture.putData(registry.buffer, 0, registry.size); + this.bufferTexture.putData(particleData.buffer, cache.size, particleData.size); + this.bufferTexture.putData(cache.buffer, 0, cache.size); } @Inject(method = "setWorld", at = @At("RETURN")) From 26df41e6d8fec472e77eed4f50968ce2f2be41c3 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Sun, 1 Oct 2023 13:18:34 -0500 Subject: [PATCH 17/30] Fix uploading issues --- .../client/gl/buffer/GlBufferTexture.java | 11 ++--- .../render/particle/ParticleDataBuffer.java | 20 +++++++++ .../shader/ParticleShaderInterface.java | 13 +++--- .../render/particle/ParticleManagerMixin.java | 43 ++++++++----------- .../sodium/shaders/particles/particle.vsh | 6 +-- 5 files changed, 49 insertions(+), 44 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java index f971bcde25..1f60707b1f 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java @@ -21,17 +21,14 @@ public GlBufferTexture() { this.glBufferHandle = GlStateManager._glGenBuffers(); } - public void putData(ByteBuffer data, int offset, int size) { - int neededSize = offset + size; + public void putData(ByteBuffer data, int size) { GL31.glBindBuffer(GL31.GL_TEXTURE_BUFFER, this.glBufferHandle); - if (neededSize > this.bufferSize) { - // This is flawed since it can reallocate and lose information - // if the entire buffer is not overwritten... opting to not fix it for now + if (size > this.bufferSize) { RenderSystem.glBufferData(GL31.GL_TEXTURE_BUFFER, data, GlConst.GL_DYNAMIC_DRAW); - this.bufferSize = neededSize; + this.bufferSize = size; } else { - GL15.glBufferSubData(GL31.GL_TEXTURE_BUFFER, offset, data); + GL15.glBufferSubData(GL31.GL_TEXTURE_BUFFER, 0, data); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java new file mode 100644 index 0000000000..3118a36b7f --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java @@ -0,0 +1,20 @@ +package me.jellysquid.mods.sodium.client.render.particle; + +import me.jellysquid.mods.sodium.client.gl.buffer.GlBufferTexture; +import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; + +public class ParticleDataBuffer { + private final GlBufferTexture bufferTexture; + + public ParticleDataBuffer() { + this.bufferTexture = new GlBufferTexture(); + } + + public void uploadParticleData(UnmanagedBufferBuilder.Built data) { + this.bufferTexture.putData(data.buffer, data.size); + } + + public void bind() { + this.bufferTexture.bind(); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java index 7c93ffd133..4e58807872 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java @@ -2,20 +2,17 @@ import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; -import me.jellysquid.mods.sodium.client.gl.shader.uniform.GlUniformFloat4v; import me.jellysquid.mods.sodium.client.gl.shader.uniform.GlUniformInt; import me.jellysquid.mods.sodium.client.gl.shader.uniform.GlUniformMatrix4f; -import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderTextureSlot; import me.jellysquid.mods.sodium.client.render.chunk.shader.ShaderBindingContext; import me.jellysquid.mods.sodium.client.util.TextureUtil; -import org.joml.*; +import org.joml.Matrix4fc; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL31; -import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32C; public class ParticleShaderInterface { - private final GlUniformInt uniformDataOffset; + private final GlUniformInt uniformTextureOffset; private final GlUniformInt uniformParticleTexture; private final GlUniformInt uniformLightTexture; private final GlUniformMatrix4f uniformModelViewMatrix; @@ -23,7 +20,7 @@ public class ParticleShaderInterface { private final GlUniformInt uniformBufferTexture; public ParticleShaderInterface(ShaderBindingContext context) { - this.uniformDataOffset = context.bindUniform("u_DataOffset", GlUniformInt::new); + this.uniformTextureOffset = context.bindUniform("u_TextureOffset", GlUniformInt::new); this.uniformParticleTexture = context.bindUniform("u_ParticleTex", GlUniformInt::new); this.uniformLightTexture = context.bindUniform("u_LightTex", GlUniformInt::new); this.uniformModelViewMatrix = context.bindUniform("u_ModelViewMatrix", GlUniformMatrix4f::new); @@ -39,8 +36,8 @@ public void setModelViewMatrix(Matrix4fc matrix) { this.uniformModelViewMatrix.set(matrix); } - public void setDataOffset(int dataOffset) { - this.uniformDataOffset.setInt(dataOffset); + public void setTextureOffset(int dataOffset) { + this.uniformTextureOffset.setInt(dataOffset); } public void setupState() { diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index 4e5246e8f2..fd59045b8d 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -8,13 +8,10 @@ import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeFormat; -import me.jellysquid.mods.sodium.client.gl.buffer.GlBufferTexture; -import me.jellysquid.mods.sodium.client.render.particle.BillboardExtended; -import me.jellysquid.mods.sodium.client.render.particle.ParticleExtended; -import me.jellysquid.mods.sodium.client.render.particle.ParticleRenderView; -import me.jellysquid.mods.sodium.client.render.particle.ShaderBillboardParticleRenderer; -import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; +import me.jellysquid.mods.sodium.client.render.particle.*; import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; +import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleData; +import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.caffeinemc.mods.sodium.api.util.RawUVs; import net.fabricmc.loader.api.FabricLoader; @@ -90,13 +87,10 @@ public abstract class ParticleManagerMixin { private int glVertexArray; @Unique - private UnmanagedBufferBuilder particleBuffer; - - @Unique - private UnmanagedBufferBuilder particleTextureBuffer; + private UnmanagedBufferBuilder dataBufferBuilder; @Unique - private GlBufferTexture bufferTexture; + private ParticleDataBuffer dataBuffer; @Unique private Identifier prevTexture = null; @@ -104,9 +98,8 @@ public abstract class ParticleManagerMixin { @Inject(method = "", at = @At("RETURN")) private void postInit(ClientWorld world, TextureManager textureManager, CallbackInfo ci) { this.glVertexArray = GlStateManager._glGenVertexArrays(); - this.particleBuffer = new UnmanagedBufferBuilder(1); - this.particleTextureBuffer = new UnmanagedBufferBuilder(1); - this.bufferTexture = new GlBufferTexture(); + this.dataBufferBuilder = new UnmanagedBufferBuilder(1); + this.dataBuffer = new ParticleDataBuffer(); this.renderView = new ParticleRenderView(world); } @@ -213,12 +206,12 @@ public void renderParticles( if (iterable != null && !iterable.isEmpty()) { int numParticles = iterable.size(); bindParticleTextureSheet(particleTextureSheet); - this.bufferTexture.bind(); + this.dataBuffer.bind(); particleRenderer.setupState(); for (BillboardParticle particle : iterable) { ((BillboardExtended) particle).sodium$buildParticleData( - particleBuffer, + dataBufferBuilder, particleTexCache, camera, tickDelta ); @@ -261,7 +254,7 @@ private void bindParticleTextureSheet(ParticleTextureSheet sheet) { @Unique private void drawParticleTextureSheet(ParticleTextureSheet sheet, int numParticles) { if (sheet == ParticleTextureSheet.TERRAIN_SHEET || sheet == ParticleTextureSheet.PARTICLE_SHEET_LIT || sheet == ParticleTextureSheet.PARTICLE_SHEET_OPAQUE || sheet == ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT) { - uploadParticleBuffer(); + uploadParticleBuffer(numParticles); bindDummyVao(); GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, numParticles * 6); } @@ -275,12 +268,13 @@ private void bindDummyVao() { } @Unique - private void uploadParticleBuffer() { + private void uploadParticleBuffer(int numParticles) { RawUVs[] toUpload = this.particleTexCache.update(); int maxUploadIndex = this.particleTexCache.getTopIndex(); - int size = RawUVs.STRIDE * maxUploadIndex; try (MemoryStack stack = MemoryStack.stackPush()) { + int size = RawUVs.STRIDE * maxUploadIndex; + long buffer = stack.nmalloc(size); long ptr = buffer; for (int i = 0; i < maxUploadIndex; i++) { @@ -292,18 +286,15 @@ private void uploadParticleBuffer() { } ptr += RawUVs.STRIDE; } - particleTextureBuffer.push(stack, buffer, size); + dataBufferBuilder.push(stack, buffer, size); } particleRenderer.getActiveProgram() .getInterface() - .setDataOffset(size / 4); - - UnmanagedBufferBuilder.Built cache = this.particleTextureBuffer.end(); - UnmanagedBufferBuilder.Built particleData = this.particleBuffer.end(); + .setTextureOffset((numParticles * BillboardParticleData.STRIDE) / 4); - this.bufferTexture.putData(particleData.buffer, cache.size, particleData.size); - this.bufferTexture.putData(cache.buffer, 0, cache.size); + UnmanagedBufferBuilder.Built data = dataBufferBuilder.end(); + this.dataBuffer.uploadParticleData(data); } @Inject(method = "setWorld", at = @At("RETURN")) diff --git a/src/main/resources/assets/sodium/shaders/particles/particle.vsh b/src/main/resources/assets/sodium/shaders/particles/particle.vsh index 09aed82672..ab687c2f88 100644 --- a/src/main/resources/assets/sodium/shaders/particles/particle.vsh +++ b/src/main/resources/assets/sodium/shaders/particles/particle.vsh @@ -18,7 +18,7 @@ const int INDICES[] = int[]( 0, 2, 3 ); -uniform int u_DataOffset; +uniform int u_TextureOffset; uniform sampler2D u_LightTex; uniform usamplerBuffer u_BufferTexture; // R_32UI @@ -63,7 +63,7 @@ vec2 readBufferTex(int ptr) { } void init() { - int base = (PARTICLE_STRIDE * (gl_VertexID / 6)) + u_DataOffset; + int base = PARTICLE_STRIDE * (gl_VertexID / 6); position = readBufferPos(base); size = readBufferF(base + 3); @@ -72,7 +72,7 @@ void init() { angle = readBufferF(base + 6); int textureIndex = int(readBuffer(base + 7)); - int texturePtr = textureIndex * TEX_STRIDE; + int texturePtr = (textureIndex * TEX_STRIDE) + u_TextureOffset; minTexUV = readBufferTex(texturePtr); maxTexUV = readBufferTex(texturePtr + 2); } From 8a976a5854759b5a55db0678ff5de5cd62f26b1b Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Sun, 1 Oct 2023 18:25:03 -0500 Subject: [PATCH 18/30] Make uploading more robust, remove glBufferSubData --- .../client/gl/buffer/GlBufferTexture.java | 26 +++---- .../gl/buffer/GlContinuousUploadBuffer.java | 56 +++++++++++++++ .../render/particle/ParticleDataBuffer.java | 9 +-- .../render/particle/ParticleManagerMixin.java | 69 +++++++++++-------- 4 files changed, 108 insertions(+), 52 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlContinuousUploadBuffer.java diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java index 1f60707b1f..4dcbe90cda 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java @@ -1,40 +1,30 @@ package me.jellysquid.mods.sodium.client.gl.buffer; -import com.mojang.blaze3d.platform.GlConst; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; +import me.jellysquid.mods.sodium.client.gl.device.CommandList; import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL31; import java.nio.ByteBuffer; public class GlBufferTexture { - private final int glTexHandle; - - private final int glBufferHandle; + private final GlContinuousUploadBuffer buffer; - private int bufferSize = 0; + private final int glTexHandle; - public GlBufferTexture() { + public GlBufferTexture(CommandList commandList) { + this.buffer = new GlContinuousUploadBuffer(commandList); this.glTexHandle = GlStateManager._genTexture(); - this.glBufferHandle = GlStateManager._glGenBuffers(); } - public void putData(ByteBuffer data, int size) { - GL31.glBindBuffer(GL31.GL_TEXTURE_BUFFER, this.glBufferHandle); - - if (size > this.bufferSize) { - RenderSystem.glBufferData(GL31.GL_TEXTURE_BUFFER, data, GlConst.GL_DYNAMIC_DRAW); - this.bufferSize = size; - } else { - GL15.glBufferSubData(GL31.GL_TEXTURE_BUFFER, 0, data); - } + public void putData(CommandList commandList, ByteBuffer data, int size) { + this.buffer.uploadOverwrite(commandList, data, size); } public void bind() { GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, this.glTexHandle); - GL31.glTexBuffer(GL31.GL_TEXTURE_BUFFER, GL31.GL_R32UI, this.glBufferHandle); + GL31.glTexBuffer(GL31.GL_TEXTURE_BUFFER, GL31.GL_R32UI, this.buffer.getObjectHandle()); RenderSystem.setShaderTexture(3, this.glTexHandle); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlContinuousUploadBuffer.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlContinuousUploadBuffer.java new file mode 100644 index 0000000000..694c927abc --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlContinuousUploadBuffer.java @@ -0,0 +1,56 @@ +package me.jellysquid.mods.sodium.client.gl.buffer; + +import me.jellysquid.mods.sodium.client.SodiumClientMod; +import me.jellysquid.mods.sodium.client.gl.arena.staging.FallbackStagingBuffer; +import me.jellysquid.mods.sodium.client.gl.arena.staging.MappedStagingBuffer; +import me.jellysquid.mods.sodium.client.gl.arena.staging.StagingBuffer; +import me.jellysquid.mods.sodium.client.gl.device.CommandList; +import me.jellysquid.mods.sodium.client.gl.device.RenderDevice; + +import java.nio.ByteBuffer; + +public class GlContinuousUploadBuffer { + private static final GlBufferUsage BUFFER_USAGE = GlBufferUsage.STATIC_DRAW; + private static final int DEFAULT_INITIAL_CAPACITY = 1024; + + private final StagingBuffer stagingBuffer; + private final GlMutableBuffer uploadedBuffer; + + private int capacity; + + public GlContinuousUploadBuffer(CommandList commands, int initialCapacity) { + this.capacity = initialCapacity; + this.stagingBuffer = createStagingBuffer(commands); + this.uploadedBuffer = commands.createMutableBuffer(); + commands.allocateStorage(uploadedBuffer, this.capacity, BUFFER_USAGE); + } + + public GlContinuousUploadBuffer(CommandList commands) { + this(commands, DEFAULT_INITIAL_CAPACITY); + } + + public void uploadOverwrite(CommandList commandList, ByteBuffer data, int size) { + ensureCapacity(commandList, size); + this.stagingBuffer.enqueueCopy(commandList, data, this.uploadedBuffer, 0); + this.stagingBuffer.flush(commandList); + } + + public void ensureCapacity(CommandList commandList, int capacity) { + if (capacity > this.capacity) { + this.capacity = capacity; + commandList.allocateStorage(this.uploadedBuffer, capacity, BUFFER_USAGE); + } + } + + public int getObjectHandle() { + return this.uploadedBuffer.handle(); + } + + private static StagingBuffer createStagingBuffer(CommandList commandList) { + if (SodiumClientMod.options().advanced.useAdvancedStagingBuffers && MappedStagingBuffer.isSupported(RenderDevice.INSTANCE)) { + return new MappedStagingBuffer(commandList); + } + + return new FallbackStagingBuffer(commandList); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java index 3118a36b7f..bb5b65211a 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java @@ -1,17 +1,18 @@ package me.jellysquid.mods.sodium.client.render.particle; import me.jellysquid.mods.sodium.client.gl.buffer.GlBufferTexture; +import me.jellysquid.mods.sodium.client.gl.device.CommandList; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; public class ParticleDataBuffer { private final GlBufferTexture bufferTexture; - public ParticleDataBuffer() { - this.bufferTexture = new GlBufferTexture(); + public ParticleDataBuffer(CommandList commandList) { + this.bufferTexture = new GlBufferTexture(commandList); } - public void uploadParticleData(UnmanagedBufferBuilder.Built data) { - this.bufferTexture.putData(data.buffer, data.size); + public void uploadParticleData(CommandList commandList, UnmanagedBufferBuilder.Built data) { + this.bufferTexture.putData(commandList, data.buffer, data.size); } public void bind() { diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index fd59045b8d..ee2c5eb0b8 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -8,6 +8,8 @@ import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeFormat; +import me.jellysquid.mods.sodium.client.gl.device.CommandList; +import me.jellysquid.mods.sodium.client.gl.device.RenderDevice; import me.jellysquid.mods.sodium.client.render.particle.*; import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleData; @@ -90,7 +92,7 @@ public abstract class ParticleManagerMixin { private UnmanagedBufferBuilder dataBufferBuilder; @Unique - private ParticleDataBuffer dataBuffer; + private ParticleDataBuffer dataBuffer = null; @Unique private Identifier prevTexture = null; @@ -99,7 +101,6 @@ public abstract class ParticleManagerMixin { private void postInit(ClientWorld world, TextureManager textureManager, CallbackInfo ci) { this.glVertexArray = GlStateManager._glGenVertexArrays(); this.dataBufferBuilder = new UnmanagedBufferBuilder(1); - this.dataBuffer = new ParticleDataBuffer(); this.renderView = new ParticleRenderView(world); } @@ -196,33 +197,41 @@ public void renderParticles( LightmapTextureManager lightmapTextureManager, Camera camera, float tickDelta, CallbackInfo ci, MatrixStack matrixStack ) { - particleRenderer.begin(); - ParticleShaderInterface shader = this.particleRenderer.getActiveProgram().getInterface(); - shader.setProjectionMatrix(RenderSystem.getProjectionMatrix()); - shader.setModelViewMatrix(RenderSystem.getModelViewMatrix()); - - for (ParticleTextureSheet particleTextureSheet : PARTICLE_TEXTURE_SHEETS) { - Queue iterable = this.billboardParticles.get(particleTextureSheet); - if (iterable != null && !iterable.isEmpty()) { - int numParticles = iterable.size(); - bindParticleTextureSheet(particleTextureSheet); - this.dataBuffer.bind(); - particleRenderer.setupState(); - - for (BillboardParticle particle : iterable) { - ((BillboardExtended) particle).sodium$buildParticleData( - dataBufferBuilder, - particleTexCache, - camera, tickDelta - ); - } + RenderDevice.enterManagedCode(); + try (CommandList commands = RenderDevice.INSTANCE.createCommandList()) { + if (this.dataBuffer == null) { + this.dataBuffer = new ParticleDataBuffer(commands); + } - drawParticleTextureSheet(particleTextureSheet, numParticles); + particleRenderer.begin(); + ParticleShaderInterface shader = this.particleRenderer.getActiveProgram().getInterface(); + shader.setProjectionMatrix(RenderSystem.getProjectionMatrix()); + shader.setModelViewMatrix(RenderSystem.getModelViewMatrix()); + + for (ParticleTextureSheet particleTextureSheet : PARTICLE_TEXTURE_SHEETS) { + Queue iterable = this.billboardParticles.get(particleTextureSheet); + if (iterable != null && !iterable.isEmpty()) { + int numParticles = iterable.size(); + bindParticleTextureSheet(particleTextureSheet); + this.dataBuffer.bind(); + particleRenderer.setupState(); + + for (BillboardParticle particle : iterable) { + ((BillboardExtended) particle).sodium$buildParticleData( + dataBufferBuilder, + particleTexCache, + camera, tickDelta + ); + } + + drawParticleTextureSheet(commands, particleTextureSheet, numParticles); + } } + } finally { + prevTexture = null; + particleRenderer.end(); + RenderDevice.exitManagedCode(); } - - prevTexture = null; - particleRenderer.end(); } @Unique @@ -252,9 +261,9 @@ private void bindParticleTextureSheet(ParticleTextureSheet sheet) { } @Unique - private void drawParticleTextureSheet(ParticleTextureSheet sheet, int numParticles) { + private void drawParticleTextureSheet(CommandList commands, ParticleTextureSheet sheet, int numParticles) { if (sheet == ParticleTextureSheet.TERRAIN_SHEET || sheet == ParticleTextureSheet.PARTICLE_SHEET_LIT || sheet == ParticleTextureSheet.PARTICLE_SHEET_OPAQUE || sheet == ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT) { - uploadParticleBuffer(numParticles); + uploadParticleBuffer(commands, numParticles); bindDummyVao(); GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, numParticles * 6); } @@ -268,7 +277,7 @@ private void bindDummyVao() { } @Unique - private void uploadParticleBuffer(int numParticles) { + private void uploadParticleBuffer(CommandList commands, int numParticles) { RawUVs[] toUpload = this.particleTexCache.update(); int maxUploadIndex = this.particleTexCache.getTopIndex(); @@ -294,7 +303,7 @@ private void uploadParticleBuffer(int numParticles) { .setTextureOffset((numParticles * BillboardParticleData.STRIDE) / 4); UnmanagedBufferBuilder.Built data = dataBufferBuilder.end(); - this.dataBuffer.uploadParticleData(data); + this.dataBuffer.uploadParticleData(commands, data); } @Inject(method = "setWorld", at = @At("RETURN")) From d5d7ba71cc04c4df3158d57f35402fed4da14dbc Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Sun, 1 Oct 2023 19:26:03 -0500 Subject: [PATCH 19/30] Re-add optimized CPU path for particles that call super --- .../particle/BillboardParticleMixin.java | 100 +++++++++++++++++- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java index 4bad4ec272..50650bf110 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java @@ -6,6 +6,8 @@ import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.caffeinemc.mods.sodium.api.util.ColorABGR; import net.caffeinemc.mods.sodium.api.util.RawUVs; +import net.caffeinemc.mods.sodium.api.vertex.buffer.VertexBufferWriter; +import net.caffeinemc.mods.sodium.api.vertex.format.common.ParticleVertex; import net.minecraft.client.particle.BillboardParticle; import net.minecraft.client.particle.Particle; import net.minecraft.client.render.Camera; @@ -13,10 +15,12 @@ import net.minecraft.client.world.ClientWorld; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; +import org.joml.Quaternionf; import org.lwjgl.system.MemoryStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; @Mixin(BillboardParticle.class) public abstract class BillboardParticleMixin extends Particle implements BillboardExtended { @@ -68,9 +72,99 @@ protected BillboardParticleMixin(ClientWorld world, double x, double y, double z } /** - * @reason Remove function - * @author BeljihnWahfl + * @reason Optimize function + * @author JellySquid */ @Overwrite - public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float tickDelta) {} + public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float tickDelta) { + Vec3d vec3d = camera.getPos(); + + float x = (float) (MathHelper.lerp(tickDelta, this.prevPosX, this.x) - vec3d.getX()); + float y = (float) (MathHelper.lerp(tickDelta, this.prevPosY, this.y) - vec3d.getY()); + float z = (float) (MathHelper.lerp(tickDelta, this.prevPosZ, this.z) - vec3d.getZ()); + + Quaternionf quaternion; + + if (this.angle == 0.0F) { + quaternion = camera.getRotation(); + } else { + float angle = MathHelper.lerp(tickDelta, this.prevAngle, this.angle); + + quaternion = new Quaternionf(camera.getRotation()); + quaternion.rotateZ(angle); + } + + float size = this.getSize(tickDelta); + int light = this.getBrightness(tickDelta); + + float minU = this.getMinU(); + float maxU = this.getMaxU(); + float minV = this.getMinV(); + float maxV = this.getMaxV(); + + int color = ColorABGR.pack(this.red , this.green, this.blue, this.alpha); + + var writer = VertexBufferWriter.of(vertexConsumer); + + try (MemoryStack stack = MemoryStack.stackPush()) { + long buffer = stack.nmalloc(4 * ParticleVertex.STRIDE); + long ptr = buffer; + + writeVertex(ptr, quaternion,-1.0F, -1.0F, x, y, z, maxU, maxV, color, light, size); + ptr += ParticleVertex.STRIDE; + + writeVertex(ptr, quaternion,-1.0F, 1.0F, x, y, z, maxU, minV, color, light, size); + ptr += ParticleVertex.STRIDE; + + writeVertex(ptr, quaternion,1.0F, 1.0F, x, y, z, minU, minV, color, light, size); + ptr += ParticleVertex.STRIDE; + + writeVertex(ptr, quaternion,1.0F, -1.0F, x, y, z, minU, maxV, color, light, size); + ptr += ParticleVertex.STRIDE; + + writer.push(stack, buffer, 4, ParticleVertex.FORMAT); + } + + } + + @Unique + @SuppressWarnings("UnnecessaryLocalVariable") + private static void writeVertex(long buffer, + Quaternionf rotation, + float posX, float posY, + float originX, float originY, float originZ, + float u, float v, int color, int light, float size) { + // Quaternion q0 = new Quaternion(rotation); + float q0x = rotation.x(); + float q0y = rotation.y(); + float q0z = rotation.z(); + float q0w = rotation.w(); + + // q0.hamiltonProduct(x, y, 0.0f, 0.0f) + float q1x = (q0w * posX) - (q0z * posY); + float q1y = (q0w * posY) + (q0z * posX); + float q1w = (q0x * posY) - (q0y * posX); + float q1z = -(q0x * posX) - (q0y * posY); + + // Quaternion q2 = new Quaternion(rotation); + // q2.conjugate() + float q2x = -q0x; + float q2y = -q0y; + float q2z = -q0z; + float q2w = q0w; + + // q2.hamiltonProduct(q1) + float q3x = q1z * q2x + q1x * q2w + q1y * q2z - q1w * q2y; + float q3y = q1z * q2y - q1x * q2z + q1y * q2w + q1w * q2x; + float q3z = q1z * q2z + q1x * q2y - q1y * q2x + q1w * q2w; + + // Vector3f f = new Vector3f(q2.getX(), q2.getY(), q2.getZ()) + // f.multiply(size) + // f.add(pos) + float fx = (q3x * size) + originX; + float fy = (q3y * size) + originY; + float fz = (q3z * size) + originZ; + + ParticleVertex.put(buffer, fx, fy, fz, u, v, color, light); + } } From b7c3c9f856104756c15bb3ebe51643d80461cdef Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Sun, 1 Oct 2023 19:43:47 -0500 Subject: [PATCH 20/30] Don't load rdoc --- .../java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java index 014b905e8c..839105a391 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java @@ -11,6 +11,5 @@ public void onPreLaunch() { GraphicsAdapterProbe.findAdapters(); PreLaunchChecks.checkDrivers(); Workarounds.init(); - System.loadLibrary("renderdoc"); } } From c3042658fa8d8a93734606952b00f1b3331c423f Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Sun, 1 Oct 2023 20:24:42 -0500 Subject: [PATCH 21/30] Micro-optimize uvs equals --- .../java/net/caffeinemc/mods/sodium/api/util/RawUVs.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java b/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java index 0c9c33ed8b..eabcebf69a 100644 --- a/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java +++ b/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java @@ -23,10 +23,10 @@ public static void putNull(long ptr) { public boolean equals(Object o) { if (this == o) return true; if (o instanceof RawUVs other) { - return minU == other.minU() && - minV == other.minV() && - maxU == other.maxU() && - maxV == other.maxV(); + return Float.floatToRawIntBits(minU) == Float.floatToRawIntBits(other.minU()) && + Float.floatToRawIntBits(minV) == Float.floatToRawIntBits(other.minV()) && + Float.floatToRawIntBits(maxU) == Float.floatToRawIntBits(other.maxU()) && + Float.floatToRawIntBits(maxV) == Float.floatToRawIntBits(other.maxV()); } else { return false; } From 53d97b7309d234887d742a0c9c5a4a9e2daffb17 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:12:44 -0600 Subject: [PATCH 22/30] Add special-cases for specific particles --- .../render/particle/ParticleManagerMixin.java | 25 +++++++++++++----- .../DustColorTransitionParticleMixin.java | 26 +++++++++++++++++++ .../FireworksSparkParticleMixin.java | 26 +++++++++++++++++++ .../specialcases/FlashParticleMixin.java | 22 ++++++++++++++++ src/main/resources/sodium.accesswidener | 1 + 5 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/DustColorTransitionParticleMixin.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java create mode 100644 src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FlashParticleMixin.java diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index ee2c5eb0b8..14b36eb162 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -36,13 +36,22 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Queue; +import java.util.*; @Mixin(ParticleManager.class) public abstract class ParticleManagerMixin { + /** + * The set of special cases that can use the fast path, but override buildGeometry. + * These classes should have a mixin where {@link BillboardExtended#sodium$buildParticleData} + * is overridden to produce the correct behavior. See the specialcases package for examples. + */ + @Unique + private static final Set> SPECIAL_CASES = Set.of( + DustColorTransitionParticle.class, + FireworksSparkParticle.Explosion.class, + FireworksSparkParticle.Flash.class + ); + @Shadow protected ClientWorld world; @@ -161,14 +170,16 @@ public void tick() { @Unique private boolean testClassOverrides(Class particleClass) { try { - return particleClass.getDeclaredMethod( + Class c = particleClass.getDeclaredMethod( BUILD_GEOMETRY_METHOD, VertexConsumer.class, Camera.class, float.class - ).getDeclaringClass() != BillboardParticle.class; + ).getDeclaringClass(); + + return !(c == BillboardParticle.class || SPECIAL_CASES.contains(c)); } catch (NoSuchMethodException e) { - return false; + return true; } } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/DustColorTransitionParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/DustColorTransitionParticleMixin.java new file mode 100644 index 0000000000..c0ee41ef29 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/DustColorTransitionParticleMixin.java @@ -0,0 +1,26 @@ +package me.jellysquid.mods.sodium.mixin.features.render.particle.specialcases; + +import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; +import me.jellysquid.mods.sodium.mixin.features.render.particle.BillboardParticleMixin; +import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; +import net.minecraft.client.particle.DustColorTransitionParticle; +import net.minecraft.client.render.Camera; +import net.minecraft.client.world.ClientWorld; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(DustColorTransitionParticle.class) +public abstract class DustColorTransitionParticleMixin extends BillboardParticleMixin { + @Shadow + protected abstract void updateColor(float tickDelta); + + protected DustColorTransitionParticleMixin(ClientWorld world, double x, double y, double z) { + super(world, x, y, z); + } + + @Override + public void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { + this.updateColor(tickDelta); + super.sodium$buildParticleData(builder, registry, camera, tickDelta); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java new file mode 100644 index 0000000000..f27c3f11a4 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java @@ -0,0 +1,26 @@ +package me.jellysquid.mods.sodium.mixin.features.render.particle.specialcases; + +import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; +import me.jellysquid.mods.sodium.mixin.features.render.particle.BillboardParticleMixin; +import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; +import net.minecraft.client.render.Camera; +import net.minecraft.client.world.ClientWorld; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(targets = "net.minecraft.client.particle.FireworksSparkParticle$Explosion") +public abstract class FireworksSparkParticleMixin extends BillboardParticleMixin { + @Shadow + private boolean flicker; + + protected FireworksSparkParticleMixin(ClientWorld world, double x, double y, double z) { + super(world, x, y, z); + } + + @Override + public void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { + if (!this.flicker || this.age < this.maxAge / 3 || (this.age + this.maxAge) / 3 % 2 == 0) { + super.sodium$buildParticleData(builder, registry, camera, tickDelta); + } + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FlashParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FlashParticleMixin.java new file mode 100644 index 0000000000..7dc1d1b571 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FlashParticleMixin.java @@ -0,0 +1,22 @@ +package me.jellysquid.mods.sodium.mixin.features.render.particle.specialcases; + +import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; +import me.jellysquid.mods.sodium.mixin.features.render.particle.BillboardParticleMixin; +import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; +import net.minecraft.client.particle.FireworksSparkParticle; +import net.minecraft.client.render.Camera; +import net.minecraft.client.world.ClientWorld; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(FireworksSparkParticle.Flash.class) +public abstract class FlashParticleMixin extends BillboardParticleMixin { + protected FlashParticleMixin(ClientWorld world, double x, double y, double z) { + super(world, x, y, z); + } + + @Override + public void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { + this.setAlpha(0.6F - ((float)this.age + tickDelta - 1.0F) * 0.25F * 0.5F); + super.sodium$buildParticleData(builder, registry, camera, tickDelta); + } +} diff --git a/src/main/resources/sodium.accesswidener b/src/main/resources/sodium.accesswidener index 16df0192bf..e9d10c6037 100644 --- a/src/main/resources/sodium.accesswidener +++ b/src/main/resources/sodium.accesswidener @@ -12,5 +12,6 @@ accessible class net/minecraft/client/render/BackgroundRenderer$FogData accessible class net/minecraft/client/render/BackgroundRenderer$StatusEffectFogModifier accessible class net/minecraft/client/texture/TextureStitcher$Holder accessible class net/minecraft/world/biome/Biome$Weather +accessible class net/minecraft/client/particle/FireworksSparkParticle$Explosion accessible method net/minecraft/client/util/math/MatrixStack$Entry (Lorg/joml/Matrix4f;Lorg/joml/Matrix3f;)V From b8ebf4bcb3f23aad3cf65446f36e12e87f91a70e Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Mon, 6 Nov 2023 16:12:55 -0600 Subject: [PATCH 23/30] Fix override detection --- .../render/particle/ParticleManagerMixin.java | 11 ++++++----- .../FireworksSparkParticleMixin.java | 3 ++- .../tracking/SpriteBillboardParticleMixin.java | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index 14b36eb162..751422a88c 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -46,7 +46,10 @@ public abstract class ParticleManagerMixin { * is overridden to produce the correct behavior. See the specialcases package for examples. */ @Unique - private static final Set> SPECIAL_CASES = Set.of( + private static final Set> FAST_PATH_PARTICLES = Set.of( + BillboardParticle.class, + SpriteBillboardParticle.class, + DustColorTransitionParticle.class, FireworksSparkParticle.Explosion.class, FireworksSparkParticle.Flash.class @@ -170,14 +173,12 @@ public void tick() { @Unique private boolean testClassOverrides(Class particleClass) { try { - Class c = particleClass.getDeclaredMethod( + return !FAST_PATH_PARTICLES.contains(particleClass.getMethod( BUILD_GEOMETRY_METHOD, VertexConsumer.class, Camera.class, float.class - ).getDeclaringClass(); - - return !(c == BillboardParticle.class || SPECIAL_CASES.contains(c)); + ).getDeclaringClass()); } catch (NoSuchMethodException e) { return true; } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java index f27c3f11a4..84af69e7ef 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java @@ -3,12 +3,13 @@ import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; import me.jellysquid.mods.sodium.mixin.features.render.particle.BillboardParticleMixin; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; +import net.minecraft.client.particle.FireworksSparkParticle; import net.minecraft.client.render.Camera; import net.minecraft.client.world.ClientWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -@Mixin(targets = "net.minecraft.client.particle.FireworksSparkParticle$Explosion") +@Mixin(FireworksSparkParticle.Explosion.class) public abstract class FireworksSparkParticleMixin extends BillboardParticleMixin { @Shadow private boolean flicker; diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/textures/animations/tracking/SpriteBillboardParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/textures/animations/tracking/SpriteBillboardParticleMixin.java index 3c0c1ffa21..880ecae92d 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/textures/animations/tracking/SpriteBillboardParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/textures/animations/tracking/SpriteBillboardParticleMixin.java @@ -1,7 +1,10 @@ package me.jellysquid.mods.sodium.mixin.features.textures.animations.tracking; +import me.jellysquid.mods.sodium.client.render.particle.BillboardExtended; +import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; import me.jellysquid.mods.sodium.client.render.texture.SpriteUtil; -import net.minecraft.client.particle.BillboardParticle; +import me.jellysquid.mods.sodium.mixin.features.render.particle.BillboardParticleMixin; +import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.minecraft.client.particle.SpriteBillboardParticle; import net.minecraft.client.render.Camera; import net.minecraft.client.render.VertexConsumer; @@ -15,7 +18,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(SpriteBillboardParticle.class) -public abstract class SpriteBillboardParticleMixin extends BillboardParticle { +public abstract class SpriteBillboardParticleMixin extends BillboardParticleMixin implements BillboardExtended { @Shadow protected Sprite sprite; @@ -39,4 +42,13 @@ public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float ti super.buildGeometry(vertexConsumer, camera, tickDelta); } + + @Override + public void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { + if (this.shouldTickSprite) { + SpriteUtil.markSpriteActive(this.sprite); + } + + super.sodium$buildParticleData(builder, registry, camera, tickDelta); + } } \ No newline at end of file From 6468b89def55792efe39f2f0cbc59d8fe2853ae0 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Fri, 24 Nov 2023 17:09:10 -0600 Subject: [PATCH 24/30] Move texture cache to its own buffer --- .../api/buffer/UnmanagedBufferBuilder.java | 2 +- .../client/gl/buffer/GlBufferTexture.java | 11 ++++-- .../render/particle/ParticleBuffers.java | 25 +++++++++++++ .../render/particle/ParticleDataBuffer.java | 21 ----------- .../shader/ParticleShaderInterface.java | 29 ++++++++------- .../render/particle/ParticleManagerMixin.java | 35 ++++++++++--------- .../sodium/shaders/particles/particle.vsh | 15 +++++--- 7 files changed, 81 insertions(+), 57 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java delete mode 100644 src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java diff --git a/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java b/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java index 3678d9a590..0e8e2d52ef 100644 --- a/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java +++ b/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java @@ -49,7 +49,7 @@ public void reset() { /** * Builds and resets this builder. * Make sure to use/upload the return value before pushing more data. - * @return a ByteBuffer containing all the data pushed to this builder + * @return a built buffer containing all the data pushed to this builder */ public Built end() { int endOffset = this.byteOffset; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java index 4dcbe90cda..ef4f706053 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java @@ -13,9 +13,16 @@ public class GlBufferTexture { private final int glTexHandle; - public GlBufferTexture(CommandList commandList) { + private final int textureNum; + + public GlBufferTexture(CommandList commandList, int textureNum) { this.buffer = new GlContinuousUploadBuffer(commandList); this.glTexHandle = GlStateManager._genTexture(); + this.textureNum = textureNum; + } + + public int getTextureNum() { + return textureNum; } public void putData(CommandList commandList, ByteBuffer data, int size) { @@ -25,6 +32,6 @@ public void putData(CommandList commandList, ByteBuffer data, int size) { public void bind() { GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, this.glTexHandle); GL31.glTexBuffer(GL31.GL_TEXTURE_BUFFER, GL31.GL_R32UI, this.buffer.getObjectHandle()); - RenderSystem.setShaderTexture(3, this.glTexHandle); + RenderSystem.setShaderTexture(this.textureNum, this.glTexHandle); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java new file mode 100644 index 0000000000..08a359780c --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java @@ -0,0 +1,25 @@ +package me.jellysquid.mods.sodium.client.render.particle; + +import me.jellysquid.mods.sodium.client.gl.buffer.GlBufferTexture; +import me.jellysquid.mods.sodium.client.gl.device.CommandList; +import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; + +public class ParticleBuffers { + private final GlBufferTexture particleData; + private final GlBufferTexture textureCache; + + public ParticleBuffers(CommandList commandList) { + this.particleData = new GlBufferTexture(commandList, 3); + this.textureCache = new GlBufferTexture(commandList, 4); + } + + public void uploadParticleData(CommandList commandList, UnmanagedBufferBuilder.Built data, UnmanagedBufferBuilder.Built cache) { + this.particleData.putData(commandList, data.buffer, data.size); + this.textureCache.putData(commandList, cache.buffer, cache.size); + } + + public void bind() { + this.particleData.bind(); + this.textureCache.bind(); + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java deleted file mode 100644 index bb5b65211a..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleDataBuffer.java +++ /dev/null @@ -1,21 +0,0 @@ -package me.jellysquid.mods.sodium.client.render.particle; - -import me.jellysquid.mods.sodium.client.gl.buffer.GlBufferTexture; -import me.jellysquid.mods.sodium.client.gl.device.CommandList; -import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; - -public class ParticleDataBuffer { - private final GlBufferTexture bufferTexture; - - public ParticleDataBuffer(CommandList commandList) { - this.bufferTexture = new GlBufferTexture(commandList); - } - - public void uploadParticleData(CommandList commandList, UnmanagedBufferBuilder.Built data) { - this.bufferTexture.putData(commandList, data.buffer, data.size); - } - - public void bind() { - this.bufferTexture.bind(); - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java index 4e58807872..d2c8cd9747 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/shader/ParticleShaderInterface.java @@ -12,20 +12,20 @@ import org.lwjgl.opengl.GL32C; public class ParticleShaderInterface { - private final GlUniformInt uniformTextureOffset; private final GlUniformInt uniformParticleTexture; private final GlUniformInt uniformLightTexture; private final GlUniformMatrix4f uniformModelViewMatrix; private final GlUniformMatrix4f uniformProjectionMatrix; - private final GlUniformInt uniformBufferTexture; + private final GlUniformInt uniformParticleData; + private final GlUniformInt uniformTextureCache; public ParticleShaderInterface(ShaderBindingContext context) { - this.uniformTextureOffset = context.bindUniform("u_TextureOffset", GlUniformInt::new); this.uniformParticleTexture = context.bindUniform("u_ParticleTex", GlUniformInt::new); this.uniformLightTexture = context.bindUniform("u_LightTex", GlUniformInt::new); this.uniformModelViewMatrix = context.bindUniform("u_ModelViewMatrix", GlUniformMatrix4f::new); this.uniformProjectionMatrix = context.bindUniform("u_ProjectionMatrix", GlUniformMatrix4f::new); - this.uniformBufferTexture = context.bindUniform("u_BufferTexture", GlUniformInt::new); + this.uniformParticleData = context.bindUniform("u_ParticleData", GlUniformInt::new); + this.uniformTextureCache = context.bindUniform("u_TextureCache", GlUniformInt::new); } public void setProjectionMatrix(Matrix4fc matrix) { @@ -36,15 +36,12 @@ public void setModelViewMatrix(Matrix4fc matrix) { this.uniformModelViewMatrix.set(matrix); } - public void setTextureOffset(int dataOffset) { - this.uniformTextureOffset.setInt(dataOffset); - } - public void setupState() { // "BlockTexture" should represent the particle textures if bound correctly this.bindParticleTexture(ParticleShaderTextureSlot.TEXTURE, TextureUtil.getBlockTextureId()); this.bindLightTexture(ParticleShaderTextureSlot.LIGHT, TextureUtil.getLightTextureId()); - this.bindBufferTexture(ParticleShaderTextureSlot.STORAGE, RenderSystem.getShaderTexture(3)); + this.bindParticleData(ParticleShaderTextureSlot.PARTICLE_DATA, RenderSystem.getShaderTexture(3)); + this.bindTextureCache(ParticleShaderTextureSlot.TEXTURE_CACHE, RenderSystem.getShaderTexture(4)); } private void bindParticleTexture(ParticleShaderTextureSlot slot, int textureId) { @@ -61,16 +58,24 @@ private void bindLightTexture(ParticleShaderTextureSlot slot, int textureId) { uniformLightTexture.setInt(slot.ordinal()); } - private void bindBufferTexture(ParticleShaderTextureSlot slot, int textureId) { + private void bindParticleData(ParticleShaderTextureSlot slot, int textureId) { + GlStateManager._activeTexture(GL32C.GL_TEXTURE0 + slot.ordinal()); + GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, textureId); + + uniformParticleData.setInt(slot.ordinal()); + } + + private void bindTextureCache(ParticleShaderTextureSlot slot, int textureId) { GlStateManager._activeTexture(GL32C.GL_TEXTURE0 + slot.ordinal()); GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, textureId); - uniformBufferTexture.setInt(slot.ordinal()); + uniformTextureCache.setInt(slot.ordinal()); } private enum ParticleShaderTextureSlot { TEXTURE, LIGHT, - STORAGE, + PARTICLE_DATA, + TEXTURE_CACHE, } } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index 751422a88c..4d8e3d9c80 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -12,7 +12,6 @@ import me.jellysquid.mods.sodium.client.gl.device.RenderDevice; import me.jellysquid.mods.sodium.client.render.particle.*; import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; -import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleData; import me.jellysquid.mods.sodium.client.render.particle.shader.ParticleShaderInterface; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.caffeinemc.mods.sodium.api.util.RawUVs; @@ -41,9 +40,11 @@ @Mixin(ParticleManager.class) public abstract class ParticleManagerMixin { /** - * The set of special cases that can use the fast path, but override buildGeometry. - * These classes should have a mixin where {@link BillboardExtended#sodium$buildParticleData} - * is overridden to produce the correct behavior. See the specialcases package for examples. + * The set of particle classes that can use the GPU fast path.
+ * If the class overrides the build geometry method, + * it should have a mixin where {@link BillboardExtended#sodium$buildParticleData} + * is overridden to produce the correct behavior. + * See the specialcases package for examples. */ @Unique private static final Set> FAST_PATH_PARTICLES = Set.of( @@ -104,7 +105,10 @@ public abstract class ParticleManagerMixin { private UnmanagedBufferBuilder dataBufferBuilder; @Unique - private ParticleDataBuffer dataBuffer = null; + private UnmanagedBufferBuilder cacheBufferBuilder; + + @Unique + private ParticleBuffers buffers = null; @Unique private Identifier prevTexture = null; @@ -113,6 +117,7 @@ public abstract class ParticleManagerMixin { private void postInit(ClientWorld world, TextureManager textureManager, CallbackInfo ci) { this.glVertexArray = GlStateManager._glGenVertexArrays(); this.dataBufferBuilder = new UnmanagedBufferBuilder(1); + this.cacheBufferBuilder = new UnmanagedBufferBuilder(1); this.renderView = new ParticleRenderView(world); } @@ -211,8 +216,8 @@ public void renderParticles( ) { RenderDevice.enterManagedCode(); try (CommandList commands = RenderDevice.INSTANCE.createCommandList()) { - if (this.dataBuffer == null) { - this.dataBuffer = new ParticleDataBuffer(commands); + if (this.buffers == null) { + this.buffers = new ParticleBuffers(commands); } particleRenderer.begin(); @@ -225,7 +230,7 @@ public void renderParticles( if (iterable != null && !iterable.isEmpty()) { int numParticles = iterable.size(); bindParticleTextureSheet(particleTextureSheet); - this.dataBuffer.bind(); + this.buffers.bind(); particleRenderer.setupState(); for (BillboardParticle particle : iterable) { @@ -275,7 +280,7 @@ private void bindParticleTextureSheet(ParticleTextureSheet sheet) { @Unique private void drawParticleTextureSheet(CommandList commands, ParticleTextureSheet sheet, int numParticles) { if (sheet == ParticleTextureSheet.TERRAIN_SHEET || sheet == ParticleTextureSheet.PARTICLE_SHEET_LIT || sheet == ParticleTextureSheet.PARTICLE_SHEET_OPAQUE || sheet == ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT) { - uploadParticleBuffer(commands, numParticles); + uploadParticleBuffer(commands); bindDummyVao(); GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, numParticles * 6); } @@ -289,7 +294,7 @@ private void bindDummyVao() { } @Unique - private void uploadParticleBuffer(CommandList commands, int numParticles) { + private void uploadParticleBuffer(CommandList commands) { RawUVs[] toUpload = this.particleTexCache.update(); int maxUploadIndex = this.particleTexCache.getTopIndex(); @@ -307,15 +312,13 @@ private void uploadParticleBuffer(CommandList commands, int numParticles) { } ptr += RawUVs.STRIDE; } - dataBufferBuilder.push(stack, buffer, size); + cacheBufferBuilder.push(stack, buffer, size); } - particleRenderer.getActiveProgram() - .getInterface() - .setTextureOffset((numParticles * BillboardParticleData.STRIDE) / 4); - UnmanagedBufferBuilder.Built data = dataBufferBuilder.end(); - this.dataBuffer.uploadParticleData(commands, data); + UnmanagedBufferBuilder.Built cache = cacheBufferBuilder.end(); + + this.buffers.uploadParticleData(commands, data, cache); } @Inject(method = "setWorld", at = @At("RETURN")) diff --git a/src/main/resources/assets/sodium/shaders/particles/particle.vsh b/src/main/resources/assets/sodium/shaders/particles/particle.vsh index ab687c2f88..66a0c743a6 100644 --- a/src/main/resources/assets/sodium/shaders/particles/particle.vsh +++ b/src/main/resources/assets/sodium/shaders/particles/particle.vsh @@ -18,9 +18,9 @@ const int INDICES[] = int[]( 0, 2, 3 ); -uniform int u_TextureOffset; uniform sampler2D u_LightTex; -uniform usamplerBuffer u_BufferTexture; // R_32UI +uniform usamplerBuffer u_ParticleData; // R_32UI +uniform usamplerBuffer u_TextureCache; // R_32UI out vec2 texCoord0; out vec4 vertexColor; @@ -39,7 +39,7 @@ vec2 maxTexUV; // Returns a collection of 4 bytes // ptr is essentially multiplied by 4 since u_BufferTexture is R_32UI uint readBuffer(int ptr) { - return texelFetch(u_BufferTexture, ptr).x; + return texelFetch(u_ParticleData, ptr).x; } float readBufferF(int ptr) { @@ -58,8 +58,13 @@ ivec2 readBufferLight(int ptr) { return ivec2((uvec2(readBuffer(ptr)) >> uvec2(0, 16)) & uvec2(0xFFFFu)); } + +float readFloatTex(int ptr) { + return uintBitsToFloat(texelFetch(u_TextureCache, ptr).x); +} + vec2 readBufferTex(int ptr) { - return vec2(readBufferF(ptr), readBufferF(ptr + 1)); + return vec2(readFloatTex(ptr), readFloatTex(ptr + 1)); } void init() { @@ -72,7 +77,7 @@ void init() { angle = readBufferF(base + 6); int textureIndex = int(readBuffer(base + 7)); - int texturePtr = (textureIndex * TEX_STRIDE) + u_TextureOffset; + int texturePtr = textureIndex * TEX_STRIDE; minTexUV = readBufferTex(texturePtr); maxTexUV = readBufferTex(texturePtr + 2); } From a10b2c7e9cc7bb745e19e0f5ee6032f427910d54 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:45:38 -0600 Subject: [PATCH 25/30] EvictingPool implementation --- .../client/util/collections/EvictingPool.java | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/util/collections/EvictingPool.java diff --git a/src/main/java/me/jellysquid/mods/sodium/client/util/collections/EvictingPool.java b/src/main/java/me/jellysquid/mods/sodium/client/util/collections/EvictingPool.java new file mode 100644 index 0000000000..a1ad7cbb68 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/util/collections/EvictingPool.java @@ -0,0 +1,182 @@ +package me.jellysquid.mods.sodium.client.util.collections; + +import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue; +import it.unimi.dsi.fastutil.ints.IntIntMutablePair; +import org.jetbrains.annotations.NotNull; + +import java.util.AbstractCollection; +import java.util.Arrays; +import java.util.Iterator; + +/** + * Functions similarly to an EvictingQueue, except existing elements + * stay in the same place in memory until they are removed or replaced + * in favor of a new element. + * @param + */ +public class EvictingPool extends AbstractCollection { + E[] pool; + IntIntMutablePair[] links; + int oldestIdx; + int newestIdx; + + IntArrayFIFOQueue free; + int top; + + int size; + + public EvictingPool(int size) { + this.pool = (E[]) new Object[size]; + this.links = new IntIntMutablePair[size]; + Arrays.asList(this.links).replaceAll(i -> new IntIntMutablePair(-1, -1)); + + this.free = new IntArrayFIFOQueue(); + this.top = 0; + this.size = size; + + this.oldestIdx = -1; + this.newestIdx = -1; + } + + public boolean add(E item) { + if (free.isEmpty()) { + if (this.top < size) { + pool[this.top] = item; + this.newest(this.top); + + ++this.top; + } else { + pool[this.oldestIdx] = item; + int nextOldest = links[this.oldestIdx].rightInt(); + links[nextOldest].left(-1); + + this.newest(this.oldestIdx); + this.oldestIdx = nextOldest; + } + } else { + int freeIdx = free.dequeueInt(); + pool[freeIdx] = item; + this.newest(freeIdx); + } + + return true; + } + + public void remove(int index) { + free.enqueue(index); + IntIntMutablePair link = this.links[index]; + + if (index == oldestIdx) { + links[link.leftInt()].right(-1); + this.oldestIdx = link.leftInt(); + } else if (index == newestIdx) { + links[link.rightInt()].left(-1); + this.newestIdx = link.rightInt(); + } else { + links[link.leftInt()].right(link.rightInt()); + links[link.rightInt()].left(link.leftInt()); + } + + pool[index] = null; + } + + @Override + public int size() { + return top - free.size(); + } + + public boolean isEmpty() { + return size() == 0; + } + + public @NotNull Iterator iterator() { + if (this.saturation() < Itr.MAGIC_RATIO) { + return new LinkItr(); + } else { + return new IncrementItr(); + } + } + + private double saturation() { + return (double) top / free.size(); + } + + private void newest(int idx) { + int prevNewest = this.newestIdx; + this.newestIdx = idx; + + links[prevNewest].right(newestIdx); + links[newestIdx] = new IntIntMutablePair(prevNewest, -1); + } + + private abstract class Itr implements Iterator { + static final double MAGIC_RATIO = 0.50; + int cursor = -1; + + @Override + public void remove() { + EvictingPool.this.remove(cursor); + } + } + + private class LinkItr extends Itr { + boolean beganIterating; + IntIntMutablePair currentLink; + + LinkItr() { + this.cursor = -1; + this.currentLink = null; + } + + @Override + public boolean hasNext() { + return this.currentLink.rightInt() != -1; + } + + @Override + public E next() { + if (!beganIterating) { + this.cursor = EvictingPool.this.newestIdx; + beganIterating = true; + } else { + this.cursor = this.currentLink.rightInt(); + } + + this.currentLink = EvictingPool.this.links[this.cursor]; + return EvictingPool.this.pool[this.cursor]; + } + } + + private class IncrementItr extends Itr { + E next = null; + int nextCursor = -1; + + IncrementItr() { + this.cursor = -1; + this.next = this.getNext(); + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public E next() { + E ret = this.next; + this.cursor = this.nextCursor; + this.next = this.getNext(); + return ret; + } + + private E getNext() { + E ret = null; + while (ret == null && this.nextCursor + 1 < EvictingPool.this.top) { + ++this.nextCursor; + ret = EvictingPool.this.pool[this.nextCursor]; + } + + return ret; + } + } +} \ No newline at end of file From 6984e7f11a452c2190dad9a8349d6a126be044f3 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:48:17 -0600 Subject: [PATCH 26/30] Link to reduce confusion --- .../client/util/collections/EvictingPool.java | 65 +++++++++++++------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/src/main/java/me/jellysquid/mods/sodium/client/util/collections/EvictingPool.java b/src/main/java/me/jellysquid/mods/sodium/client/util/collections/EvictingPool.java index a1ad7cbb68..4f60068638 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/util/collections/EvictingPool.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/util/collections/EvictingPool.java @@ -16,7 +16,7 @@ */ public class EvictingPool extends AbstractCollection { E[] pool; - IntIntMutablePair[] links; + Link[] links; int oldestIdx; int newestIdx; @@ -27,8 +27,8 @@ public class EvictingPool extends AbstractCollection { public EvictingPool(int size) { this.pool = (E[]) new Object[size]; - this.links = new IntIntMutablePair[size]; - Arrays.asList(this.links).replaceAll(i -> new IntIntMutablePair(-1, -1)); + this.links = new Link[size]; + Arrays.asList(this.links).replaceAll(i -> new Link(-1, -1)); this.free = new IntArrayFIFOQueue(); this.top = 0; @@ -47,8 +47,8 @@ public boolean add(E item) { ++this.top; } else { pool[this.oldestIdx] = item; - int nextOldest = links[this.oldestIdx].rightInt(); - links[nextOldest].left(-1); + int nextOldest = links[this.oldestIdx].next(); + links[nextOldest].prev(-1); this.newest(this.oldestIdx); this.oldestIdx = nextOldest; @@ -64,17 +64,19 @@ public boolean add(E item) { public void remove(int index) { free.enqueue(index); - IntIntMutablePair link = this.links[index]; + Link link = this.links[index]; + + System.out.println(link); if (index == oldestIdx) { - links[link.leftInt()].right(-1); - this.oldestIdx = link.leftInt(); + links[link.next()].prev(-1); + this.oldestIdx = link.next(); } else if (index == newestIdx) { - links[link.rightInt()].left(-1); - this.newestIdx = link.rightInt(); + links[link.prev()].next(-1); + this.newestIdx = link.prev(); } else { - links[link.leftInt()].right(link.rightInt()); - links[link.rightInt()].left(link.leftInt()); + links[link.prev()].next(link.next()); + links[link.next()].prev(link.prev()); } pool[index] = null; @@ -85,10 +87,6 @@ public int size() { return top - free.size(); } - public boolean isEmpty() { - return size() == 0; - } - public @NotNull Iterator iterator() { if (this.saturation() < Itr.MAGIC_RATIO) { return new LinkItr(); @@ -105,8 +103,33 @@ private void newest(int idx) { int prevNewest = this.newestIdx; this.newestIdx = idx; - links[prevNewest].right(newestIdx); - links[newestIdx] = new IntIntMutablePair(prevNewest, -1); + if (prevNewest >= 0) links[prevNewest].next(newestIdx); + links[newestIdx] = new Link(prevNewest, -1); + } + + /** + * Added to prevent confusion on left/right + */ + private static class Link extends IntIntMutablePair { + public Link(int prev, int next) { + super(prev, next); + } + + public int next() { + return this.right; + } + + public void next(int next) { + this.right = next; + } + + public int prev() { + return this.left; + } + + public void prev(int prev) { + this.left = prev; + } } private abstract class Itr implements Iterator { @@ -121,7 +144,7 @@ public void remove() { private class LinkItr extends Itr { boolean beganIterating; - IntIntMutablePair currentLink; + Link currentLink; LinkItr() { this.cursor = -1; @@ -130,7 +153,7 @@ private class LinkItr extends Itr { @Override public boolean hasNext() { - return this.currentLink.rightInt() != -1; + return this.currentLink.next() != -1; } @Override @@ -139,7 +162,7 @@ public E next() { this.cursor = EvictingPool.this.newestIdx; beganIterating = true; } else { - this.cursor = this.currentLink.rightInt(); + this.cursor = this.currentLink.next(); } this.currentLink = EvictingPool.this.links[this.cursor]; From 931835b0c22317bf479603bd0d2a98b9fa9bfcd2 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Fri, 8 Mar 2024 21:35:43 -0600 Subject: [PATCH 27/30] UV micro-optimizations --- .../api/buffer/UnmanagedBufferBuilder.java | 3 ++- .../mods/sodium/api/util/RawUVs.java | 4 ++++ .../particle/cache/ParticleTextureCache.java | 16 +++++++++++----- .../particle/BillboardParticleMixin.java | 19 ++++++++++++++++++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java b/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java index 0e8e2d52ef..25ac2b8221 100644 --- a/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java +++ b/src/api/java/net/caffeinemc/mods/sodium/api/buffer/UnmanagedBufferBuilder.java @@ -21,7 +21,8 @@ public UnmanagedBufferBuilder(int initialCapacity) { public void ensureCapacity(int capacity) { if (capacity > this.buffer.capacity()) { - ByteBuffer byteBuffer = GlAllocationUtils.resizeByteBuffer(this.buffer, capacity); + int newCapacity = (int) Math.ceil(this.buffer.capacity() * 1.5); + ByteBuffer byteBuffer = GlAllocationUtils.resizeByteBuffer(this.buffer, Math.max(newCapacity, capacity)); byteBuffer.rewind(); this.buffer = byteBuffer; } diff --git a/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java b/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java index eabcebf69a..784ece5152 100644 --- a/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java +++ b/src/api/java/net/caffeinemc/mods/sodium/api/util/RawUVs.java @@ -19,6 +19,10 @@ public static void putNull(long ptr) { MemoryUtil.memPutFloat(ptr + 12, Float.NaN); } + public long key() { + return ((long) Float.floatToRawIntBits(minU)) << 32 | Float.floatToRawIntBits(minV); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/ParticleTextureCache.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/ParticleTextureCache.java index e04e8e5c18..ec38d1e24d 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/ParticleTextureCache.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/cache/ParticleTextureCache.java @@ -2,6 +2,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue; import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.caffeinemc.mods.sodium.api.util.RawUVs; @@ -11,7 +12,7 @@ public class ParticleTextureCache { private static final int DEFAULT_CAPACITY = 64; private RawUVs[] rawUVs; - private final Object2IntOpenHashMap uvToIndex = new Object2IntOpenHashMap<>(); + private final Long2IntOpenHashMap uvToIndex = new Long2IntOpenHashMap(); private final TextureUsageMonitor usageMonitor = new TextureUsageMonitor(); private final IntArrayFIFOQueue freeIndices = new IntArrayFIFOQueue(); @@ -26,12 +27,13 @@ public int getTopIndex() { } public int getUvIndex(RawUVs uvs) { - int use = uvToIndex.computeIfAbsent(uvs, key -> { - RawUVs uvsKey = (RawUVs) key; + long uvKey = uvs.key(); + + int use = uvToIndex.computeIfAbsent(uvKey, key -> { int index = freeIndices.isEmpty() ? topIndex++ : freeIndices.dequeueInt(); ensureCapacity(index); - rawUVs[index] = uvsKey; + rawUVs[index] = uvs; return index; }); @@ -39,6 +41,10 @@ public int getUvIndex(RawUVs uvs) { return use; } + public void markTextureAsUsed(int index) { + usageMonitor.markUsed(index); + } + /** * @return The array of RawUVs that should be uploaded */ @@ -51,7 +57,7 @@ public RawUVs[] update() { rawUVs[index] = null; this.freeIndices.enqueue(index); - uvToIndex.removeInt(uvs); + uvToIndex.remove(uvs.key()); } return this.rawUVs; } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java index 50650bf110..8d3cca9dc3 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java @@ -24,6 +24,12 @@ @Mixin(BillboardParticle.class) public abstract class BillboardParticleMixin extends Particle implements BillboardExtended { + @Unique + private long prevUvKey = 0x7fc000007fc00000L; + + @Unique + private int prevTextureIndex = -1; + @Shadow public abstract float getSize(float tickDelta); @@ -51,7 +57,18 @@ protected BillboardParticleMixin(ClientWorld world, double x, double y, double z ) { Vec3d vec3d = camera.getPos(); RawUVs uvs = new RawUVs(getMinU(), getMinV(), getMaxU(), getMaxV()); - int textureIndex = registry.getUvIndex(uvs); + + int textureIndex; + long uvKey = uvs.key(); + + if (prevTextureIndex == -1 || uvKey != prevUvKey) { + textureIndex = registry.getUvIndex(uvs); + prevTextureIndex = textureIndex; + prevUvKey = uvKey; + } else { + textureIndex = prevTextureIndex; + registry.markTextureAsUsed(textureIndex); + } float x = (float) (MathHelper.lerp(tickDelta, this.prevPosX, this.x) - vec3d.getX()); float y = (float) (MathHelper.lerp(tickDelta, this.prevPosY, this.y) - vec3d.getY()); From e0a853a9ef7e29825b5f8d495118b7052a9dfa34 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:23:19 -0500 Subject: [PATCH 28/30] Initial direct staging buffer impl --- .../mods/sodium/client/SodiumPreLaunch.java | 1 + .../arena/staging/StagingBufferBuilder.java | 63 +++++++++++++++++++ .../client/gl/buffer/GlBufferMapping.java | 5 ++ .../client/gl/buffer/GlBufferTexture.java | 12 ++-- .../gl/buffer/GlContinuousUploadBuffer.java | 15 ++--- .../render/particle/BillboardExtended.java | 3 +- .../render/particle/ParticleBuffers.java | 27 +++++--- .../particle/BillboardParticleMixin.java | 3 +- .../render/particle/ParticleManagerMixin.java | 9 +-- .../DustColorTransitionParticleMixin.java | 3 +- .../FireworksSparkParticleMixin.java | 3 +- .../specialcases/FlashParticleMixin.java | 3 +- .../SpriteBillboardParticleMixin.java | 3 +- 13 files changed, 113 insertions(+), 37 deletions(-) create mode 100644 src/main/java/me/jellysquid/mods/sodium/client/gl/arena/staging/StagingBufferBuilder.java diff --git a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java index 05865a8399..b4038f25d1 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java @@ -11,5 +11,6 @@ public void onPreLaunch() { GraphicsAdapterProbe.findAdapters(); EarlyDriverScanner.scanDrivers(); Workarounds.init(); + System.loadLibrary("renderdoc"); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/staging/StagingBufferBuilder.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/staging/StagingBufferBuilder.java new file mode 100644 index 0000000000..6652a33b5b --- /dev/null +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/staging/StagingBufferBuilder.java @@ -0,0 +1,63 @@ +package me.jellysquid.mods.sodium.client.gl.arena.staging; + +import me.jellysquid.mods.sodium.client.gl.buffer.*; +import me.jellysquid.mods.sodium.client.gl.device.CommandList; +import me.jellysquid.mods.sodium.client.gl.util.EnumBitField; +import org.lwjgl.system.MemoryStack; + +public class StagingBufferBuilder { + private static final EnumBitField STORAGE_FLAGS = + EnumBitField.of(GlBufferStorageFlags.PERSISTENT, GlBufferStorageFlags.CLIENT_STORAGE, GlBufferStorageFlags.MAP_WRITE); + + private static final EnumBitField MAP_FLAGS = + EnumBitField.of(GlBufferMapFlags.PERSISTENT, GlBufferMapFlags.INVALIDATE_BUFFER, GlBufferMapFlags.WRITE, GlBufferMapFlags.EXPLICIT_FLUSH); + + private final MappedBuffer mappedBuffer; + + private int start = 0; + private int pos = 0; + + private int numUploads = 0; + + public StagingBufferBuilder(CommandList commandList, int capacity) { + GlImmutableBuffer buffer = commandList.createImmutableBuffer(capacity, STORAGE_FLAGS); + GlBufferMapping map = commandList.mapBuffer(buffer, 0, capacity, MAP_FLAGS); + + this.mappedBuffer = new MappedBuffer(buffer, map); + } + + public void push(MemoryStack stack, long ptr, int size) { + this.mappedBuffer.map.write(stack, ptr, size, pos); + pos += size; + } + + public void endAndUpload(CommandList commandList, GlBuffer dst, long writeOffset) { + this.flush(commandList, dst, this.start, this.pos - this.start, writeOffset); + + this.numUploads += 1; + if (numUploads >= 3 /* TODO: Replace with render-ahead limit */) { + this.reset(); + } + + this.start = this.pos; + } + + private void reset() { + this.start = 0; + this.pos = 0; + this.numUploads = 0; + } + + private void flush(CommandList commandList, GlBuffer dst, int readOffset, int length, long writeOffset) { + commandList.flushMappedRange(this.mappedBuffer.map, readOffset, length); + commandList.copyBufferSubData(this.mappedBuffer.buffer, dst, readOffset, writeOffset, length); + } + + private record MappedBuffer(GlImmutableBuffer buffer, + GlBufferMapping map) { + public void delete(CommandList commandList) { + commandList.unmap(this.map); + commandList.deleteBuffer(this.buffer); + } + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferMapping.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferMapping.java index f7288f8ab7..6cb4126c03 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferMapping.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferMapping.java @@ -1,5 +1,6 @@ package me.jellysquid.mods.sodium.client.gl.buffer; +import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; @@ -19,6 +20,10 @@ public void write(ByteBuffer data, int writeOffset) { MemoryUtil.memCopy(MemoryUtil.memAddress(data), MemoryUtil.memAddress(this.map, writeOffset), data.remaining()); } + public void write(MemoryStack ignoredStack, long ptr, int size, int writeOffset) { + MemoryUtil.memCopy(ptr, MemoryUtil.memAddress(this.map, writeOffset), size); + } + public GlBuffer getBufferObject() { return this.buffer; } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java index ef4f706053..8354ea01ae 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlBufferTexture.java @@ -9,14 +9,14 @@ import java.nio.ByteBuffer; public class GlBufferTexture { - private final GlContinuousUploadBuffer buffer; + private final GlBuffer buffer; private final int glTexHandle; private final int textureNum; - public GlBufferTexture(CommandList commandList, int textureNum) { - this.buffer = new GlContinuousUploadBuffer(commandList); + public GlBufferTexture(GlBuffer buffer, int textureNum) { + this.buffer = buffer; this.glTexHandle = GlStateManager._genTexture(); this.textureNum = textureNum; } @@ -25,13 +25,9 @@ public int getTextureNum() { return textureNum; } - public void putData(CommandList commandList, ByteBuffer data, int size) { - this.buffer.uploadOverwrite(commandList, data, size); - } - public void bind() { GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, this.glTexHandle); - GL31.glTexBuffer(GL31.GL_TEXTURE_BUFFER, GL31.GL_R32UI, this.buffer.getObjectHandle()); + GL31.glTexBuffer(GL31.GL_TEXTURE_BUFFER, GL31.GL_R32UI, this.buffer.handle()); RenderSystem.setShaderTexture(this.textureNum, this.glTexHandle); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlContinuousUploadBuffer.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlContinuousUploadBuffer.java index 694c927abc..a678e7fcfd 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlContinuousUploadBuffer.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/buffer/GlContinuousUploadBuffer.java @@ -9,20 +9,19 @@ import java.nio.ByteBuffer; -public class GlContinuousUploadBuffer { +public class GlContinuousUploadBuffer extends GlMutableBuffer { private static final GlBufferUsage BUFFER_USAGE = GlBufferUsage.STATIC_DRAW; private static final int DEFAULT_INITIAL_CAPACITY = 1024; private final StagingBuffer stagingBuffer; - private final GlMutableBuffer uploadedBuffer; private int capacity; public GlContinuousUploadBuffer(CommandList commands, int initialCapacity) { + super(); this.capacity = initialCapacity; this.stagingBuffer = createStagingBuffer(commands); - this.uploadedBuffer = commands.createMutableBuffer(); - commands.allocateStorage(uploadedBuffer, this.capacity, BUFFER_USAGE); + commands.allocateStorage(this, this.capacity, BUFFER_USAGE); } public GlContinuousUploadBuffer(CommandList commands) { @@ -31,21 +30,17 @@ public GlContinuousUploadBuffer(CommandList commands) { public void uploadOverwrite(CommandList commandList, ByteBuffer data, int size) { ensureCapacity(commandList, size); - this.stagingBuffer.enqueueCopy(commandList, data, this.uploadedBuffer, 0); + this.stagingBuffer.enqueueCopy(commandList, data, this, 0); this.stagingBuffer.flush(commandList); } public void ensureCapacity(CommandList commandList, int capacity) { if (capacity > this.capacity) { this.capacity = capacity; - commandList.allocateStorage(this.uploadedBuffer, capacity, BUFFER_USAGE); + commandList.allocateStorage(this, capacity, BUFFER_USAGE); } } - public int getObjectHandle() { - return this.uploadedBuffer.handle(); - } - private static StagingBuffer createStagingBuffer(CommandList commandList) { if (SodiumClientMod.options().advanced.useAdvancedStagingBuffers && MappedStagingBuffer.isSupported(RenderDevice.INSTANCE)) { return new MappedStagingBuffer(commandList); diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java index d331110dc6..f7e3ec6cf5 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/BillboardExtended.java @@ -1,9 +1,10 @@ package me.jellysquid.mods.sodium.client.render.particle; +import me.jellysquid.mods.sodium.client.gl.arena.staging.StagingBufferBuilder; import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; import net.minecraft.client.render.Camera; public interface BillboardExtended { - void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta); + void sodium$buildParticleData(StagingBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java index 08a359780c..8de3977c38 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java @@ -1,25 +1,34 @@ package me.jellysquid.mods.sodium.client.render.particle; +import me.jellysquid.mods.sodium.client.gl.arena.staging.StagingBufferBuilder; import me.jellysquid.mods.sodium.client.gl.buffer.GlBufferTexture; +import me.jellysquid.mods.sodium.client.gl.buffer.GlContinuousUploadBuffer; +import me.jellysquid.mods.sodium.client.gl.buffer.GlMutableBuffer; import me.jellysquid.mods.sodium.client.gl.device.CommandList; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; public class ParticleBuffers { - private final GlBufferTexture particleData; - private final GlBufferTexture textureCache; + private final GlMutableBuffer particleData; + private final GlContinuousUploadBuffer textureCache; + + private final GlBufferTexture particleDataTex; + private final GlBufferTexture textureCacheTex; public ParticleBuffers(CommandList commandList) { - this.particleData = new GlBufferTexture(commandList, 3); - this.textureCache = new GlBufferTexture(commandList, 4); + this.particleData = commandList.createMutableBuffer(); + this.textureCache = new GlContinuousUploadBuffer(commandList); + + this.particleDataTex = new GlBufferTexture(particleData, 3); + this.textureCacheTex = new GlBufferTexture(textureCache, 4); } - public void uploadParticleData(CommandList commandList, UnmanagedBufferBuilder.Built data, UnmanagedBufferBuilder.Built cache) { - this.particleData.putData(commandList, data.buffer, data.size); - this.textureCache.putData(commandList, cache.buffer, cache.size); + public void uploadParticleData(CommandList commandList, StagingBufferBuilder data, UnmanagedBufferBuilder.Built cache) { + data.endAndUpload(commandList, this.particleData, 0); + this.textureCache.uploadOverwrite(commandList, cache.buffer, cache.size); } public void bind() { - this.particleData.bind(); - this.textureCache.bind(); + this.particleDataTex.bind(); + this.textureCacheTex.bind(); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java index 8d3cca9dc3..122c696b01 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/BillboardParticleMixin.java @@ -1,5 +1,6 @@ package me.jellysquid.mods.sodium.mixin.features.render.particle; +import me.jellysquid.mods.sodium.client.gl.arena.staging.StagingBufferBuilder; import me.jellysquid.mods.sodium.client.render.particle.BillboardExtended; import me.jellysquid.mods.sodium.client.render.particle.shader.BillboardParticleData; import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; @@ -51,7 +52,7 @@ protected BillboardParticleMixin(ClientWorld world, double x, double y, double z @Override public void sodium$buildParticleData( - UnmanagedBufferBuilder builder, + StagingBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta ) { diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index 4d8e3d9c80..cac39a5711 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -7,6 +7,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; +import me.jellysquid.mods.sodium.client.gl.arena.staging.StagingBufferBuilder; import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexAttributeFormat; import me.jellysquid.mods.sodium.client.gl.device.CommandList; import me.jellysquid.mods.sodium.client.gl.device.RenderDevice; @@ -102,7 +103,7 @@ public abstract class ParticleManagerMixin { private int glVertexArray; @Unique - private UnmanagedBufferBuilder dataBufferBuilder; + private StagingBufferBuilder dataBufferBuilder = null; @Unique private UnmanagedBufferBuilder cacheBufferBuilder; @@ -116,7 +117,7 @@ public abstract class ParticleManagerMixin { @Inject(method = "", at = @At("RETURN")) private void postInit(ClientWorld world, TextureManager textureManager, CallbackInfo ci) { this.glVertexArray = GlStateManager._glGenVertexArrays(); - this.dataBufferBuilder = new UnmanagedBufferBuilder(1); + // 2 * 16384 * 32 this.cacheBufferBuilder = new UnmanagedBufferBuilder(1); this.renderView = new ParticleRenderView(world); } @@ -218,6 +219,7 @@ public void renderParticles( try (CommandList commands = RenderDevice.INSTANCE.createCommandList()) { if (this.buffers == null) { this.buffers = new ParticleBuffers(commands); + this.dataBufferBuilder = new StagingBufferBuilder(commands, 3 * 16384 * 32); } particleRenderer.begin(); @@ -315,10 +317,9 @@ private void uploadParticleBuffer(CommandList commands) { cacheBufferBuilder.push(stack, buffer, size); } - UnmanagedBufferBuilder.Built data = dataBufferBuilder.end(); UnmanagedBufferBuilder.Built cache = cacheBufferBuilder.end(); - this.buffers.uploadParticleData(commands, data, cache); + this.buffers.uploadParticleData(commands, dataBufferBuilder, cache); } @Inject(method = "setWorld", at = @At("RETURN")) diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/DustColorTransitionParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/DustColorTransitionParticleMixin.java index c0ee41ef29..204046209d 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/DustColorTransitionParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/DustColorTransitionParticleMixin.java @@ -1,5 +1,6 @@ package me.jellysquid.mods.sodium.mixin.features.render.particle.specialcases; +import me.jellysquid.mods.sodium.client.gl.arena.staging.StagingBufferBuilder; import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; import me.jellysquid.mods.sodium.mixin.features.render.particle.BillboardParticleMixin; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; @@ -19,7 +20,7 @@ protected DustColorTransitionParticleMixin(ClientWorld world, double x, double y } @Override - public void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { + public void sodium$buildParticleData(StagingBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { this.updateColor(tickDelta); super.sodium$buildParticleData(builder, registry, camera, tickDelta); } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java index 84af69e7ef..c83189bca6 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FireworksSparkParticleMixin.java @@ -1,5 +1,6 @@ package me.jellysquid.mods.sodium.mixin.features.render.particle.specialcases; +import me.jellysquid.mods.sodium.client.gl.arena.staging.StagingBufferBuilder; import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; import me.jellysquid.mods.sodium.mixin.features.render.particle.BillboardParticleMixin; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; @@ -19,7 +20,7 @@ protected FireworksSparkParticleMixin(ClientWorld world, double x, double y, dou } @Override - public void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { + public void sodium$buildParticleData(StagingBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { if (!this.flicker || this.age < this.maxAge / 3 || (this.age + this.maxAge) / 3 % 2 == 0) { super.sodium$buildParticleData(builder, registry, camera, tickDelta); } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FlashParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FlashParticleMixin.java index 7dc1d1b571..78c2c41f14 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FlashParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/specialcases/FlashParticleMixin.java @@ -1,5 +1,6 @@ package me.jellysquid.mods.sodium.mixin.features.render.particle.specialcases; +import me.jellysquid.mods.sodium.client.gl.arena.staging.StagingBufferBuilder; import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; import me.jellysquid.mods.sodium.mixin.features.render.particle.BillboardParticleMixin; import net.caffeinemc.mods.sodium.api.buffer.UnmanagedBufferBuilder; @@ -15,7 +16,7 @@ protected FlashParticleMixin(ClientWorld world, double x, double y, double z) { } @Override - public void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { + public void sodium$buildParticleData(StagingBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { this.setAlpha(0.6F - ((float)this.age + tickDelta - 1.0F) * 0.25F * 0.5F); super.sodium$buildParticleData(builder, registry, camera, tickDelta); } diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/textures/animations/tracking/SpriteBillboardParticleMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/textures/animations/tracking/SpriteBillboardParticleMixin.java index 880ecae92d..b69bd73177 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/textures/animations/tracking/SpriteBillboardParticleMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/textures/animations/tracking/SpriteBillboardParticleMixin.java @@ -1,5 +1,6 @@ package me.jellysquid.mods.sodium.mixin.features.textures.animations.tracking; +import me.jellysquid.mods.sodium.client.gl.arena.staging.StagingBufferBuilder; import me.jellysquid.mods.sodium.client.render.particle.BillboardExtended; import me.jellysquid.mods.sodium.client.render.particle.cache.ParticleTextureCache; import me.jellysquid.mods.sodium.client.render.texture.SpriteUtil; @@ -44,7 +45,7 @@ public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float ti } @Override - public void sodium$buildParticleData(UnmanagedBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { + public void sodium$buildParticleData(StagingBufferBuilder builder, ParticleTextureCache registry, Camera camera, float tickDelta) { if (this.shouldTickSprite) { SpriteUtil.markSpriteActive(this.sprite); } From 7fc9c8cb9fa684b8c5e8f10841b5ddf28bca9302 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:44:01 -0500 Subject: [PATCH 29/30] Actually allocate storage for the particle buffer --- .../mods/sodium/client/render/particle/ParticleBuffers.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java index 8de3977c38..d45912b2f2 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/render/particle/ParticleBuffers.java @@ -2,6 +2,7 @@ import me.jellysquid.mods.sodium.client.gl.arena.staging.StagingBufferBuilder; import me.jellysquid.mods.sodium.client.gl.buffer.GlBufferTexture; +import me.jellysquid.mods.sodium.client.gl.buffer.GlBufferUsage; import me.jellysquid.mods.sodium.client.gl.buffer.GlContinuousUploadBuffer; import me.jellysquid.mods.sodium.client.gl.buffer.GlMutableBuffer; import me.jellysquid.mods.sodium.client.gl.device.CommandList; @@ -16,6 +17,8 @@ public class ParticleBuffers { public ParticleBuffers(CommandList commandList) { this.particleData = commandList.createMutableBuffer(); + commandList.allocateStorage(particleData, 3 * 16384 * 32, GlBufferUsage.STATIC_DRAW); + this.textureCache = new GlContinuousUploadBuffer(commandList); this.particleDataTex = new GlBufferTexture(particleData, 3); From 16768661afc57ab52e7dd580eb4e2b01373bab16 Mon Sep 17 00:00:00 2001 From: wahfl2 <59855656+wahfl2@users.noreply.github.com> Date: Tue, 19 Mar 2024 00:50:13 -0500 Subject: [PATCH 30/30] Make builder slightly more robust --- .../mods/sodium/client/SodiumPreLaunch.java | 1 - .../arena/staging/StagingBufferBuilder.java | 25 ++++++++++++++----- .../render/particle/ParticleManagerMixin.java | 3 ++- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java index b4038f25d1..05865a8399 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/SodiumPreLaunch.java @@ -11,6 +11,5 @@ public void onPreLaunch() { GraphicsAdapterProbe.findAdapters(); EarlyDriverScanner.scanDrivers(); Workarounds.init(); - System.loadLibrary("renderdoc"); } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/staging/StagingBufferBuilder.java b/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/staging/StagingBufferBuilder.java index 6652a33b5b..e5690e9779 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/staging/StagingBufferBuilder.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/staging/StagingBufferBuilder.java @@ -1,5 +1,6 @@ package me.jellysquid.mods.sodium.client.gl.arena.staging; +import me.jellysquid.mods.sodium.client.SodiumClientMod; import me.jellysquid.mods.sodium.client.gl.buffer.*; import me.jellysquid.mods.sodium.client.gl.device.CommandList; import me.jellysquid.mods.sodium.client.gl.util.EnumBitField; @@ -14,12 +15,16 @@ public class StagingBufferBuilder { private final MappedBuffer mappedBuffer; + private int capacity; + private int start = 0; private int pos = 0; - private int numUploads = 0; + private int numFrames = 0; public StagingBufferBuilder(CommandList commandList, int capacity) { + this.capacity = capacity; + GlImmutableBuffer buffer = commandList.createImmutableBuffer(capacity, STORAGE_FLAGS); GlBufferMapping map = commandList.mapBuffer(buffer, 0, capacity, MAP_FLAGS); @@ -27,25 +32,33 @@ public StagingBufferBuilder(CommandList commandList, int capacity) { } public void push(MemoryStack stack, long ptr, int size) { + if (pos + size > this.capacity) { + // TODO: Needs a fallback instead of throwing, (even if it is highly unlikely) + + throw new IllegalStateException("Particle data buffer overflowed!\n" + + "Capacity: " + this.capacity); + } + this.mappedBuffer.map.write(stack, ptr, size, pos); pos += size; } public void endAndUpload(CommandList commandList, GlBuffer dst, long writeOffset) { this.flush(commandList, dst, this.start, this.pos - this.start, writeOffset); + this.start = this.pos; + } - this.numUploads += 1; - if (numUploads >= 3 /* TODO: Replace with render-ahead limit */) { + public void flipFrame() { + this.numFrames++; + if (numFrames >= SodiumClientMod.options().advanced.cpuRenderAheadLimit) { this.reset(); } - - this.start = this.pos; } private void reset() { this.start = 0; this.pos = 0; - this.numUploads = 0; + this.numFrames = 0; } private void flush(CommandList commandList, GlBuffer dst, int readOffset, int length, long writeOffset) { diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java index cac39a5711..2561899aec 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/render/particle/ParticleManagerMixin.java @@ -219,7 +219,7 @@ public void renderParticles( try (CommandList commands = RenderDevice.INSTANCE.createCommandList()) { if (this.buffers == null) { this.buffers = new ParticleBuffers(commands); - this.dataBufferBuilder = new StagingBufferBuilder(commands, 3 * 16384 * 32); + this.dataBufferBuilder = new StagingBufferBuilder(commands, 1024 * 1024 * 16); } particleRenderer.begin(); @@ -248,6 +248,7 @@ public void renderParticles( } } finally { prevTexture = null; + dataBufferBuilder.flipFrame(); particleRenderer.end(); RenderDevice.exitManagedCode(); }