Skip to content

Commit

Permalink
Various fixes to the ordering of entity rendering
Browse files Browse the repository at this point in the history
- Fixes IrisShaders#325
  - Caused by clothes being drawn before skin
- Fixes IrisShaders#586
  - Caused by transparent entities not being part of depthtex1 / depthtex2
  - This was an intentional change meant to allow shader packs to handle
    translucency better, but given the lack of a gbuffers_entities_translucent
    program, only caused issues. This approach will be revisited in the future.
- Fixes IrisShaders#524
  - This was caused by the water mask being drawn before translucent entity
    content.
- Might help with IrisShaders#346
  - I imagine this is similar to IrisShaders#586
  • Loading branch information
coderbot16 committed Jul 21, 2021
1 parent f91d7fc commit 3dd4ef5
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package net.coderbot.iris.fantastic;

public interface BlendingStateHolder {
boolean hasBlend();
TransparencyType getTransparencyType();
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private boolean isTransparent(RenderLayer layer) {
}

if (layer instanceof BlendingStateHolder) {
return ((BlendingStateHolder) layer).hasBlend();
return ((BlendingStateHolder) layer).getTransparencyType() != TransparencyType.OPAQUE;
}

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.client.MinecraftClient;
import net.coderbot.iris.layer.WrappableRenderLayer;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
Expand All @@ -16,13 +16,32 @@ public class FullyBufferedVertexConsumerProvider extends VertexConsumerProvider.
private final Set<BufferBuilder> activeBuffers;
private boolean flushed;

private final Set<RenderLayer> layersThisFrame;
private final List<RenderLayer> layersInOrder;

public FullyBufferedVertexConsumerProvider() {
super(new BufferBuilder(0), Collections.emptyMap());

this.bufferBuilders = new HashMap<>();
this.unused = new Object2IntOpenHashMap<>();
this.activeBuffers = new HashSet<>();
this.flushed = false;

this.layersThisFrame = new HashSet<>();
this.layersInOrder = new ArrayList<>();
}

private TransparencyType getTransparencyType(RenderLayer layer) {
while (layer instanceof WrappableRenderLayer) {
layer = ((WrappableRenderLayer) layer).unwrap();
}

if (layer instanceof BlendingStateHolder) {
return ((BlendingStateHolder) layer).getTransparencyType();
}

// Default to "generally transparent" if we can't figure it out.
return TransparencyType.GENERAL_TRANSPARENT;
}

@Override
Expand All @@ -35,6 +54,18 @@ public VertexConsumer getBuffer(RenderLayer renderLayer) {
buffer.begin(renderLayer.getDrawMode(), renderLayer.getVertexFormat());
}

if (this.layersThisFrame.add(renderLayer)) {
// If we haven't seen this layer yet, add it to the list of layers to render.
//
// We keep track of the order that layers were added, in order to ensure that if layers are not
// sorted relative each other due to translucency, that they are sorted in the order that they were
// drawn in.
//
// This is important for things like villager rendering, where the clothes and skin of villagers overlap
// each other, so if the clothes are drawn before the skin, they appear to be poorly-clothed.
this.layersInOrder.add(renderLayer);
}

// If this buffer is scheduled to be removed, unschedule it since it's now being used.
unused.removeInt(renderLayer);

Expand Down Expand Up @@ -68,7 +99,15 @@ public void draw() {
unused.removeInt(removed);
}

bufferBuilders.keySet().forEach(this::drawInternal);
// Make sure translucent layers are rendered after non-translucent ones.
layersInOrder.sort(Comparator.comparing(this::getTransparencyType));

for (RenderLayer layer : layersInOrder) {
drawInternal(layer);
}

layersInOrder.clear();
layersThisFrame.clear();

flushed = true;
}
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/net/coderbot/iris/fantastic/TransparencyType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.coderbot.iris.fantastic;

public enum TransparencyType {
/**
* Opaque, non transparent content.
*/
OPAQUE,
/**
* Generally transparent / translucent content.
*/
GENERAL_TRANSPARENT,
/**
* Enchantment glint and crumbling blocks
* These *must* be rendered after their corresponding opaque / transparent parts.
*/
DECAL,
/**
* Water mask, must be drawn after all other things.
* Prevents water from appearing inside of boats.
*/
WATER_MASK
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import java.util.Objects;
import java.util.Optional;

public class EntityColorWrappedRenderLayer extends RenderLayer {
public class EntityColorWrappedRenderLayer extends RenderLayer implements WrappableRenderLayer {
private final EntityColorRenderPhase entityColor;
private final RenderLayer wrapped;

Expand All @@ -33,6 +33,7 @@ public void endDrawing() {
super.endDrawing();
}

@Override
public RenderLayer unwrap() {
return this.wrapped;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import net.minecraft.client.render.RenderLayer;

public class IrisRenderLayerWrapper extends RenderLayer {
public class IrisRenderLayerWrapper extends RenderLayer implements WrappableRenderLayer {
private final UseProgramRenderPhase useProgram;
private final RenderLayer wrapped;

Expand All @@ -34,6 +34,7 @@ public void endDrawing() {
super.endDrawing();
}

@Override
public RenderLayer unwrap() {
return this.wrapped;
}
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/net/coderbot/iris/layer/WrappableRenderLayer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package net.coderbot.iris.layer;

import net.minecraft.client.render.RenderLayer;

public interface WrappableRenderLayer {
/**
* Returns the underlying wrapped RenderLayer. Might return itself if this RenderLayer doesn't wrap anything.
*/
RenderLayer unwrap();
}
7 changes: 2 additions & 5 deletions src/main/java/net/coderbot/iris/mixin/MixinWorldRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,8 @@ public class MixinWorldRenderer {
CallbackInfo ci, Profiler profiler, Vec3d vec3d, double d, double e, double f,
Matrix4f matrix4f2, boolean bl, Frustum frustum2, boolean bl3,
VertexConsumerProvider.Immediate immediate) {
profiler.swap("iris_opaque_entity_draws");

if (immediate instanceof FlushableVertexConsumerProvider) {
((FlushableVertexConsumerProvider) immediate).flushNonTranslucentContent();
}
profiler.swap("iris_entity_draws");
immediate.draw();

profiler.swap("iris_pre_translucent");
pipeline.beginTranslucents();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package net.coderbot.iris.mixin.fantastic;

import net.coderbot.iris.fantastic.ExtendedBufferStorage;
import net.coderbot.iris.fantastic.FantasticVertexConsumerProvider;
import net.coderbot.iris.fantastic.FullyBufferedVertexConsumerProvider;
import net.minecraft.client.render.BufferBuilderStorage;
import net.minecraft.client.render.OutlineVertexConsumerProvider;
import net.minecraft.client.render.VertexConsumerProvider;
Expand All @@ -14,7 +14,7 @@
@Mixin(BufferBuilderStorage.class)
public class MixinBufferBuilderStorage implements ExtendedBufferStorage {
@Unique
private final VertexConsumerProvider.Immediate buffered = new FantasticVertexConsumerProvider();
private final VertexConsumerProvider.Immediate buffered = new FullyBufferedVertexConsumerProvider();

@Unique
private int begins = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.coderbot.iris.mixin.fantastic;

import net.coderbot.iris.fantastic.BlendingStateHolder;
import net.coderbot.iris.fantastic.TransparencyType;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderPhase;
import net.minecraft.client.render.VertexFormat;
Expand All @@ -13,7 +14,7 @@
@Mixin(targets = "net/minecraft/client/render/RenderLayer$MultiPhase")
public abstract class MixinMultiPhaseRenderLayer extends RenderLayer implements BlendingStateHolder {
@Unique
private boolean hasBlending;
private TransparencyType transparencyType;

private MixinMultiPhaseRenderLayer(String name, VertexFormat vertexFormat, int drawMode, int expectedBufferSize, boolean hasCrumbling, boolean translucent, Runnable startAction, Runnable endAction) {
super(name, vertexFormat, drawMode, expectedBufferSize, hasCrumbling, translucent, startAction, endAction);
Expand All @@ -24,11 +25,21 @@ private MixinMultiPhaseRenderLayer(String name, VertexFormat vertexFormat, int d
boolean hasCrumbling, boolean translucent, RenderLayer.MultiPhaseParameters phases,
CallbackInfo ci) {
RenderPhase.Transparency transparency = ((MultiPhaseParametersAccessor) (Object) phases).getTransparency();
hasBlending = transparency != RenderPhaseAccessor.getNO_TRANSPARENCY();

if ("water_mask".equals(name)) {
transparencyType = TransparencyType.WATER_MASK;
} else if (transparency == RenderPhaseAccessor.getNO_TRANSPARENCY()) {
transparencyType = TransparencyType.OPAQUE;
} else if (transparency == RenderPhaseAccessor.getGLINT_TRANSPARENCY() ||
transparency == RenderPhaseAccessor.getCRUMBLING_TRANSPARENCY()) {
transparencyType = TransparencyType.DECAL;
} else {
transparencyType = TransparencyType.GENERAL_TRANSPARENT;
}
}

@Override
public boolean hasBlend() {
return hasBlending;
public TransparencyType getTransparencyType() {
return transparencyType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,6 @@ public class MixinWorldRenderer {
((ExtendedBufferStorage) bufferBuilders).beginWorldRendering();
}

@Inject(method = "render", at = @At(value = "INVOKE", target = "net/minecraft/client/render/RenderLayer.getTranslucent ()Lnet/minecraft/client/render/RenderLayer;"))
private void iris$fantastic$preRenderTranslucentTerrain(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo callback) {
VertexConsumerProvider.Immediate vertexConsumers = bufferBuilders.getEntityVertexConsumers();

if (vertexConsumers instanceof FlushableVertexConsumerProvider) {
MinecraftClient.getInstance().getProfiler().swap("iris_translucent_entity_draws");
((FlushableVertexConsumerProvider) vertexConsumers).flushTranslucentContent();
MinecraftClient.getInstance().getProfiler().swap("translucent");
}
}

@Inject(method = "render", at = @At("RETURN"))
private void iris$fantastic$endWorldRender(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo callback) {
((ExtendedBufferStorage) bufferBuilders).endWorldRendering();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,14 @@ public interface RenderPhaseAccessor {
static RenderPhase.Transparency getNO_TRANSPARENCY() {
throw new AssertionError();
}

@Accessor("GLINT_TRANSPARENCY")
static RenderPhase.Transparency getGLINT_TRANSPARENCY() {
throw new AssertionError();
}

@Accessor("CRUMBLING_TRANSPARENCY")
static RenderPhase.Transparency getCRUMBLING_TRANSPARENCY() {
throw new AssertionError();
}
}

0 comments on commit 3dd4ef5

Please sign in to comment.