Skip to content

Commit

Permalink
Merge pull request #1 from IMS212/gpu
Browse files Browse the repository at this point in the history
Optimize code; fix translucent particles
  • Loading branch information
wahfl2 committed Sep 25, 2023
2 parents bbd18ab + 941189c commit 69c96d5
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 93 deletions.
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);
}
}

0 comments on commit 69c96d5

Please sign in to comment.