From 11472c2d9ce563ab1793eed6af87e04cf06646c6 Mon Sep 17 00:00:00 2001 From: Grondag Date: Sun, 1 Nov 2020 10:54:29 -0800 Subject: [PATCH] Partial cleanup of transparency, material semantics, add material debug code --- .../buffer/encoding/CanvasImmediate.java | 6 +- .../material/state/AbstractRenderState.java | 10 +-- .../state/AbstractRenderStateView.java | 65 +++++++++++++++++-- .../material/state/AbstractStateFinder.java | 27 +++++++- .../material/state/MaterialFinderImpl.java | 2 + .../material/state/RenderLayerHelper.java | 27 +++++--- .../canvas/render/CanvasWorldRenderer.java | 27 ++++---- .../canvas/shader/MaterialShaderImpl.java | 12 ++++ 8 files changed, 133 insertions(+), 43 deletions(-) diff --git a/src/main/java/grondag/canvas/buffer/encoding/CanvasImmediate.java b/src/main/java/grondag/canvas/buffer/encoding/CanvasImmediate.java index e406e1f23..142c9b835 100644 --- a/src/main/java/grondag/canvas/buffer/encoding/CanvasImmediate.java +++ b/src/main/java/grondag/canvas/buffer/encoding/CanvasImmediate.java @@ -3,6 +3,7 @@ import java.util.Map; import java.util.SortedMap; +import grondag.canvas.material.property.MaterialTarget; import grondag.canvas.material.state.RenderContextState; import grondag.canvas.material.state.RenderLayerHelper; import grondag.canvas.material.state.RenderMaterialImpl; @@ -41,7 +42,8 @@ public VertexConsumer getBuffer(RenderLayer renderLayer) { } } - public void drawCollectors(boolean translucentTerrain) { + // WIP: need a way to ensure decal layers go last + public void drawCollectors(MaterialTarget target) { final ObjectArrayList drawList = this.drawList; final int limit = collectors.size(); @@ -49,7 +51,7 @@ public void drawCollectors(boolean translucentTerrain) { for (int i = 0; i < limit; ++i) { final VertexCollectorImpl collector = collectors.get(i); - if (collector.materialState.isTranslucentTerrain == translucentTerrain && !collector.isEmpty()) { + if (collector.materialState.target == target && !collector.isEmpty()) { drawList.add(collector); } } diff --git a/src/main/java/grondag/canvas/material/state/AbstractRenderState.java b/src/main/java/grondag/canvas/material/state/AbstractRenderState.java index 9a5dfa12c..0c93973b3 100644 --- a/src/main/java/grondag/canvas/material/state/AbstractRenderState.java +++ b/src/main/java/grondag/canvas/material/state/AbstractRenderState.java @@ -69,13 +69,6 @@ abstract class AbstractRenderState extends AbstractRenderStateView { public final boolean hurtOverlay; public final boolean flashOverlay; - /** - * True when translucent transparency and targets the terrain layer. - * Should not be rendered until that framebuffer is initialized in fabulous mode - * or should be delayed to render with other trasnslucent when not. - */ - public final boolean isTranslucentTerrain; - protected AbstractRenderState(int index, long bits) { super(bits); this.index = index; @@ -92,9 +85,8 @@ protected AbstractRenderState(int index, long bits) { fog = fog(); condition = condition(); transparency = TRANSPARENCY.getValue(bits); - sorted = transparency != MaterialTransparency.NONE && decal != MaterialDecal.TRANSLUCENT; + sorted = sorted(); shader = MaterialShaderManager.INSTANCE.get(SHADER.getValue(bits)); - isTranslucentTerrain = (target == MaterialTarget.MAIN || target == MaterialTarget.TRANSLUCENT) && transparency == MaterialTransparency.TRANSLUCENT; blendMode = blendMode(); emissive = emissive(); disableDiffuse = disableDiffuse(); diff --git a/src/main/java/grondag/canvas/material/state/AbstractRenderStateView.java b/src/main/java/grondag/canvas/material/state/AbstractRenderStateView.java index 3d5041592..85c469797 100644 --- a/src/main/java/grondag/canvas/material/state/AbstractRenderStateView.java +++ b/src/main/java/grondag/canvas/material/state/AbstractRenderStateView.java @@ -44,7 +44,7 @@ protected AbstractRenderStateView(long bits) { } public long collectorKey() { - return bits & COLLECTOR_KEY_MASK; + return sorted() ? (bits & SORTED_COLLECTOR_KEY_MASK) : (bits & UNSORTED_COLLECTOR_KEY_MASK); } public MaterialShaderImpl shader() { @@ -55,6 +55,10 @@ public MaterialConditionImpl condition() { return MaterialConditionImpl.fromIndex(CONDITION.getValue(bits)); } + public boolean sorted() { + return SORTED.getValue(bits); + } + public int conditionIndex() { return CONDITION.getValue(bits); } @@ -162,6 +166,41 @@ public int shaderFlags() { return (int) (bits >>> FLAG_SHIFT) & 0xFF; } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("bits: ").append(Long.toHexString(bits)).append(" ").append(Long.toBinaryString(bits)).append("\n"); + sb.append("collectorKey: ").append(Long.toHexString(collectorKey())).append(" ").append(Long.toBinaryString(collectorKey())).append("\n"); + sb.append("bilinear: ").append(bilinear()).append("\n"); + sb.append("blendMode: ").append(blendMode().name()).append("\n"); + sb.append("conditionIndex: ").append(conditionIndex()).append("\n"); + sb.append("cull: ").append(cull()).append("\n"); + sb.append("cutout: ").append(cutout()).append("\n"); + sb.append("decal: ").append(decal().name()).append("\n"); + sb.append("depthTest: ").append(depthTest().name()).append("\n"); + sb.append("disableAo: ").append(disableAo()).append("\n"); + sb.append("disableColorIndex: ").append(disableColorIndex()).append("\n"); + sb.append("disableDiffuse: ").append(disableDiffuse()).append("\n"); + sb.append("emissive: ").append(emissive()).append("\n"); + sb.append("enableLightmap: ").append(enableLightmap()).append("\n"); + sb.append("flashoverlay: ").append(flashOverlay()).append("\n"); + sb.append("fog: ").append(fog().name()).append("\n"); + sb.append("hurtOverlay: ").append(hurtOverlay()).append("\n"); + sb.append("lines: ").append(lines()).append("\n"); + sb.append("primitive: ").append(primitive()).append("\n"); + sb.append("shader: [").append(shader().toString()).append("]\n"); + sb.append("shaderFlags: ").append(Integer.toBinaryString(shaderFlags())).append("\n"); + sb.append("sorted: ").append(sorted()).append("\n"); + sb.append("target: ").append(target().name()).append("\n"); + sb.append("texture: ").append(texture().index).append(" ").append(texture().id.toString()).append("\n"); + sb.append("transparency: ").append(translucency().name()).append("\n"); + sb.append("transparentCutout: ").append(translucentCutout()).append("\n"); + sb.append("unmipped: ").append(unmipped()).append("\n"); + sb.append("writeMask: ").append(writeMask().name()).append("\n"); + return sb.toString(); + + } + static final BitPacker64 PACKER = new BitPacker64<> (null, null); // GL State comes first for sorting @@ -174,20 +213,34 @@ public int shaderFlags() { static final BitPacker64.EnumElement WRITE_MASK = PACKER.createEnumElement(MaterialWriteMask.class); static final BitPacker64.BooleanElement ENABLE_LIGHTMAP = PACKER.createBooleanElement(); // note that translucent decal is never persisted because it isn't part of GL state - that is indicated by SORTED + // WIP: move matrix-based decal out of render state static final BitPacker64.EnumElement DECAL = PACKER.createEnumElement(MaterialDecal.class); static final BitPacker64.EnumElement TARGET = PACKER.createEnumElement(MaterialTarget.class); static final BitPacker64.BooleanElement LINES = PACKER.createBooleanElement(); static final BitPacker64.EnumElement FOG = PACKER.createEnumElement(MaterialFog.class); + + // These don't affect GL state but must be collected and drawn separately + // They also generally won't change within a render state for any given context + // so they don't cause fragmentation except for sorted transparency, which is intended. + /** indicates sorted transparency - should be only one true value per render target */ + static final BitPacker64.BooleanElement SORTED = PACKER.createBooleanElement(); + static final BitPacker64.IntElement PRIMITIVE = PACKER.createIntElement(8); + + public static final long SORTED_RENDER_STATE_MASK = PACKER.bitMask(); + + // WIP: simplify shaders - the actual programs really aren't variant except by sorting + // WIP: make vertex/uniform configurable for non-sorted layers + + // Part of render state and collection key for non-sorted, not included in either for sorted static final BitPacker64.IntElement SHADER = PACKER.createIntElement(4096); - public static final long RENDER_STATE_MASK = PACKER.bitMask(); + public static final long UNSORTED_RENDER_STATE_MASK = PACKER.bitMask(); + public static final long SORTED_COLLECTOR_KEY_MASK = PACKER.bitMask(); - // These don't affect GL state but must be buffered separately - // Should always be zero in render state, only used in buffer key and material - static final BitPacker64.IntElement PRIMITIVE = PACKER.createIntElement(8); + // Can't be part of translucent collector key static final BitPacker64.IntElement CONDITION = PACKER.createIntElement(MaterialConditionImpl.MAX_CONDITIONS); - public static final long COLLECTOR_KEY_MASK = PACKER.bitMask(); + public static final long UNSORTED_COLLECTOR_KEY_MASK = PACKER.bitMask(); // here and below only used in material - holds vertex state - does not affect buffering or gl State static final BitPacker64.BooleanElement DISABLE_COLOR_INDEX = PACKER.createBooleanElement(); diff --git a/src/main/java/grondag/canvas/material/state/AbstractStateFinder.java b/src/main/java/grondag/canvas/material/state/AbstractStateFinder.java index 1f3ef5c70..710abae73 100644 --- a/src/main/java/grondag/canvas/material/state/AbstractStateFinder.java +++ b/src/main/java/grondag/canvas/material/state/AbstractStateFinder.java @@ -114,6 +114,11 @@ public T target(MaterialTarget target) { return (T) this; } + public T sorted(boolean sorted) { + bits = SORTED.setValue(sorted, bits); + return (T) this; + } + public T lines(boolean lines) { bits = LINES.setValue(lines, bits); return (T) this; @@ -195,6 +200,8 @@ public T blendMode(BlendMode blendMode) { cutout(true); unmipped(true); translucentCutout(false); + target(MaterialTarget.MAIN); + sorted(false); bits = DEFAULT_BLEND_MODE.setValue(false, bits); break; case CUTOUT_MIPPED: @@ -202,13 +209,17 @@ public T blendMode(BlendMode blendMode) { cutout(true); unmipped(false); translucentCutout(false); + target(MaterialTarget.MAIN); + sorted(false); bits = DEFAULT_BLEND_MODE.setValue(false, bits); break; case TRANSLUCENT: transparency(MaterialTransparency.TRANSLUCENT); - cutout(true); + cutout(false); unmipped(false); - translucentCutout(true); + translucentCutout(false); + target(MaterialTarget.TRANSLUCENT); + sorted(true); bits = DEFAULT_BLEND_MODE.setValue(false, bits); break; case DEFAULT: @@ -216,6 +227,8 @@ public T blendMode(BlendMode blendMode) { cutout(false); unmipped(false); translucentCutout(false); + target(MaterialTarget.MAIN); + sorted(false); bits = DEFAULT_BLEND_MODE.setValue(true, bits); break; default: @@ -224,6 +237,8 @@ public T blendMode(BlendMode blendMode) { cutout(false); unmipped(false); translucentCutout(false); + target(MaterialTarget.MAIN); + sorted(false); bits = DEFAULT_BLEND_MODE.setValue(false, bits); break; } @@ -256,11 +271,17 @@ public T copyFrom(RenderMaterial material) { public V find() { // WIP: need a way to ensure only one translucent buffer/render state per target - bits = SHADER.setValue(MaterialShaderManager.INSTANCE.find(vertexShaderIndex,fragmentShaderIndex, TRANSPARENCY.getValue(bits) == MaterialTransparency.TRANSLUCENT ? ProgramType.MATERIAL_VERTEX_LOGIC : ProgramType.MATERIAL_UNIFORM_LOGIC).index, bits); + bits = SHADER.setValue(MaterialShaderManager.INSTANCE.find(vertexShaderIndex,fragmentShaderIndex, sorted() ? ProgramType.MATERIAL_VERTEX_LOGIC : ProgramType.MATERIAL_UNIFORM_LOGIC).index, bits); return findInner(); } public V fromBits(long bits) { + if (SORTED.getValue(bits)) { + bits &= SORTED_RENDER_STATE_MASK; + } else { + bits &= UNSORTED_RENDER_STATE_MASK; + } + this.bits = bits; return findInner(); } diff --git a/src/main/java/grondag/canvas/material/state/MaterialFinderImpl.java b/src/main/java/grondag/canvas/material/state/MaterialFinderImpl.java index eeb530fe1..29e316b8f 100644 --- a/src/main/java/grondag/canvas/material/state/MaterialFinderImpl.java +++ b/src/main/java/grondag/canvas/material/state/MaterialFinderImpl.java @@ -2,6 +2,8 @@ import grondag.frex.api.material.MaterialFinder; +// WIP: expose attributes in FREX +// WIP: use decal layer in JMX, RenderBender and XB/XM to improve performance for mult-layer blocks public class MaterialFinderImpl extends AbstractStateFinder implements MaterialFinder { @Override protected synchronized RenderMaterialImpl findInner() { diff --git a/src/main/java/grondag/canvas/material/state/RenderLayerHelper.java b/src/main/java/grondag/canvas/material/state/RenderLayerHelper.java index 2f11c9a50..0412d1e94 100644 --- a/src/main/java/grondag/canvas/material/state/RenderLayerHelper.java +++ b/src/main/java/grondag/canvas/material/state/RenderLayerHelper.java @@ -25,6 +25,7 @@ import grondag.canvas.mixin.AccessTexture; import grondag.canvas.mixinterface.EntityRenderDispatcherExt; import grondag.canvas.mixinterface.MultiPhaseExt; +import grondag.canvas.mixinterface.RenderLayerExt; import it.unimi.dsi.fastutil.Hash; import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import org.lwjgl.opengl.GL11; @@ -65,16 +66,11 @@ public static boolean isExcluded(RenderLayer layer) { return EXCLUSIONS.contains(layer); } - public static RenderMaterialImpl copyFromLayer(RenderLayer layer) { - if (isExcluded(layer)) { - return RenderMaterialImpl.MISSING; - } - + private static void copyFromLayer(MaterialFinderImpl finder, RenderLayer layer) { final AccessMultiPhaseParameters params = ((MultiPhaseExt) layer).canvas_phases(); final AccessTexture tex = (AccessTexture) params.getTexture(); - final MaterialFinderImpl finder = MaterialFinderImpl.threadLocal(); - + finder.sorted(((RenderLayerExt) layer).canvas_isTranslucent()); finder.primitive(GL11.GL_QUADS); finder.texture(tex.getId().orElse(null)); finder.transparency(MaterialTransparency.fromPhase(params.getTransparency())); @@ -87,10 +83,9 @@ public static RenderMaterialImpl copyFromLayer(RenderLayer layer) { finder.lines(params.getLineWidth() != RenderPhase.FULL_LINE_WIDTH); finder.fog(MaterialFog.fromPhase(params.getFog())); finder.unmipped(!tex.getMipmap()); - finder.disableDiffuse(params.getDiffuseLighting() == RenderPhase.DISABLE_DIFFUSE_LIGHTING); + finder.bilinear(tex.getBilinear()); finder.cutout(params.getAlpha() != RenderPhase.ZERO_ALPHA); finder.translucentCutout(params.getAlpha() == RenderPhase.ONE_TENTH_ALPHA); - finder.disableAo(true); finder.defaultBlendMode(false); // vanilla sets these as part of draw process but we don't want special casing @@ -99,7 +94,21 @@ public static RenderMaterialImpl copyFromLayer(RenderLayer layer) { finder.texture(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE); finder.writeMask(MaterialWriteMask.COLOR_DEPTH); finder.enableLightmap(true); + finder.disableAo(false); + finder.disableDiffuse(false); + } else { + finder.disableAo(true); + finder.disableDiffuse(params.getDiffuseLighting() == RenderPhase.DISABLE_DIFFUSE_LIGHTING); } + } + + public static RenderMaterialImpl copyFromLayer(RenderLayer layer) { + if (isExcluded(layer)) { + return RenderMaterialImpl.MISSING; + } + + final MaterialFinderImpl finder = MaterialFinderImpl.threadLocal(); + copyFromLayer(finder, layer); // WIP2: put in proper material map hooks final String name = ((MultiPhaseExt) layer).canvas_name(); diff --git a/src/main/java/grondag/canvas/render/CanvasWorldRenderer.java b/src/main/java/grondag/canvas/render/CanvasWorldRenderer.java index e5b14d02b..264668bfd 100644 --- a/src/main/java/grondag/canvas/render/CanvasWorldRenderer.java +++ b/src/main/java/grondag/canvas/render/CanvasWorldRenderer.java @@ -46,6 +46,7 @@ import grondag.canvas.compat.VoxelMapHolder; import grondag.canvas.light.LightmapHdTexture; import grondag.canvas.material.property.MaterialMatrixState; +import grondag.canvas.material.property.MaterialTarget; import grondag.canvas.material.state.RenderContextState; import grondag.canvas.material.state.RenderState; import grondag.canvas.mixinterface.WorldRendererExt; @@ -614,7 +615,7 @@ public void renderWorld(MatrixStack matrixStack, float tickDelta, long limitTime assert matrixStack.isEmpty() : "Matrix stack not empty in world render when expected"; - immediate.drawCollectors(false); + immediate.drawCollectors(MaterialTarget.MAIN); bufferBuilders.getOutlineVertexConsumers().draw(); @@ -678,10 +679,8 @@ public void renderWorld(MatrixStack matrixStack, float tickDelta, long limitTime mc.debugRenderer.render(matrixStack, immediate, cameraX, cameraY, cameraZ); RenderSystem.popMatrix(); - // Intention here seems to be to draw all the non-translucent layers before - // enabling the translucent target - this is a very brittle way of handling - // should be able to draw all layers for a given target - immediate.drawCollectors(false); + // Should generally not have anything here but draw in case content injected in hooks? + immediate.drawCollectors(MaterialTarget.MAIN); immediate.draw(RenderLayer.getArmorGlint()); immediate.draw(RenderLayer.getArmorEntityGlint()); @@ -696,22 +695,22 @@ public void renderWorld(MatrixStack matrixStack, float tickDelta, long limitTime if (advancedTranslucency) { profiler.swap("translucent"); + Framebuffer fb = mcwr.getTranslucentFramebuffer(); + fb.clear(MinecraftClient.IS_SYSTEM_MAC); + fb.copyDepthFrom(mcfb); + fb.beginWrite(false); + // in fabulous mode, the only thing that renders to terrain translucency // is terrain itself - so everything else can be rendered first // Lines draw to entity (item) target immediate.draw(RenderLayer.getLines()); - immediate.drawCollectors(true); + immediate.drawCollectors(MaterialTarget.TRANSLUCENT); - // This presumably catches any remaining translucent layers in vanilla + // This catches entity layer and any remaining non-main layers immediate.draw(); - Framebuffer fb = mcwr.getTranslucentFramebuffer(); - fb.clear(MinecraftClient.IS_SYSTEM_MAC); - fb.copyDepthFrom(mcfb); - fb.beginWrite(false); - MaterialMatrixState.set(MaterialMatrixState.REGION, null); renderTerrainLayer(true, matrixStack, cameraX, cameraY, cameraZ); MaterialMatrixState.set(MaterialMatrixState.ENTITY, matrixStack.peek().getNormal()); @@ -740,9 +739,9 @@ public void renderWorld(MatrixStack matrixStack, float tickDelta, long limitTime // and other translucent elements get drawn on top of terrain immediate.draw(RenderLayer.getLines()); - immediate.drawCollectors(true); + immediate.drawCollectors(MaterialTarget.TRANSLUCENT); - // This presumably catches any remaining translucent layers in vanilla + // This catches entity layer and any remaining non-main layers immediate.draw(); VoxelMapHolder.postRenderLayerHandler.render(this, RenderLayer.getTranslucent(), matrixStack, cameraX, cameraY, cameraZ); diff --git a/src/main/java/grondag/canvas/shader/MaterialShaderImpl.java b/src/main/java/grondag/canvas/shader/MaterialShaderImpl.java index 433ac7d40..e61a276d5 100644 --- a/src/main/java/grondag/canvas/shader/MaterialShaderImpl.java +++ b/src/main/java/grondag/canvas/shader/MaterialShaderImpl.java @@ -24,6 +24,9 @@ public final class MaterialShaderImpl implements MaterialShader { public final int index; public final int vertexShaderIndex; public final int fragmentShaderIndex; + public final String vertexShaderSource; + public final String fragmentShaderSource; + public final ProgramType programType; private GlProgram program; @@ -32,6 +35,8 @@ public MaterialShaderImpl(int index, int vertexShaderIndex, int fragmentShaderIn this.fragmentShaderIndex = fragmentShaderIndex; this.programType = programType; this.index = index; + vertexShaderSource = MaterialShaderManager.VERTEX_INDEXER.fromHandle(vertexShaderIndex).toString(); + fragmentShaderSource = MaterialShaderManager.FRAGMENT_INDEXER.fromHandle(fragmentShaderIndex).toString(); } private GlProgram getOrCreate() { @@ -95,4 +100,11 @@ public void onGameTick() { program.onGameTick(); } } + + @Override + public String toString() { + return "index: " + index + " type: " + programType.name + + " vertex: " + vertexShaderSource + "(" + vertexShaderIndex + + ") fragment: " + fragmentShaderSource + "(" + fragmentShaderIndex + ")"; + } }