Skip to content

Commit c04eb1f

Browse files
committed
Merge remote-tracking branch 'origin/20.1/forge' into 21.0/neoforge
2 parents c94d1c1 + 170a1c1 commit c04eb1f

File tree

12 files changed

+161
-24
lines changed

12 files changed

+161
-24
lines changed

src/main/java/org/embeddedt/embeddium/api/options/structure/StandardOptions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,6 @@ public static class Option {
6161
public static final ResourceLocation CPU_FRAMES_AHEAD = ResourceLocation.fromNamespaceAndPath(Embeddium.MODID, "cpu_render_ahead_limit");
6262
public static final ResourceLocation TRANSLUCENT_FACE_SORTING = ResourceLocation.fromNamespaceAndPath(Embeddium.MODID, "translucent_face_sorting");
6363
public static final ResourceLocation USE_QUAD_NORMALS_FOR_LIGHTING = ResourceLocation.fromNamespaceAndPath(Embeddium.MODID, "use_quad_normals_for_lighting");
64+
public static final ResourceLocation RENDER_PASS_OPTIMIZATION = ResourceLocation.fromNamespaceAndPath(Embeddium.MODID, "render_pass_optimization");
6465
}
6566
}

src/main/java/org/embeddedt/embeddium/impl/gui/EmbeddiumGameOptionPages.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,15 @@ public static OptionPage performance() {
391391
.setFlags(OptionFlag.REQUIRES_RENDERER_UPDATE)
392392
.build()
393393
)
394+
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
395+
.setId(StandardOptions.Option.RENDER_PASS_OPTIMIZATION)
396+
.setName(Component.translatable("embeddium.options.use_render_pass_optimization.name"))
397+
.setTooltip(Component.translatable("embeddium.options.use_render_pass_optimization.tooltip"))
398+
.setControl(TickBoxControl::new)
399+
.setImpact(OptionImpact.LOW)
400+
.setBinding((opts, value) -> opts.performance.useRenderPassOptimization = value, opts -> opts.performance.useRenderPassOptimization)
401+
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
402+
.build())
394403
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
395404
.setId(StandardOptions.Option.NO_ERROR_CONTEXT)
396405
.setName(Component.translatable("sodium.options.use_no_error_context.name"))

src/main/java/org/embeddedt/embeddium/impl/gui/EmbeddiumOptions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public static class PerformanceSettings {
5151
public boolean useCompactVertexFormat = true;
5252
@SerializedName("use_translucent_face_sorting_v2")
5353
public boolean useTranslucentFaceSorting = true;
54+
public boolean useRenderPassOptimization = true;
5455
public boolean useNoErrorGLContext = true;
5556
}
5657

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.embeddedt.embeddium.impl.mixin.core.model.quad;
2+
3+
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
4+
import com.llamalad7.mixinextras.sugar.Local;
5+
import net.minecraft.client.renderer.block.model.BakedQuad;
6+
import net.minecraft.client.renderer.block.model.BlockElementFace;
7+
import net.minecraft.client.renderer.block.model.FaceBakery;
8+
import net.minecraft.client.renderer.texture.SpriteContents;
9+
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
10+
import org.embeddedt.embeddium.impl.model.quad.BakedQuadView;
11+
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFlags;
12+
import org.spongepowered.asm.mixin.Mixin;
13+
import org.spongepowered.asm.mixin.injection.At;
14+
15+
@Mixin(FaceBakery.class)
16+
public class BakedQuadFactoryMixin {
17+
/**
18+
* @author embeddedt
19+
* @reason Check if quad's UVs are contained within the sprite's boundaries; if so, mark it as having a trusted sprite
20+
* (meaning the particle sprite matches the encoded UVs)
21+
*/
22+
@ModifyReturnValue(method = "bakeQuad", at = @At("RETURN"))
23+
private BakedQuad setMaterialClassification(BakedQuad quad, @Local(ordinal = 0, argsOnly = true) BlockElementFace face, @Local(ordinal = 0, argsOnly = true) TextureAtlasSprite sprite) {
24+
if (sprite.getClass() == TextureAtlasSprite.class && sprite.contents().getClass() == SpriteContents.class) {
25+
float[] uvs = face.uv().uvs;
26+
float minUV = Float.MAX_VALUE, maxUV = Float.MIN_VALUE;
27+
28+
for (float uv : uvs) {
29+
minUV = Math.min(minUV, uv);
30+
maxUV = Math.max(maxUV, uv);
31+
}
32+
33+
if (minUV >= 0 && maxUV <= 16) {
34+
// Quad UVs do not extend outside texture boundary, we can trust the given sprite
35+
BakedQuadView view = (BakedQuadView)quad;
36+
view.setFlags(view.getFlags() | ModelQuadFlags.IS_TRUSTED_SPRITE);
37+
}
38+
39+
}
40+
41+
return quad;
42+
}
43+
}

src/main/java/org/embeddedt/embeddium/impl/mixin/core/model/quad/BakedQuadMixin.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ public int getForgeNormal(int idx) {
9797
@Override
9898
public int getFlags() {
9999
int f = this.flags;
100-
if (f == 0) {
101-
this.flags = f = ModelQuadFlags.getQuadFlags(this, direction);
100+
if ((f & ModelQuadFlags.IS_POPULATED) == 0) {
101+
this.flags = f = (f | ModelQuadFlags.getQuadFlags(this, direction));
102102
}
103103
return f;
104104
}

src/main/java/org/embeddedt/embeddium/impl/mixin/features/textures/mipmaps/SpriteContentsMixin.java

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import net.minecraft.client.renderer.texture.SpriteContents;
88
import net.minecraft.resources.ResourceLocation;
99
import net.minecraft.util.FastColor;
10+
import org.embeddedt.embeddium.impl.render.chunk.sprite.SpriteTransparencyLevel;
11+
import org.embeddedt.embeddium.impl.render.chunk.sprite.SpriteTransparencyLevelHolder;
1012
import org.lwjgl.system.MemoryUtil;
1113
import org.objectweb.asm.Opcodes;
1214
import org.spongepowered.asm.mixin.Final;
@@ -18,10 +20,10 @@
1820
import org.spongepowered.asm.mixin.injection.Redirect;
1921

2022
/**
21-
* This Mixin is ported from Iris at <a href="https://github.com/IrisShaders/Iris/blob/41095ac23ea0add664afd1b85c414d1f1ed94066/src/main/java/net/coderbot/iris/mixin/bettermipmaps/MixinTextureAtlasSprite.java">MixinTextureAtlasSprite</a>.
23+
* This Mixin is partially ported from Iris at <a href="https://github.com/IrisShaders/Iris/blob/41095ac23ea0add664afd1b85c414d1f1ed94066/src/main/java/net/coderbot/iris/mixin/bettermipmaps/MixinTextureAtlasSprite.java">MixinTextureAtlasSprite</a>.
2224
*/
2325
@Mixin(SpriteContents.class)
24-
public class SpriteContentsMixin {
26+
public class SpriteContentsMixin implements SpriteTransparencyLevelHolder {
2527
@Mutable
2628
@Shadow
2729
@Final
@@ -31,6 +33,9 @@ public class SpriteContentsMixin {
3133
@Final
3234
private ResourceLocation name;
3335

36+
@Unique
37+
private SpriteTransparencyLevel embeddium$transparencyLevel;
38+
3439
// While Fabric allows us to @Inject into the constructor here, that's just a specific detail of FabricMC's mixin
3540
// fork. Upstream Mixin doesn't allow arbitrary @Inject usage in constructor. However, we can use @ModifyVariable
3641
// just fine, in a way that hopefully doesn't conflict with other mods.
@@ -41,11 +46,9 @@ public class SpriteContentsMixin {
4146
// cross-platform.
4247
@Redirect(method = "<init>", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/texture/SpriteContents;originalImage:Lcom/mojang/blaze3d/platform/NativeImage;", opcode = Opcodes.PUTFIELD))
4348
private void sodium$beforeGenerateMipLevels(SpriteContents instance, NativeImage nativeImage, ResourceLocation identifier) {
44-
// Embeddium: Only fill in transparent colors if mipmaps are on and the texture name does not contain "leaves".
49+
// Only fill in transparent colors if mipmaps are on and the texture name does not contain "leaves".
4550
// We're injecting after the "name" field has been set, so this is safe even though we're in a constructor.
46-
if (Minecraft.getInstance().options.mipmapLevels().get() > 0 && !this.name.getPath().contains("leaves")) {
47-
sodium$fillInTransparentPixelColors(nativeImage);
48-
}
51+
embeddium$processTransparentImages(nativeImage, Minecraft.getInstance().options.mipmapLevels().get() > 0 && !this.name.getPath().contains("leaves"));
4952

5053
this.originalImage = nativeImage;
5154
}
@@ -59,7 +62,7 @@ public class SpriteContentsMixin {
5962
* black color does not leak over into sampling.
6063
*/
6164
@Unique
62-
private static void sodium$fillInTransparentPixelColors(NativeImage nativeImage) {
65+
private void embeddium$processTransparentImages(NativeImage nativeImage, boolean shouldRewriteColors) {
6366
final long ppPixel = NativeImageHelper.getPointerRGBA(nativeImage);
6467
final int pixelCount = nativeImage.getHeight() * nativeImage.getWidth();
6568

@@ -71,27 +74,41 @@ public class SpriteContentsMixin {
7174

7275
float totalWeight = 0.0f;
7376

77+
SpriteTransparencyLevel level = SpriteTransparencyLevel.OPAQUE;
78+
7479
for (int pixelIndex = 0; pixelIndex < pixelCount; pixelIndex++) {
7580
long pPixel = ppPixel + (pixelIndex * 4);
7681

7782
int color = MemoryUtil.memGetInt(pPixel);
7883
int alpha = FastColor.ABGR32.alpha(color);
7984

8085
// Ignore all fully-transparent pixels for the purposes of computing an average color.
81-
if (alpha != 0) {
82-
float weight = (float) alpha;
83-
84-
// Make sure to convert to linear space so that we don't lose brightness.
85-
r += ColorSRGB.srgbToLinear(FastColor.ABGR32.red(color)) * weight;
86-
g += ColorSRGB.srgbToLinear(FastColor.ABGR32.green(color)) * weight;
87-
b += ColorSRGB.srgbToLinear(FastColor.ABGR32.blue(color)) * weight;
88-
89-
totalWeight += weight;
86+
if (alpha > 0) {
87+
if(alpha < 255) {
88+
level = level.chooseNextLevel(SpriteTransparencyLevel.TRANSLUCENT);
89+
} else {
90+
level = level.chooseNextLevel(SpriteTransparencyLevel.OPAQUE);
91+
}
92+
93+
if (shouldRewriteColors) {
94+
float weight = (float) alpha;
95+
96+
// Make sure to convert to linear space so that we don't lose brightness.
97+
r += ColorSRGB.srgbToLinear(FastColor.ABGR32.red(color)) * weight;
98+
g += ColorSRGB.srgbToLinear(FastColor.ABGR32.green(color)) * weight;
99+
b += ColorSRGB.srgbToLinear(FastColor.ABGR32.blue(color)) * weight;
100+
101+
totalWeight += weight;
102+
}
103+
} else {
104+
level = level.chooseNextLevel(SpriteTransparencyLevel.TRANSPARENT);
90105
}
91106
}
92107

93-
// Bail if none of the pixels are semi-transparent.
94-
if (totalWeight == 0.0f) {
108+
this.embeddium$transparencyLevel = level;
109+
110+
// Bail if none of the pixels are semi-transparent or we aren't supposed to rewrite colors.
111+
if (!shouldRewriteColors || totalWeight == 0.0f) {
95112
return;
96113
}
97114

@@ -115,4 +132,9 @@ public class SpriteContentsMixin {
115132
}
116133
}
117134
}
135+
136+
@Override
137+
public SpriteTransparencyLevel embeddium$getTransparencyLevel() {
138+
return this.embeddium$transparencyLevel;
139+
}
118140
}

src/main/java/org/embeddedt/embeddium/impl/model/quad/properties/ModelQuadFlags.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ public class ModelQuadFlags {
2626
* the normals of each vertex.
2727
*/
2828
public static final int IS_VANILLA_SHADED = 0b1000;
29+
/**
30+
* Indicates that the particle sprite on this quad can be trusted to be the only sprite it shows.
31+
*/
32+
public static final int IS_TRUSTED_SPRITE = (1 << 4);
2933
/**
3034
* Indicates that the flags are populated for the quad.
3135
*/

src/main/java/org/embeddedt/embeddium/impl/render/chunk/compile/pipeline/BlockRenderer.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.mojang.blaze3d.vertex.PoseStack;
44
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
55
import org.embeddedt.embeddium.api.render.chunk.BlockRenderContext;
6+
import org.embeddedt.embeddium.impl.Embeddium;
67
import org.embeddedt.embeddium.impl.model.color.ColorProvider;
78
import org.embeddedt.embeddium.impl.model.color.ColorProviderRegistry;
89
import org.embeddedt.embeddium.impl.model.light.LightMode;
@@ -11,9 +12,13 @@
1112
import org.embeddedt.embeddium.impl.model.light.data.QuadLightData;
1213
import org.embeddedt.embeddium.impl.model.quad.BakedQuadView;
1314
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
15+
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFlags;
1416
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadOrientation;
17+
import org.embeddedt.embeddium.impl.render.ShaderModBridge;
1518
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildBuffers;
1619
import org.embeddedt.embeddium.impl.render.chunk.compile.buffers.ChunkModelBuilder;
20+
import org.embeddedt.embeddium.impl.render.chunk.sprite.SpriteTransparencyLevel;
21+
import org.embeddedt.embeddium.impl.render.chunk.sprite.SpriteTransparencyLevelHolder;
1722
import org.embeddedt.embeddium.impl.render.chunk.terrain.material.DefaultMaterials;
1823
import org.embeddedt.embeddium.impl.render.chunk.terrain.material.Material;
1924
import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkVertexEncoder;
@@ -76,13 +81,16 @@ public class BlockRenderer {
7681

7782
private final ChunkColorWriter colorEncoder = ChunkColorWriter.get();
7883

84+
private final boolean useRenderPassOptimization;
85+
7986
public BlockRenderer(ColorProviderRegistry colorRegistry, LightPipelineProvider lighters) {
8087
this.colorProviderRegistry = colorRegistry;
8188
this.lighters = lighters;
8289

8390
this.occlusionCache = new BlockOcclusionCache();
8491
this.useAmbientOcclusion = Minecraft.useAmbientOcclusion();
8592
this.fabricModelRenderingHandler = FRAPIRenderHandler.INDIGO_PRESENT ? new IndigoBlockRenderContext(this.occlusionCache, lighters.getLightData()) : null;
93+
this.useRenderPassOptimization = Embeddium.options().performance.useRenderPassOptimization && !ShaderModBridge.areShadersEnabled();
8694
}
8795

8896
/**
@@ -136,7 +144,7 @@ public void renderModel(BlockRenderContext ctx, ChunkBuildBuffers buffers) {
136144

137145
if (!quads.isEmpty() && this.isFaceVisible(ctx, face)) {
138146
this.useReorienting = true;
139-
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, meshBuilder, quads, face);
147+
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, buffers, meshBuilder, quads, face);
140148
if (!this.useReorienting) {
141149
// Reorienting was disabled on this side, make sure it's disabled for the null cullface too, in case
142150
// a mod layers textures in different lists
@@ -149,7 +157,7 @@ public void renderModel(BlockRenderContext ctx, ChunkBuildBuffers buffers) {
149157

150158
if (!all.isEmpty()) {
151159
this.useReorienting = canReorientNullCullface;
152-
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, meshBuilder, all, null);
160+
this.renderQuadList(ctx, material, lighter, colorizer, renderOffset, buffers, meshBuilder, all, null);
153161
}
154162
}
155163

@@ -202,8 +210,28 @@ private static boolean checkQuadsHaveSameLightingConfig(List<BakedQuad> quads) {
202210
return true;
203211
}
204212

213+
private ChunkModelBuilder chooseOptimalBuilder(Material defaultMaterial, ChunkBuildBuffers buffers, ChunkModelBuilder defaultBuilder, BakedQuadView quad) {
214+
if (defaultMaterial == DefaultMaterials.SOLID || !this.useRenderPassOptimization || (quad.getFlags() & ModelQuadFlags.IS_TRUSTED_SPRITE) == 0 || quad.getSprite() == null) {
215+
// No improvement possible
216+
return defaultBuilder;
217+
}
218+
219+
SpriteTransparencyLevel level = SpriteTransparencyLevelHolder.getTransparencyLevel(quad.getSprite().contents());
220+
221+
if (level == SpriteTransparencyLevel.OPAQUE && defaultMaterial.pass.supportsFragmentDiscard()) {
222+
// Can use solid with no visual difference
223+
return buffers.get(DefaultMaterials.SOLID);
224+
} else if (level == SpriteTransparencyLevel.TRANSPARENT && defaultMaterial == DefaultMaterials.TRANSLUCENT) {
225+
// Can use cutout_mipped with no visual difference
226+
return buffers.get(DefaultMaterials.CUTOUT_MIPPED);
227+
} else {
228+
// Have to use default
229+
return defaultBuilder;
230+
}
231+
}
232+
205233
private void renderQuadList(BlockRenderContext ctx, Material material, LightPipeline lighter, ColorProvider<BlockState> colorizer, Vec3 offset,
206-
ChunkModelBuilder builder, List<BakedQuad> quads, Direction cullFace) {
234+
ChunkBuildBuffers buffers, ChunkModelBuilder defaultBuilder, List<BakedQuad> quads, Direction cullFace) {
207235

208236
if(!checkQuadsHaveSameLightingConfig(quads)) {
209237
// Disable reorienting if quads use different light configurations, as otherwise layered quads
@@ -219,6 +247,8 @@ private void renderQuadList(BlockRenderContext ctx, Material material, LightPipe
219247
final var lightData = this.getVertexLight(ctx, quad.hasAmbientOcclusion() ? lighter : this.lighters.getLighter(LightMode.FLAT), cullFace, quad);
220248
final var vertexColors = this.getVertexColors(ctx, colorizer, quad);
221249

250+
ChunkModelBuilder builder = this.chooseOptimalBuilder(material, buffers, defaultBuilder, quad);
251+
222252
this.writeGeometry(ctx, builder, offset, material, quad, vertexColors, lightData);
223253

224254
TextureAtlasSprite sprite = quad.getSprite();
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.embeddedt.embeddium.impl.render.chunk.sprite;
2+
3+
public enum SpriteTransparencyLevel {
4+
OPAQUE,
5+
TRANSPARENT,
6+
TRANSLUCENT;
7+
8+
/**
9+
* {@return whichever level has a higher ordinal, i.e. requires a better render pass}
10+
*/
11+
public SpriteTransparencyLevel chooseNextLevel(SpriteTransparencyLevel level) {
12+
return level.ordinal() >= this.ordinal() ? level : this;
13+
}
14+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.embeddedt.embeddium.impl.render.chunk.sprite;
2+
3+
import net.minecraft.client.renderer.texture.SpriteContents;
4+
5+
public interface SpriteTransparencyLevelHolder {
6+
SpriteTransparencyLevel embeddium$getTransparencyLevel();
7+
8+
static SpriteTransparencyLevel getTransparencyLevel(SpriteContents contents) {
9+
return ((SpriteTransparencyLevelHolder)contents).embeddium$getTransparencyLevel();
10+
}
11+
}

0 commit comments

Comments
 (0)