Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize code; fix translucent particles #1

Merged
merged 4 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package me.jellysquid.mods.sodium.client.render.particle;

public interface ExtendedParticle {
boolean sodium$reachedBillboardDraw();
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public void begin() {
// pass.startDrawing(); .. Do I need a pass?

this.activeProgram.bind();
}

public void setupState() {
this.activeProgram.getInterface().setupState();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);

Expand All @@ -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);
}
Expand All @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +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;
Expand All @@ -32,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;

Expand All @@ -58,7 +60,7 @@ public abstract class ParticleManagerMixin {
private final ShaderBillboardParticleRenderer particleRenderer = new ShaderBillboardParticleRenderer();

@Unique
private static final Map<Class<? extends BillboardParticle>, Boolean> classOverridesBuild = Maps.newIdentityHashMap();
private static final Object2BooleanMap<Class<? extends BillboardParticle>> classOverridesBuild = new Object2BooleanOpenHashMap<>();

@Unique
private static final String BUILD_GEOMETRY_METHOD = FabricLoader.getInstance().getMappingResolver().mapMethodName(
Expand All @@ -77,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);
Expand Down Expand Up @@ -108,7 +111,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)
)) {
this.billboardParticles
.computeIfAbsent(particle.getType(), sheet -> EvictingQueue.create(16384))
Expand All @@ -120,74 +123,81 @@ public void tick() {
}
}
}

testBuffer.end().release();
}

@Unique
private boolean testClassOverrides(Class<? extends BillboardParticle> 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"))
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
) {
for(ParticleTextureSheet particleTextureSheet : PARTICLE_TEXTURE_SHEETS) {
particleRenderer.begin();
ParticleShaderInterface shader = this.particleRenderer.getActiveProgram().getInterface();
shader.setProjectionMatrix(RenderSystem.getProjectionMatrix());
shader.setModelViewMatrix(RenderSystem.getModelViewMatrix());

for (ParticleTextureSheet particleTextureSheet : PARTICLE_TEXTURE_SHEETS) {
Queue<BillboardParticle> iterable = this.billboardParticles.get(particleTextureSheet);
if (iterable != null && !iterable.isEmpty()) {
bindParticleTextureSheet(particleTextureSheet);
particleRenderer.setupState();
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) {
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();
}
}

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.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);
} 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();

buffer.draw();
}
}
}
53 changes: 13 additions & 40 deletions src/main/resources/assets/sodium/shaders/particles/particle.vsh
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

#import <sodium:include/matrices.glsl>

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;
Expand All @@ -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);
}
}