From 39a9c45b25c6bc3aa1d6a876cf6eb37823396485 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 15 Feb 2025 12:45:29 -0800 Subject: [PATCH 01/23] Just not my type - Remove VisualType - Engines now render everything in a single pass, and instancers can be shared between entities, block entities, and effects --- .../flywheel/api/backend/Engine.java | 7 ++-- .../api/visualization/VisualType.java | 7 ---- .../visualization/VisualizationManager.java | 4 --- .../flywheel/backend/engine/DrawManager.java | 7 ++-- .../flywheel/backend/engine/EngineImpl.java | 21 +++++------- .../flywheel/backend/engine/InstancerKey.java | 4 +-- .../backend/engine/InstancerProviderImpl.java | 5 ++- .../engine/embed/EmbeddedEnvironment.java | 13 +++---- .../engine/indirect/IndirectCullingGroup.java | 31 ++++------------- .../backend/engine/indirect/IndirectDraw.java | 9 +---- .../engine/indirect/IndirectDrawManager.java | 18 ++-------- .../instancing/InstancedDrawManager.java | 28 +++++---------- .../impl/mixin/LevelRendererMixin.java | 34 ------------------- .../VisualizationManagerImpl.java | 24 ++++--------- 14 files changed, 47 insertions(+), 165 deletions(-) delete mode 100644 common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualType.java diff --git a/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java b/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java index 8c65ec5a1..1df77bff5 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java @@ -8,7 +8,6 @@ import org.jetbrains.annotations.Range; import dev.engine_room.flywheel.api.RenderContext; import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.task.Plan; -import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.api.visualization.VisualizationContext; import it.unimi.dsi.fastutil.longs.LongSet; import net.minecraft.client.Camera; @@ -22,10 +21,9 @@ public interface Engine { /** * Create a visualization context that will be used to create visuals of the given type. * - * @param visualType The type of visual. * @return A new visualization context. */ - VisualizationContext createVisualizationContext(VisualType visualType); + VisualizationContext createVisualizationContext(); /** * Create a plan that will start execution after the start of the level render and @@ -78,9 +76,8 @@ public interface Engine { * level render. This method is guaranteed to be called on the render thread. * * @param context The context for the current level render. - * @param visualType The type of visual. */ - void render(RenderContext context, VisualType visualType); + void render(RenderContext context); /** * Render the given instances as a crumbling overlay. diff --git a/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualType.java b/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualType.java deleted file mode 100644 index 486eaa532..000000000 --- a/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualType.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.engine_room.flywheel.api.visualization; - -public enum VisualType { - BLOCK_ENTITY, - ENTITY, - EFFECT; -} diff --git a/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java b/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java index 2579d5f86..512ea1439 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java @@ -49,12 +49,8 @@ public interface VisualizationManager { interface RenderDispatcher { void onStartLevelRender(RenderContext ctx); - void afterBlockEntities(RenderContext ctx); - void afterEntities(RenderContext ctx); void beforeCrumbling(RenderContext ctx, Long2ObjectMap> destructionProgress); - - void afterParticles(RenderContext ctx); } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java index 9f5c4e2ed..bf4d10bee 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java @@ -18,7 +18,6 @@ import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.model.Model; import dev.engine_room.flywheel.api.task.Plan; -import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.backend.FlwBackend; import dev.engine_room.flywheel.backend.engine.embed.Environment; import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage; @@ -43,8 +42,8 @@ public abstract class DrawManager> { */ protected final Queue> initializationQueue = new ConcurrentLinkedQueue<>(); - public AbstractInstancer getInstancer(Environment environment, InstanceType type, Model model, VisualType visualType, int bias) { - return getInstancer(new InstancerKey<>(environment, type, model, visualType, bias)); + public AbstractInstancer getInstancer(Environment environment, InstanceType type, Model model, int bias) { + return getInstancer(new InstancerKey<>(environment, type, model, bias)); } @SuppressWarnings("unchecked") @@ -76,7 +75,7 @@ public abstract class DrawManager> { .forEach(AbstractInstancer::clear); } - public abstract void render(VisualType visualType); + public abstract void render(); public abstract void renderCrumbling(List crumblingBlocks); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java index 47b58fae5..330915bd9 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java @@ -13,7 +13,6 @@ import dev.engine_room.flywheel.api.instance.InstancerProvider; import dev.engine_room.flywheel.api.model.Model; import dev.engine_room.flywheel.api.task.Plan; import dev.engine_room.flywheel.api.visualization.VisualEmbedding; -import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.backend.FlwBackend; import dev.engine_room.flywheel.backend.compile.core.ShaderException; @@ -47,8 +46,8 @@ public class EngineImpl implements Engine { } @Override - public VisualizationContext createVisualizationContext(VisualType visualType) { - return new VisualizationContextImpl(visualType); + public VisualizationContext createVisualizationContext() { + return new VisualizationContextImpl(); } @Override @@ -104,9 +103,9 @@ public class EngineImpl implements Engine { } @Override - public void render(RenderContext context, VisualType visualType) { + public void render(RenderContext context) { try (var state = GlStateTracker.getRestoreState()) { - drawManager.render(visualType); + drawManager.render(); } catch (ShaderException e) { FlwBackend.LOGGER.error("Falling back", e); triggerFallback(); @@ -134,8 +133,8 @@ public class EngineImpl implements Engine { drawManager.triggerFallback(); } - public Instancer instancer(Environment environment, InstanceType type, Model model, VisualType visualType, int bias) { - return drawManager.getInstancer(environment, type, model, visualType, bias); + public Instancer instancer(Environment environment, InstanceType type, Model model, int bias) { + return drawManager.getInstancer(environment, type, model, bias); } public EnvironmentStorage environmentStorage() { @@ -148,11 +147,9 @@ public class EngineImpl implements Engine { private class VisualizationContextImpl implements VisualizationContext { private final InstancerProviderImpl instancerProvider; - private final VisualType visualType; - public VisualizationContextImpl(VisualType visualType) { - instancerProvider = new InstancerProviderImpl(EngineImpl.this, visualType); - this.visualType = visualType; + public VisualizationContextImpl() { + instancerProvider = new InstancerProviderImpl(EngineImpl.this); } @Override @@ -167,7 +164,7 @@ public class EngineImpl implements Engine { @Override public VisualEmbedding createEmbedding(Vec3i renderOrigin) { - var out = new EmbeddedEnvironment(EngineImpl.this, visualType, renderOrigin); + var out = new EmbeddedEnvironment(EngineImpl.this, renderOrigin); environmentStorage.track(out); return out; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java index 1910c9fbf..843d1075b 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java @@ -3,9 +3,7 @@ package dev.engine_room.flywheel.backend.engine; import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.model.Model; -import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.backend.engine.embed.Environment; -public record InstancerKey(Environment environment, InstanceType type, Model model, - VisualType visualType, int bias) { +public record InstancerKey(Environment environment, InstanceType type, Model model, int bias) { } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java index 064a5388a..c7a70300a 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java @@ -5,12 +5,11 @@ import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.instance.Instancer; import dev.engine_room.flywheel.api.instance.InstancerProvider; import dev.engine_room.flywheel.api.model.Model; -import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.backend.engine.embed.GlobalEnvironment; -public record InstancerProviderImpl(EngineImpl engine, VisualType visualType) implements InstancerProvider { +public record InstancerProviderImpl(EngineImpl engine) implements InstancerProvider { @Override public Instancer instancer(InstanceType type, Model model, int bias) { - return engine.instancer(GlobalEnvironment.INSTANCE, type, model, visualType, bias); + return engine.instancer(GlobalEnvironment.INSTANCE, type, model, bias); } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java index 09dff65c8..e45dceec0 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java @@ -12,7 +12,6 @@ import dev.engine_room.flywheel.api.instance.Instancer; import dev.engine_room.flywheel.api.instance.InstancerProvider; import dev.engine_room.flywheel.api.model.Model; import dev.engine_room.flywheel.api.visualization.VisualEmbedding; -import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.backend.compile.ContextShader; import dev.engine_room.flywheel.backend.engine.EngineImpl; import dev.engine_room.flywheel.backend.gl.shader.GlProgram; @@ -21,7 +20,6 @@ import net.minecraft.core.Vec3i; public class EmbeddedEnvironment implements VisualEmbedding, Environment { private final EngineImpl engine; - private final VisualType visualType; private final Vec3i renderOrigin; @Nullable private final EmbeddedEnvironment parent; @@ -36,9 +34,8 @@ public class EmbeddedEnvironment implements VisualEmbedding, Environment { private boolean deleted = false; - public EmbeddedEnvironment(EngineImpl engine, VisualType visualType, Vec3i renderOrigin, @Nullable EmbeddedEnvironment parent) { + public EmbeddedEnvironment(EngineImpl engine, Vec3i renderOrigin, @Nullable EmbeddedEnvironment parent) { this.engine = engine; - this.visualType = visualType; this.renderOrigin = renderOrigin; this.parent = parent; @@ -46,13 +43,13 @@ public class EmbeddedEnvironment implements VisualEmbedding, Environment { @Override public Instancer instancer(InstanceType type, Model model, int bias) { // Kinda cursed usage of anonymous classes here, but it does the job. - return engine.instancer(EmbeddedEnvironment.this, type, model, visualType, bias); + return engine.instancer(EmbeddedEnvironment.this, type, model, bias); } }; } - public EmbeddedEnvironment(EngineImpl engine, VisualType visualType, Vec3i renderOrigin) { - this(engine, visualType, renderOrigin, null); + public EmbeddedEnvironment(EngineImpl engine, Vec3i renderOrigin) { + this(engine, renderOrigin, null); } @Override @@ -73,7 +70,7 @@ public class EmbeddedEnvironment implements VisualEmbedding, Environment { @Override public VisualEmbedding createEmbedding(Vec3i renderOrigin) { - var out = new EmbeddedEnvironment(engine, visualType, renderOrigin, this); + var out = new EmbeddedEnvironment(engine, renderOrigin, this); engine.environmentStorage() .track(out); return out; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java index 9b56968d2..faec3b63a 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -8,15 +8,12 @@ import static org.lwjgl.opengl.GL43.glDispatchCompute; import java.util.ArrayList; import java.util.Comparator; -import java.util.EnumMap; import java.util.List; -import java.util.Map; import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.material.Material; import dev.engine_room.flywheel.api.model.Model; -import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.backend.compile.ContextShader; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; import dev.engine_room.flywheel.backend.engine.InstancerKey; @@ -28,8 +25,7 @@ import dev.engine_room.flywheel.backend.gl.shader.GlProgram; import dev.engine_room.flywheel.lib.math.MoreMath; public class IndirectCullingGroup { - private static final Comparator DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::visualType) - .thenComparing(IndirectDraw::isEmbedded) + private static final Comparator DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::isEmbedded) .thenComparing(IndirectDraw::bias) .thenComparing(IndirectDraw::indexOfMeshInModel) .thenComparing(IndirectDraw::material, MaterialRenderState.COMPARATOR); @@ -39,7 +35,7 @@ public class IndirectCullingGroup { private final IndirectBuffers buffers; private final List> instancers = new ArrayList<>(); private final List indirectDraws = new ArrayList<>(); - private final Map> multiDraws = new EnumMap<>(VisualType.class); + private final List multiDraws = new ArrayList<>(); private final IndirectPrograms programs; private final GlProgram cullProgram; @@ -132,10 +128,6 @@ public class IndirectCullingGroup { return indirectDraws.isEmpty() || instanceCountThisFrame == 0; } - private boolean nothingToDo(VisualType visualType) { - return nothingToDo() || !multiDraws.containsKey(visualType); - } - private void sortDraws() { multiDraws.clear(); // sort by visual type, then material @@ -146,28 +138,19 @@ public class IndirectCullingGroup { // if the next draw call has a different VisualType or Material, start a new MultiDraw if (i == indirectDraws.size() - 1 || incompatibleDraws(draw1, indirectDraws.get(i + 1))) { - multiDraws.computeIfAbsent(draw1.visualType(), s -> new ArrayList<>()) - .add(new MultiDraw(draw1.material(), draw1.isEmbedded(), start, i + 1)); + multiDraws.add(new MultiDraw(draw1.material(), draw1.isEmbedded(), start, i + 1)); start = i + 1; } } } private boolean incompatibleDraws(IndirectDraw draw1, IndirectDraw draw2) { - if (draw1.visualType() != draw2.visualType()) { - return true; - } - if (draw1.isEmbedded() != draw2.isEmbedded()) { return true; } return !MaterialRenderState.materialEquals(draw1.material(), draw2.material()); } - public boolean hasVisualType(VisualType visualType) { - return multiDraws.containsKey(visualType); - } - public void add(IndirectInstancer instancer, InstancerKey key, MeshPool meshPool) { instancer.mapping = buffers.objectStorage.createMapping(); instancer.update(instancers.size(), -1); @@ -180,7 +163,7 @@ public class IndirectCullingGroup { var entry = meshes.get(i); MeshPool.PooledMesh mesh = meshPool.alloc(entry.mesh()); - var draw = new IndirectDraw(instancer, entry.material(), mesh, key.visualType(), key.bias(), i); + var draw = new IndirectDraw(instancer, entry.material(), mesh, key.bias(), i); indirectDraws.add(draw); instancer.addDraw(draw); } @@ -188,8 +171,8 @@ public class IndirectCullingGroup { needsDrawSort = true; } - public void submit(VisualType visualType) { - if (nothingToDo(visualType)) { + public void submit() { + if (nothingToDo()) { return; } @@ -199,7 +182,7 @@ public class IndirectCullingGroup { GlProgram lastProgram = null; - for (var multiDraw : multiDraws.get(visualType)) { + for (var multiDraw : multiDraws) { var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material); if (drawProgram != lastProgram) { lastProgram = drawProgram; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java index 3ac62d3af..a01769ea7 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java @@ -3,7 +3,6 @@ package dev.engine_room.flywheel.backend.engine.indirect; import org.lwjgl.system.MemoryUtil; import dev.engine_room.flywheel.api.material.Material; -import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.backend.engine.MaterialEncoder; import dev.engine_room.flywheel.backend.engine.MeshPool; import dev.engine_room.flywheel.backend.engine.embed.EmbeddedEnvironment; @@ -12,7 +11,6 @@ public class IndirectDraw { private final IndirectInstancer instancer; private final Material material; private final MeshPool.PooledMesh mesh; - private final VisualType visualType; private final int bias; private final int indexOfMeshInModel; @@ -20,11 +18,10 @@ public class IndirectDraw { private final int packedMaterialProperties; private boolean deleted; - public IndirectDraw(IndirectInstancer instancer, Material material, MeshPool.PooledMesh mesh, VisualType visualType, int bias, int indexOfMeshInModel) { + public IndirectDraw(IndirectInstancer instancer, Material material, MeshPool.PooledMesh mesh, int bias, int indexOfMeshInModel) { this.instancer = instancer; this.material = material; this.mesh = mesh; - this.visualType = visualType; this.bias = bias; this.indexOfMeshInModel = indexOfMeshInModel; @@ -50,10 +47,6 @@ public class IndirectDraw { return mesh; } - public VisualType visualType() { - return visualType; - } - public int bias() { return bias; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index 7fab5d55e..6ac442dab 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -15,7 +15,6 @@ import java.util.Map; import dev.engine_room.flywheel.api.backend.Engine; import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.instance.InstanceType; -import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.backend.Samplers; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; import dev.engine_room.flywheel.backend.engine.AbstractInstancer; @@ -79,20 +78,7 @@ public class IndirectDrawManager extends DrawManager> { group.add((IndirectInstancer) instancer, key, meshPool); } - public boolean hasVisualType(VisualType visualType) { - for (var group : cullingGroups.values()) { - if (group.hasVisualType(visualType)) { - return true; - } - } - return false; - } - - public void render(VisualType visualType) { - if (!hasVisualType(visualType)) { - return; - } - + public void render() { TextureBinder.bindLightAndOverlay(); vertexArray.bindForDraw(); @@ -106,7 +92,7 @@ public class IndirectDrawManager extends DrawManager> { } for (var group : cullingGroups.values()) { - group.submit(visualType); + group.submit(); } MaterialRenderState.reset(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java index 88526f360..7a798e0d3 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java @@ -1,13 +1,10 @@ package dev.engine_room.flywheel.backend.engine.instancing; -import java.util.EnumMap; import java.util.List; -import java.util.Map; import dev.engine_room.flywheel.api.backend.Engine; import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.material.Material; -import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.backend.Samplers; import dev.engine_room.flywheel.backend.compile.ContextShader; import dev.engine_room.flywheel.backend.compile.InstancingPrograms; @@ -31,10 +28,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.resources.model.ModelBakery; public class InstancedDrawManager extends DrawManager> { - /** - * The set of draw calls to make for each {@link VisualType}. - */ - private final Map stages = new EnumMap<>(VisualType.class); + private final InstancedRenderStage draws = new InstancedRenderStage(); private final InstancingPrograms programs; /** * A map of vertex types to their mesh pools. @@ -71,10 +65,8 @@ public class InstancedDrawManager extends DrawManager> { } }); - for (InstancedRenderStage stage : stages.values()) { - // Remove the draw calls for any instancers we deleted. - stage.flush(); - } + // Remove the draw calls for any instancers we deleted. + draws.flush(); meshPool.flush(); @@ -82,10 +74,10 @@ public class InstancedDrawManager extends DrawManager> { } @Override - public void render(VisualType visualType) { - var stage = stages.get(visualType); + public void render() { + var stage = draws; - if (stage == null || stage.isEmpty()) { + if (stage.isEmpty()) { return; } @@ -105,9 +97,7 @@ public class InstancedDrawManager extends DrawManager> { instancers.values() .forEach(InstancedInstancer::delete); - stages.values() - .forEach(InstancedRenderStage::delete); - stages.clear(); + draws.delete(); meshPool.delete(); instanceTexture.delete(); @@ -128,8 +118,6 @@ public class InstancedDrawManager extends DrawManager> { protected void initialize(InstancerKey key, InstancedInstancer instancer) { instancer.init(); - InstancedRenderStage stage = stages.computeIfAbsent(key.visualType(), $ -> new InstancedRenderStage()); - var meshes = key.model() .meshes(); for (int i = 0; i < meshes.size(); i++) { @@ -139,7 +127,7 @@ public class InstancedDrawManager extends DrawManager> { GroupKey groupKey = new GroupKey<>(key.type(), key.environment()); InstancedDraw instancedDraw = new InstancedDraw(instancer, mesh, groupKey, entry.material(), key.bias(), i); - stage.put(groupKey, instancedDraw); + draws.put(groupKey, instancedDraw); instancer.addDrawCall(instancedDraw); } } diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/mixin/LevelRendererMixin.java b/common/src/main/java/dev/engine_room/flywheel/impl/mixin/LevelRendererMixin.java index c7714b228..e0ba70f49 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/mixin/LevelRendererMixin.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/mixin/LevelRendererMixin.java @@ -9,8 +9,6 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.At.Shift; -import org.spongepowered.asm.mixin.injection.Group; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -82,16 +80,6 @@ abstract class LevelRendererMixin { } } - @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/OutlineBufferSource;endOutlineBatch()V", ordinal = 0)) - private void flywheel$afterBlockEntities(CallbackInfo ci) { - if (flywheel$renderContext != null) { - VisualizationManager manager = VisualizationManager.get(level); - if (manager != null) { - manager.renderDispatcher().afterBlockEntities(flywheel$renderContext); - } - } - } - @Inject(method = "renderLevel", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", args = "ldc=destroyProgress")) private void flywheel$beforeRenderCrumbling(CallbackInfo ci) { if (flywheel$renderContext != null) { @@ -102,28 +90,6 @@ abstract class LevelRendererMixin { } } - @Group(name = "afterParticles", min = 2, max = 2) - @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleEngine;render(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/client/Camera;F)V", shift = Shift.AFTER)) - private void flywheel$afterParticles$fabric(CallbackInfo ci) { - if (flywheel$renderContext != null) { - VisualizationManager manager = VisualizationManager.get(level); - if (manager != null) { - manager.renderDispatcher().afterParticles(flywheel$renderContext); - } - } - } - - @Group(name = "afterParticles") - @Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleEngine;render(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/client/Camera;FLnet/minecraft/client/renderer/culling/Frustum;)V", shift = Shift.AFTER)) - private void flywheel$afterParticles$forge(CallbackInfo ci) { - if (flywheel$renderContext != null) { - VisualizationManager manager = VisualizationManager.get(level); - if (manager != null) { - manager.renderDispatcher().afterParticles(flywheel$renderContext); - } - } - } - @Inject(method = "renderEntity", at = @At("HEAD"), cancellable = true) private void flywheel$decideNotToRenderEntity(Entity entity, double camX, double camY, double camZ, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, CallbackInfo ci) { if (VisualizationManager.supportsVisualization(entity.level()) && VisualizationHelper.skipVanillaRender(entity)) { diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java index 2683bcc8d..ba91bc73e 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java @@ -18,7 +18,6 @@ import dev.engine_room.flywheel.api.visual.DynamicVisual; import dev.engine_room.flywheel.api.visual.Effect; import dev.engine_room.flywheel.api.visual.TickableVisual; import dev.engine_room.flywheel.api.visualization.VisualManager; -import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.api.visualization.VisualizationLevel; import dev.engine_room.flywheel.api.visualization.VisualizationManager; import dev.engine_room.flywheel.impl.FlwConfig; @@ -82,9 +81,10 @@ public class VisualizationManagerImpl implements VisualizationManager { .createEngine(level); frameLimiter = createUpdateLimiter(); - var blockEntitiesStorage = new BlockEntityStorage(engine.createVisualizationContext(VisualType.BLOCK_ENTITY)); - var entitiesStorage = new EntityStorage(engine.createVisualizationContext(VisualType.ENTITY)); - var effectsStorage = new EffectStorage(engine.createVisualizationContext(VisualType.EFFECT)); + var visualizationContext = engine.createVisualizationContext(); + var blockEntitiesStorage = new BlockEntityStorage(visualizationContext); + var entitiesStorage = new EntityStorage(visualizationContext); + var effectsStorage = new EffectStorage(visualizationContext); blockEntities = new VisualManagerImpl<>(blockEntitiesStorage); entities = new VisualManagerImpl<>(entitiesStorage); @@ -257,9 +257,9 @@ public class VisualizationManagerImpl implements VisualizationManager { /** * Draw all visuals of the given type. */ - private void render(RenderContext context, VisualType visualType) { + private void render(RenderContext context) { ensureCanRender(context); - engine.render(context, visualType); + engine.render(context); } private void renderCrumbling(RenderContext context, Long2ObjectMap> destructionProgress) { @@ -337,25 +337,15 @@ public class VisualizationManagerImpl implements VisualizationManager { beginFrame(ctx); } - @Override - public void afterBlockEntities(RenderContext ctx) { - render(ctx, VisualType.BLOCK_ENTITY); - } - @Override public void afterEntities(RenderContext ctx) { - render(ctx, VisualType.ENTITY); + render(ctx); } @Override public void beforeCrumbling(RenderContext ctx, Long2ObjectMap> destructionProgress) { renderCrumbling(ctx, destructionProgress); } - - @Override - public void afterParticles(RenderContext ctx) { - render(ctx, VisualType.EFFECT); - } } private record CrumblingBlockImpl(BlockPos pos, int progress, List instances) implements Engine.CrumblingBlock { From a2fc11149962beb1025ab183feedd52e2064f26f Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 15 Feb 2025 13:05:02 -0800 Subject: [PATCH 02/23] More hooks more problems - Remove Engine#setupRender - Combine DrawManager#flush and DrawManager#render to simplify engines - Document the hooks in RenderDispatcher --- .../flywheel/api/backend/Engine.java | 17 ++------ .../visualization/VisualizationManager.java | 21 +++++++++ .../flywheel/backend/engine/DrawManager.java | 6 +-- .../flywheel/backend/engine/EngineImpl.java | 14 +----- .../engine/indirect/IndirectDrawManager.java | 43 ++++++++----------- .../instancing/InstancedDrawManager.java | 13 ++---- .../VisualizationManagerImpl.java | 15 +------ 7 files changed, 50 insertions(+), 79 deletions(-) diff --git a/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java b/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java index 1df77bff5..9843d9094 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java @@ -58,22 +58,11 @@ public interface Engine { void onLightUpdate(SectionPos sectionPos, LightLayer layer); /** - * Set up rendering for the current level render. + * Render all instances necessary for the given visual type. * *

This method is guaranteed to be called after * {@linkplain #createFramePlan() the frame plan} has finished execution and before - * {@link #render} and {@link #renderCrumbling} are called. This method is guaranteed to - * be called on the render thread. - * - * @param context The context for the current level render. - */ - void setupRender(RenderContext context); - - /** - * Render all instances necessary for the given visual type. - * - *

This method is guaranteed to be called after {@link #setupRender} for the current - * level render. This method is guaranteed to be called on the render thread. + * {@link #renderCrumbling} are called. This method is guaranteed to be called on the render thread. * * @param context The context for the current level render. */ @@ -82,7 +71,7 @@ public interface Engine { /** * Render the given instances as a crumbling overlay. * - *

This method is guaranteed to be called after {@link #setupRender} for the current + *

This method is guaranteed to be called after {@link #render} for the current * level render. This method is guaranteed to be called on the render thread. * * @param context The context for the current level render. diff --git a/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java b/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java index 512ea1439..e7f831dbc 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java @@ -47,10 +47,31 @@ public interface VisualizationManager { @ApiStatus.NonExtendable interface RenderDispatcher { + /** + * Prepare visuals for render. + * + *

Guaranteed to be called before {@link #afterEntities} and {@link #beforeCrumbling}. + *
Guaranteed to be called after the render thread has processed all light updates. + *
The caller is otherwise free to choose an invocation site, but it is recommended to call + * this as early as possible to give the VisualizationManager time to process things off-thread. + */ void onStartLevelRender(RenderContext ctx); + /** + * Render instances. + * + *

Guaranteed to be called after {@link #onStartLevelRender} and before {@link #beforeCrumbling}. + *
The caller is otherwise free to choose an invocation site, but it is recommended to call + * this between rendering entities and block entities. + */ void afterEntities(RenderContext ctx); + /** + * Render crumbling block entities. + * + *

Guaranteed to be called after {@link #onStartLevelRender} and {@link #afterEntities} + * @param destructionProgress The destruction progress map from {@link net.minecraft.client.renderer.LevelRenderer LevelRenderer}. + */ void beforeCrumbling(RenderContext ctx, Long2ObjectMap> destructionProgress); } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java index bf4d10bee..996c900a2 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java @@ -38,7 +38,7 @@ public abstract class DrawManager> { /** * A list of instancers that have not yet been initialized. *
- * All new instancers land here before having resources allocated in {@link #flush}. + * All new instancers land here before having resources allocated in {@link #render}. */ protected final Queue> initializationQueue = new ConcurrentLinkedQueue<>(); @@ -56,7 +56,7 @@ public abstract class DrawManager> { return ForEachPlan.of(() -> new ArrayList<>(instancers.values()), AbstractInstancer::parallelUpdate); } - public void flush(LightStorage lightStorage, EnvironmentStorage environmentStorage) { + public void render(LightStorage lightStorage, EnvironmentStorage environmentStorage) { // Thread safety: flush is called from the render thread after all visual updates have been made, // so there are no:tm: threads we could be racing with. for (var init : initializationQueue) { @@ -75,8 +75,6 @@ public abstract class DrawManager> { .forEach(AbstractInstancer::clear); } - public abstract void render(); - public abstract void renderCrumbling(List crumblingBlocks); protected abstract N create(InstancerKey type); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java index 330915bd9..a5c83d33d 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java @@ -89,23 +89,13 @@ public class EngineImpl implements Engine { } @Override - public void setupRender(RenderContext context) { + public void render(RenderContext context) { try (var state = GlStateTracker.getRestoreState()) { // Process the render queue for font updates RenderSystem.replayQueue(); Uniforms.update(context); environmentStorage.flush(); - drawManager.flush(lightStorage, environmentStorage); - } catch (ShaderException e) { - FlwBackend.LOGGER.error("Falling back", e); - triggerFallback(); - } - } - - @Override - public void render(RenderContext context) { - try (var state = GlStateTracker.getRestoreState()) { - drawManager.render(); + drawManager.render(lightStorage, environmentStorage); } catch (ShaderException e) { FlwBackend.LOGGER.error("Falling back", e); triggerFallback(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index 6ac442dab..5cc280750 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -48,8 +48,6 @@ public class IndirectDrawManager extends DrawManager> { private final DepthPyramid depthPyramid; - private boolean needsBarrier = false; - public IndirectDrawManager(IndirectPrograms programs) { this.programs = programs; programs.acquire(); @@ -78,30 +76,9 @@ public class IndirectDrawManager extends DrawManager> { group.add((IndirectInstancer) instancer, key, meshPool); } - public void render() { - TextureBinder.bindLightAndOverlay(); - - vertexArray.bindForDraw(); - lightBuffers.bind(); - matrixBuffer.bind(); - Uniforms.bindAll(); - - if (needsBarrier) { - glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); - needsBarrier = false; - } - - for (var group : cullingGroups.values()) { - group.submit(); - } - - MaterialRenderState.reset(); - TextureBinder.resetLightAndOverlay(); - } - @Override - public void flush(LightStorage lightStorage, EnvironmentStorage environmentStorage) { - super.flush(lightStorage, environmentStorage); + public void render(LightStorage lightStorage, EnvironmentStorage environmentStorage) { + super.render(lightStorage, environmentStorage); for (var group : cullingGroups.values()) { group.flushInstancers(); @@ -151,7 +128,21 @@ public class IndirectDrawManager extends DrawManager> { group.dispatchApply(); } - needsBarrier = true; + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + + TextureBinder.bindLightAndOverlay(); + + vertexArray.bindForDraw(); + lightBuffers.bind(); + matrixBuffer.bind(); + Uniforms.bindAll(); + + for (var group : cullingGroups.values()) { + group.submit(); + } + + MaterialRenderState.reset(); + TextureBinder.resetLightAndOverlay(); } @Override diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java index 7a798e0d3..e2a97ddf7 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java @@ -51,8 +51,8 @@ public class InstancedDrawManager extends DrawManager> { } @Override - public void flush(LightStorage lightStorage, EnvironmentStorage environmentStorage) { - super.flush(lightStorage, environmentStorage); + public void render(LightStorage lightStorage, EnvironmentStorage environmentStorage) { + super.render(lightStorage, environmentStorage); this.instancers.values() .removeIf(instancer -> { @@ -71,13 +71,8 @@ public class InstancedDrawManager extends DrawManager> { meshPool.flush(); light.flush(lightStorage); - } - @Override - public void render() { - var stage = draws; - - if (stage.isEmpty()) { + if (draws.isEmpty()) { return; } @@ -86,7 +81,7 @@ public class InstancedDrawManager extends DrawManager> { TextureBinder.bindLightAndOverlay(); light.bind(); - stage.draw(instanceTexture, programs); + draws.draw(instanceTexture, programs); MaterialRenderState.reset(); TextureBinder.resetLightAndOverlay(); diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java index ba91bc73e..5eeadf043 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java @@ -73,8 +73,6 @@ public class VisualizationManagerImpl implements VisualizationManager { private final Plan framePlan; private final Plan tickPlan; - private boolean canEngineRender; - private VisualizationManagerImpl(LevelAccessor level) { taskExecutor = FlwTaskExecutor.get(); engine = BackendManager.currentBackend() @@ -241,24 +239,15 @@ public class VisualizationManagerImpl implements VisualizationManager { frameFlag.lower(); frameLimiter.tick(); - canEngineRender = false; framePlan.execute(taskExecutor, context); } - private void ensureCanRender(RenderContext context) { - taskExecutor.syncUntil(frameFlag::isRaised); - if (!canEngineRender) { - engine.setupRender(context); - canEngineRender = true; - } - } - /** * Draw all visuals of the given type. */ private void render(RenderContext context) { - ensureCanRender(context); + taskExecutor.syncUntil(frameFlag::isRaised); engine.render(context); } @@ -267,8 +256,6 @@ public class VisualizationManagerImpl implements VisualizationManager { return; } - ensureCanRender(context); - List crumblingBlocks = new ArrayList<>(); for (var entry : destructionProgress.long2ObjectEntrySet()) { From 68ed161269da6c5070e61f7c7316bae9d75e313c Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 15 Feb 2025 13:11:34 -0800 Subject: [PATCH 03/23] Doc a point for this one - Fix error in Engine#createFramePlan documentation --- .../api/java/dev/engine_room/flywheel/api/backend/Engine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java b/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java index 9843d9094..6f9d0a93a 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java @@ -27,7 +27,7 @@ public interface Engine { /** * Create a plan that will start execution after the start of the level render and - * finish execution before {@link #setupRender} is called. + * finish execution before {@link #render} is called. * * @return A new plan. */ From c438fb57cafc1c179dbc2d2cec2c57bfdc8ef529 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 15 Feb 2025 15:51:30 -0800 Subject: [PATCH 04/23] Atomic fusion before Flywheel 1.0 - Add workaround for race condition flipping the "mergeable" bit for instance pages - Sometimes a page would end up full while another thread was racing to set the mergeable bit, so when we later tried to merge pages we'd get an array index out of bounds - This change makes it so we only ever set the mergeable bit and handle the case of a full page when trying to merge - Should also help us avoid the alternative case where a page that is actually mergeable isn't marked as such --- .../engine/indirect/IndirectInstancer.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectInstancer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectInstancer.java index aa9b372f1..499825bbb 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectInstancer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectInstancer.java @@ -141,12 +141,8 @@ public class IndirectInstancer extends AbstractInstancer // This is safe because only one bit position changes at a time. parent.fullPages.set(pageNo); } - if (isEmpty(currentValue)) { - // Value we just saw was zero, so since we added something we are now mergeable! + if (isMergeable(newValue)) { parent.mergeablePages.set(pageNo); - } else if (Integer.bitCount(currentValue) == 16) { - // We just filled the 17th instance, so we are no longer mergeable. - parent.mergeablePages.clear(pageNo); } parent.instanceCount.incrementAndGet(); @@ -199,11 +195,7 @@ public class IndirectInstancer extends AbstractInstancer if (valid.compareAndSet(currentValue, newValue)) { parent.validityChanged.set(pageNo); - if (isEmpty(newValue)) { - // If we decremented to zero then we're no longer mergeable. - parent.mergeablePages.clear(pageNo); - } else if (Integer.bitCount(newValue) == 16) { - // If we decremented to 16 then we're now mergeable. + if (isMergeable(newValue)) { parent.mergeablePages.set(pageNo); } // Set full page last so that other threads don't race to set the other bitsets. @@ -223,6 +215,13 @@ public class IndirectInstancer extends AbstractInstancer // Fill the holes in this page with instances from the other page. int valid = this.valid.get(); + + if (isFull(valid)) { + // We got filled after being marked mergeable, nothing to do + parent.mergeablePages.clear(pageNo); + return; + } + int otherValid = other.valid.get(); for (int i = 0; i < ObjectStorage.PAGE_SIZE; i++) { From 5d16ebda6033c018ced0209358ef653cba11d932 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 15 Feb 2025 18:13:16 -0800 Subject: [PATCH 05/23] Wbout it - Implement WBOIT --- .../backend/compile/IndirectPrograms.java | 31 ++++- .../backend/compile/InstancingPrograms.java | 2 +- .../backend/compile/PipelineCompiler.java | 15 ++- .../backend/engine/MaterialRenderState.java | 11 ++ .../engine/indirect/IndirectCullingGroup.java | 39 +++++- .../engine/indirect/IndirectDrawManager.java | 14 ++- .../engine/indirect/WboitFrameBuffer.java | 117 ++++++++++++++++++ .../flywheel/flywheel/internal/common.frag | 45 ++++++- .../internal/indirect/fullscreen.vert | 4 + .../internal/indirect/oit_composite.frag | 48 +++++++ 10 files changed, 310 insertions(+), 16 deletions(-) create mode 100644 common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/WboitFrameBuffer.java create mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/fullscreen.vert create mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java index 39d211ef9..9d3ce83e0 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java @@ -31,7 +31,9 @@ public class IndirectPrograms extends AtomicReferenceCounted { private static final ResourceLocation SCATTER_SHADER_MAIN = Flywheel.rl("internal/indirect/scatter.glsl"); private static final ResourceLocation DOWNSAMPLE_FIRST = Flywheel.rl("internal/indirect/downsample_first.glsl"); private static final ResourceLocation DOWNSAMPLE_SECOND = Flywheel.rl("internal/indirect/downsample_second.glsl"); - public static final List UTIL_SHADERS = List.of(APPLY_SHADER_MAIN, SCATTER_SHADER_MAIN, DOWNSAMPLE_FIRST, DOWNSAMPLE_SECOND); + + private static final ResourceLocation FULLSCREEN = Flywheel.rl("internal/indirect/fullscreen.vert"); + private static final ResourceLocation OIT_COMPOSITE = Flywheel.rl("internal/indirect/oit_composite.frag"); private static final Compile> CULL = new Compile<>(); private static final Compile UTIL = new Compile<>(); @@ -45,11 +47,13 @@ public class IndirectPrograms extends AtomicReferenceCounted { private final PipelineCompiler pipeline; private final CompilationHarness> culling; private final CompilationHarness utils; + private final CompilationHarness fullscreen; - private IndirectPrograms(PipelineCompiler pipeline, CompilationHarness> culling, CompilationHarness utils) { + private IndirectPrograms(PipelineCompiler pipeline, CompilationHarness> culling, CompilationHarness utils, CompilationHarness fullscreen) { this.pipeline = pipeline; this.culling = culling; this.utils = utils; + this.fullscreen = fullscreen; } private static List getExtensions(GlslVersion glslVersion) { @@ -88,8 +92,9 @@ public class IndirectPrograms extends AtomicReferenceCounted { var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INDIRECT, vertexComponents, fragmentComponents, EXTENSIONS); var cullingCompiler = createCullingCompiler(sources); var utilCompiler = createUtilCompiler(sources); + var fullscreenCompiler = createFullscreenCompiler(sources); - IndirectPrograms newInstance = new IndirectPrograms(pipelineCompiler, cullingCompiler, utilCompiler); + IndirectPrograms newInstance = new IndirectPrograms(pipelineCompiler, cullingCompiler, utilCompiler, fullscreenCompiler); setInstance(newInstance); } @@ -125,6 +130,17 @@ public class IndirectPrograms extends AtomicReferenceCounted { .harness("utilities", sources); } + private static CompilationHarness createFullscreenCompiler(ShaderSources sources) { + return UTIL.program() + .link(UTIL.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.VERTEX) + .nameMapper($ -> "fullscreen/fullscreen") + .withResource(FULLSCREEN)) + .link(UTIL.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.FRAGMENT) + .nameMapper(rl -> "fullscreen/" + ResourceUtil.toDebugFileNameNoExtension(rl)) + .withResource(s -> s)) + .harness("fullscreen", sources); + } + static void setInstance(@Nullable IndirectPrograms newInstance) { if (instance != null) { instance.release(); @@ -148,8 +164,8 @@ public class IndirectPrograms extends AtomicReferenceCounted { setInstance(null); } - public GlProgram getIndirectProgram(InstanceType instanceType, ContextShader contextShader, Material material) { - return pipeline.get(instanceType, contextShader, material); + public GlProgram getIndirectProgram(InstanceType instanceType, ContextShader contextShader, Material material, boolean oit) { + return pipeline.get(instanceType, contextShader, material, oit); } public GlProgram getCullingProgram(InstanceType instanceType) { @@ -172,10 +188,15 @@ public class IndirectPrograms extends AtomicReferenceCounted { return utils.get(DOWNSAMPLE_SECOND); } + public GlProgram getOitCompositeProgram() { + return fullscreen.get(OIT_COMPOSITE); + } + @Override protected void _delete() { pipeline.delete(); culling.delete(); utils.delete(); + fullscreen.delete(); } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java index c1e736b1e..db3014402 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java @@ -70,7 +70,7 @@ public class InstancingPrograms extends AtomicReferenceCounted { } public GlProgram get(InstanceType instanceType, ContextShader contextShader, Material material) { - return pipeline.get(instanceType, contextShader, material); + return pipeline.get(instanceType, contextShader, material, false); } @Override diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java index e752edfd2..e315dce35 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java @@ -50,7 +50,7 @@ public final class PipelineCompiler { ALL.add(this); } - public GlProgram get(InstanceType instanceType, ContextShader contextShader, Material material) { + public GlProgram get(InstanceType instanceType, ContextShader contextShader, Material material, boolean oit) { var light = material.light(); var cutout = material.cutout(); var shaders = material.shaders(); @@ -66,7 +66,7 @@ public final class PipelineCompiler { MaterialShaderIndices.cutoutSources() .index(cutout.source()); - return harness.get(new PipelineProgramKey(instanceType, contextShader, light, shaders, cutout != CutoutShaders.OFF, FrameUniforms.debugOn())); + return harness.get(new PipelineProgramKey(instanceType, contextShader, light, shaders, cutout != CutoutShaders.OFF, FrameUniforms.debugOn(), oit)); } public void delete() { @@ -128,7 +128,8 @@ public final class PipelineCompiler { .source()); var debug = key.debugEnabled() ? "_debug" : ""; var cutout = key.useCutout() ? "_cutout" : ""; - return "pipeline/" + pipeline.compilerMarker() + "/frag/" + material + "/" + light + "_" + context + cutout + debug; + var oit = key.oit() ? "_oit" : ""; + return "pipeline/" + pipeline.compilerMarker() + "/frag/" + material + "/" + light + "_" + context + cutout + debug + oit; }) .requireExtensions(extensions) .enableExtension("GL_ARB_conservative_depth") @@ -146,6 +147,11 @@ public final class PipelineCompiler { comp.define("_FLW_USE_DISCARD"); } }) + .onCompile((key, comp) -> { + if (key.oit()) { + comp.define("_FLW_OIT"); + } + }) .withResource(API_IMPL_FRAG) .withResource(key -> key.materialShaders() .fragmentSource()) @@ -217,6 +223,7 @@ public final class PipelineCompiler { * @param light The light shader to use. */ public record PipelineProgramKey(InstanceType instanceType, ContextShader contextShader, LightShader light, - MaterialShaders materialShaders, boolean useCutout, boolean debugEnabled) { + MaterialShaders materialShaders, boolean useCutout, boolean debugEnabled, + boolean oit) { } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java index 43206153f..ec98d5c09 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java @@ -45,6 +45,17 @@ public final class MaterialRenderState { setupWriteMask(material.writeMask()); } + public static void setupOit(Material material) { + setupTexture(material); + setupBackfaceCulling(material.backfaceCulling()); + setupPolygonOffset(material.polygonOffset()); + setupDepthTest(material.depthTest()); + + WriteMask mask = material.writeMask(); + boolean writeColor = mask.color(); + RenderSystem.colorMask(writeColor, writeColor, writeColor, writeColor); + } + private static void setupTexture(Material material) { Samplers.DIFFUSE.makeActive(); AbstractTexture texture = Minecraft.getInstance() diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java index faec3b63a..aa605f017 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -13,6 +13,7 @@ import java.util.List; import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.material.Material; +import dev.engine_room.flywheel.api.material.Transparency; import dev.engine_room.flywheel.api.model.Model; import dev.engine_room.flywheel.backend.compile.ContextShader; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; @@ -36,6 +37,7 @@ public class IndirectCullingGroup { private final List> instancers = new ArrayList<>(); private final List indirectDraws = new ArrayList<>(); private final List multiDraws = new ArrayList<>(); + private final List transparentDraws = new ArrayList<>(); private final IndirectPrograms programs; private final GlProgram cullProgram; @@ -130,6 +132,7 @@ public class IndirectCullingGroup { private void sortDraws() { multiDraws.clear(); + transparentDraws.clear(); // sort by visual type, then material indirectDraws.sort(DRAW_COMPARATOR); @@ -138,7 +141,9 @@ public class IndirectCullingGroup { // if the next draw call has a different VisualType or Material, start a new MultiDraw if (i == indirectDraws.size() - 1 || incompatibleDraws(draw1, indirectDraws.get(i + 1))) { - multiDraws.add(new MultiDraw(draw1.material(), draw1.isEmbedded(), start, i + 1)); + var dst = draw1.material() + .transparency() == Transparency.TRANSLUCENT ? transparentDraws : multiDraws; + dst.add(new MultiDraw(draw1.material(), draw1.isEmbedded(), start, i + 1)); start = i + 1; } } @@ -171,7 +176,7 @@ public class IndirectCullingGroup { needsDrawSort = true; } - public void submit() { + public void submitSolid() { if (nothingToDo()) { return; } @@ -183,7 +188,7 @@ public class IndirectCullingGroup { GlProgram lastProgram = null; for (var multiDraw : multiDraws) { - var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material); + var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, false); if (drawProgram != lastProgram) { lastProgram = drawProgram; @@ -197,8 +202,34 @@ public class IndirectCullingGroup { } } + public void submitTransparent() { + if (nothingToDo()) { + return; + } + + buffers.bindForDraw(); + + drawBarrier(); + + GlProgram lastProgram = null; + + for (var multiDraw : transparentDraws) { + var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, true); + if (drawProgram != lastProgram) { + lastProgram = drawProgram; + + // Don't need to do this unless the program changes. + drawProgram.bind(); + } + + MaterialRenderState.setupOit(multiDraw.material); + + multiDraw.submit(drawProgram); + } + } + public void bindForCrumbling(Material material) { - var program = programs.getIndirectProgram(instanceType, ContextShader.CRUMBLING, material); + var program = programs.getIndirectProgram(instanceType, ContextShader.CRUMBLING, material, false); program.bind(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index 5cc280750..a61677865 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -48,6 +48,8 @@ public class IndirectDrawManager extends DrawManager> { private final DepthPyramid depthPyramid; + private final WboitFrameBuffer wboitFrameBuffer; + public IndirectDrawManager(IndirectPrograms programs) { this.programs = programs; programs.acquire(); @@ -62,6 +64,8 @@ public class IndirectDrawManager extends DrawManager> { matrixBuffer = new MatrixBuffer(); depthPyramid = new DepthPyramid(programs); + + wboitFrameBuffer = new WboitFrameBuffer(programs); } @Override @@ -138,9 +142,17 @@ public class IndirectDrawManager extends DrawManager> { Uniforms.bindAll(); for (var group : cullingGroups.values()) { - group.submit(); + group.submitSolid(); } + wboitFrameBuffer.setup(); + + for (var group : cullingGroups.values()) { + group.submitTransparent(); + } + + wboitFrameBuffer.composite(); + MaterialRenderState.reset(); TextureBinder.resetLightAndOverlay(); } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/WboitFrameBuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/WboitFrameBuffer.java new file mode 100644 index 000000000..c76133709 --- /dev/null +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/WboitFrameBuffer.java @@ -0,0 +1,117 @@ +package dev.engine_room.flywheel.backend.engine.indirect; + +import org.lwjgl.opengl.ARBDrawBuffersBlend; +import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL46; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; + +import dev.engine_room.flywheel.backend.compile.IndirectPrograms; +import dev.engine_room.flywheel.backend.gl.GlTextureUnit; +import net.minecraft.client.Minecraft; + +public class WboitFrameBuffer { + + public final int fbo; + private final IndirectPrograms programs; + private final int vao; + + public int accum; + public int reveal; + + private int lastWidth = -1; + private int lastHeight = -1; + + public WboitFrameBuffer(IndirectPrograms programs) { + this.programs = programs; + fbo = GL46.glCreateFramebuffers(); + vao = GL46.glCreateVertexArrays(); + } + + public void setup() { + var mainRenderTarget = Minecraft.getInstance() + .getMainRenderTarget(); + + createTextures(mainRenderTarget.width, mainRenderTarget.height); + + // No depth writes, but we'll still use the depth test + GlStateManager._depthMask(false); + GlStateManager._enableBlend(); + ARBDrawBuffersBlend.glBlendFunciARB(0, GL46.GL_ONE, GL46.GL_ONE); // accumulation blend target + ARBDrawBuffersBlend.glBlendFunciARB(1, GL46.GL_ZERO, GL46.GL_ONE_MINUS_SRC_COLOR); // revealage blend target + GlStateManager._blendEquation(GL46.GL_FUNC_ADD); + + GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, mainRenderTarget.getDepthTextureId(), 0); + + GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); + + GL46.glClearBufferfv(GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); + GL46.glClearBufferfv(GL46.GL_COLOR, 1, new float[]{1, 1, 1, 1}); + } + + public void composite() { + var mainRenderTarget = Minecraft.getInstance() + .getMainRenderTarget(); + + mainRenderTarget.bindWrite(false); + + var oitCompositeProgram = programs.getOitCompositeProgram(); + + GlStateManager._depthMask(false); + GlStateManager._depthFunc(GL46.GL_ALWAYS); + GlStateManager._enableBlend(); + RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + + oitCompositeProgram.bind(); + + GlTextureUnit.T0.makeActive(); + GlStateManager._bindTexture(accum); + + GlTextureUnit.T1.makeActive(); + GlStateManager._bindTexture(reveal); + + // Empty VAO, the actual full screen triangle is generated in the vertex shader + GlStateManager._glBindVertexArray(vao); + + GL46.glDrawArrays(GL46.GL_TRIANGLES, 0, 3); + } + + public void delete() { + GL46.glDeleteTextures(accum); + GL46.glDeleteTextures(reveal); + GL46.glDeleteFramebuffers(fbo); + GL46.glDeleteVertexArrays(vao); + } + + private void createTextures(int width, int height) { + if (lastWidth == width && lastHeight == height) { + return; + } + + lastWidth = width; + lastHeight = height; + + GL46.glDeleteTextures(accum); + GL46.glDeleteTextures(reveal); + + accum = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); + reveal = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); + + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT0, GL46.GL_COLOR_ATTACHMENT1}); + + GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT0, accum, 0); + GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT1, reveal, 0); + + GL46.glTextureStorage2D(accum, 1, GL32.GL_RGBA32F, width, height); + GL46.glTextureStorage2D(reveal, 1, GL32.GL_R8, width, height); + + for (int tex : new int[]{accum, reveal}) { + GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST); + GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST); + GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_COMPARE_MODE, GL32.GL_NONE); + GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE); + GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE); + } + } +} diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag index d2d4fc5d8..7bc4ec54d 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag @@ -17,8 +17,20 @@ in vec2 _flw_crumblingTexCoord; flat in uvec2 _flw_ids; #endif +#ifdef _FLW_OIT + +// your first render target which is used to accumulate pre-multiplied color values +layout (location = 0) out vec4 accum; + +// your second render target which is used to store pixel revealage +layout (location = 1) out float reveal; + +#else + out vec4 _flw_outputColor; +#endif + float _flw_diffuseFactor() { if (flw_material.cardinalLightingMode == 2u) { return diffuseFromLightDirections(flw_vertexNormal); @@ -33,6 +45,11 @@ float _flw_diffuseFactor() { } } +float linearize_depth(float d, float zNear, float zFar) { + float z_n = 2.0 * d - 1.0; + return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); +} + void _flw_main() { flw_sampleColor = texture(flw_diffuseTex, flw_vertexTexCoord); flw_fragColor = flw_vertexColor * flw_sampleColor; @@ -99,5 +116,31 @@ void _flw_main() { } #endif - _flw_outputColor = flw_fogFilter(color); + color = flw_fogFilter(color); + + color.a = 0.9; + + #ifdef _FLW_OIT + + float depth = linearize_depth(gl_FragCoord.z, _flw_cullData.znear, _flw_cullData.zfar); + + // insert your favorite weighting function here. the color-based factor + // avoids color pollution from the edges of wispy clouds. the z-based + // factor gives precedence to nearer surfaces + //float weight = clamp(pow(min(1.0, color.a * 10.0) + 0.01, 3.0) * 1e8 * pow(1.0 - gl_FragCoord.z * 0.9, 3.0), 1e-2, 3e3); + float weight = max(min(1.0, max(max(color.r, color.g), color.b) * color.a), color.a) * + clamp(0.03 / (1e-5 + pow(depth / 200, 4.0)), 1e-2, 3e3); + + // blend func: GL_ONE, GL_ONE + // switch to pre-multiplied alpha and weight + accum = vec4(color.rgb * color.a, color.a) * weight; + + // blend func: GL_ZERO, GL_ONE_MINUS_SRC_ALPHA + reveal = color.a; + + #else + + _flw_outputColor = color; + + #endif } diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/fullscreen.vert b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/fullscreen.vert new file mode 100644 index 000000000..46ae47150 --- /dev/null +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/fullscreen.vert @@ -0,0 +1,4 @@ +void main() { + vec2 vertices[3] = vec2[3](vec2(-1, -1), vec2(3, -1), vec2(-1, 3)); + gl_Position = vec4(vertices[gl_VertexID], 0, 1); +} diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag new file mode 100644 index 000000000..6c5a67419 --- /dev/null +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag @@ -0,0 +1,48 @@ +// shader outputs +layout (location = 0) out vec4 frag; + +// color accumulation buffer +layout (binding = 0) uniform sampler2D accum; + +// revealage threshold buffer +layout (binding = 1) uniform sampler2D reveal; + +// epsilon number +const float EPSILON = 0.00001f; + +// calculate floating point numbers equality accurately +bool isApproximatelyEqual(float a, float b) { + return abs(a - b) <= (abs(a) < abs(b) ? abs(b) : abs(a)) * EPSILON; +} + +// get the max value between three values +float max3(vec3 v) { + return max(max(v.x, v.y), v.z); +} + +void main() { + // fragment coordination + ivec2 coords = ivec2(gl_FragCoord.xy); + + // fragment revealage + float revealage = texelFetch(reveal, coords, 0).r; + + // save the blending and color texture fetch cost if there is not a transparent fragment + if (isApproximatelyEqual(revealage, 1.0f)) { + discard; + } + + // fragment color + vec4 accumulation = texelFetch(accum, coords, 0); + + // suppress overflow + if (isinf(max3(abs(accumulation.rgb)))) { + accumulation.rgb = vec3(accumulation.a); + } + + // prevent floating point precision bug + vec3 average_color = accumulation.rgb / max(accumulation.a, EPSILON); + + // blend pixels + frag = vec4(average_color, 1.0f - revealage); +} From cb87961b192b7517b21d9e9eb475b95cc0ba324c Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 16 Feb 2025 18:16:10 -0800 Subject: [PATCH 06/23] A moment, please - Implement mboit on indirect - Has issues when many high alpha fragments are stacked on top of each other. The transmittance function explodes to zero, and we end up just writing out black in the second pass. - Other than that, I think the approach is very sound but hopefully a solution can be found - Needs some clean up work with instancing, and in fact I think mboit can be implemented on GL3.2, whereas wboit relied on extensions or GL4.0 --- .../flywheel/backend/Samplers.java | 3 + .../backend/compile/IndirectPrograms.java | 2 +- .../backend/compile/InstancingPrograms.java | 2 +- .../backend/compile/PipelineCompiler.java | 24 +- .../engine/indirect/IndirectCullingGroup.java | 9 +- .../engine/indirect/IndirectDrawManager.java | 15 +- .../engine/indirect/MboitFramebuffer.java | 142 +++++ .../engine/indirect/WboitFrameBuffer.java | 117 ---- .../flywheel/backend/gl/GlCompat.java | 6 +- .../backend/glsl/parse/ShaderField.java | 28 +- .../flywheel/flywheel/internal/common.frag | 57 +- .../internal/indirect/oit_composite.frag | 42 +- .../internal/mboit/complex_algebra.glsl | 203 +++++++ .../flywheel/internal/mboit/moment_math.glsl | 500 ++++++++++++++++++ .../flywheel/internal/mboit/moment_oit.glsl | 253 +++++++++ .../mboit/trigonometric_moment_math.glsl | 311 +++++++++++ .../vanillin/visuals/ShulkerBoxVisual.java | 7 + 17 files changed, 1517 insertions(+), 204 deletions(-) create mode 100644 common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MboitFramebuffer.java delete mode 100644 common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/WboitFrameBuffer.java create mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl create mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl create mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl create mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java b/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java index 4441c6246..b6e40e291 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java @@ -10,4 +10,7 @@ public class Samplers { public static final GlTextureUnit INSTANCE_BUFFER = GlTextureUnit.T4; public static final GlTextureUnit LIGHT_LUT = GlTextureUnit.T5; public static final GlTextureUnit LIGHT_SECTIONS = GlTextureUnit.T6; + + public static final GlTextureUnit ZEROTH_MOMENT = GlTextureUnit.T7; + public static final GlTextureUnit MOMENTS = GlTextureUnit.T8; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java index 9d3ce83e0..cc985b278 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java @@ -164,7 +164,7 @@ public class IndirectPrograms extends AtomicReferenceCounted { setInstance(null); } - public GlProgram getIndirectProgram(InstanceType instanceType, ContextShader contextShader, Material material, boolean oit) { + public GlProgram getIndirectProgram(InstanceType instanceType, ContextShader contextShader, Material material, PipelineCompiler.OitMode oit) { return pipeline.get(instanceType, contextShader, material, oit); } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java index db3014402..392435849 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java @@ -70,7 +70,7 @@ public class InstancingPrograms extends AtomicReferenceCounted { } public GlProgram get(InstanceType instanceType, ContextShader contextShader, Material material) { - return pipeline.get(instanceType, contextShader, material, false); + return pipeline.get(instanceType, contextShader, material, PipelineCompiler.OitMode.OFF); } @Override diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java index e315dce35..f33486315 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java @@ -50,7 +50,7 @@ public final class PipelineCompiler { ALL.add(this); } - public GlProgram get(InstanceType instanceType, ContextShader contextShader, Material material, boolean oit) { + public GlProgram get(InstanceType instanceType, ContextShader contextShader, Material material, OitMode oit) { var light = material.light(); var cutout = material.cutout(); var shaders = material.shaders(); @@ -128,7 +128,7 @@ public final class PipelineCompiler { .source()); var debug = key.debugEnabled() ? "_debug" : ""; var cutout = key.useCutout() ? "_cutout" : ""; - var oit = key.oit() ? "_oit" : ""; + var oit = key.oit().name; return "pipeline/" + pipeline.compilerMarker() + "/frag/" + material + "/" + light + "_" + context + cutout + debug + oit; }) .requireExtensions(extensions) @@ -148,8 +148,9 @@ public final class PipelineCompiler { } }) .onCompile((key, comp) -> { - if (key.oit()) { + if (key.oit() != OitMode.OFF) { comp.define("_FLW_OIT"); + comp.define(key.oit().define); } }) .withResource(API_IMPL_FRAG) @@ -224,6 +225,21 @@ public final class PipelineCompiler { */ public record PipelineProgramKey(InstanceType instanceType, ContextShader contextShader, LightShader light, MaterialShaders materialShaders, boolean useCutout, boolean debugEnabled, - boolean oit) { + OitMode oit) { + } + + public enum OitMode { + OFF("", ""), + GENERATE("_FLW_GENERATE_MOMENTS", "_generate"), + RESOLVE("_FLW_RESOLVE_MOMENTS", "_resolve"), + ; + + public final String define; + public final String name; + + OitMode(String define, String name) { + this.define = define; + this.name = name; + } } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java index aa605f017..25e38ad11 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -17,6 +17,7 @@ import dev.engine_room.flywheel.api.material.Transparency; import dev.engine_room.flywheel.api.model.Model; import dev.engine_room.flywheel.backend.compile.ContextShader; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; +import dev.engine_room.flywheel.backend.compile.PipelineCompiler; import dev.engine_room.flywheel.backend.engine.InstancerKey; import dev.engine_room.flywheel.backend.engine.MaterialRenderState; import dev.engine_room.flywheel.backend.engine.MeshPool; @@ -188,7 +189,7 @@ public class IndirectCullingGroup { GlProgram lastProgram = null; for (var multiDraw : multiDraws) { - var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, false); + var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, PipelineCompiler.OitMode.OFF); if (drawProgram != lastProgram) { lastProgram = drawProgram; @@ -202,7 +203,7 @@ public class IndirectCullingGroup { } } - public void submitTransparent() { + public void submitTransparent(PipelineCompiler.OitMode oit) { if (nothingToDo()) { return; } @@ -214,7 +215,7 @@ public class IndirectCullingGroup { GlProgram lastProgram = null; for (var multiDraw : transparentDraws) { - var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, true); + var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, oit); if (drawProgram != lastProgram) { lastProgram = drawProgram; @@ -229,7 +230,7 @@ public class IndirectCullingGroup { } public void bindForCrumbling(Material material) { - var program = programs.getIndirectProgram(instanceType, ContextShader.CRUMBLING, material, false); + var program = programs.getIndirectProgram(instanceType, ContextShader.CRUMBLING, material, PipelineCompiler.OitMode.OFF); program.bind(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index a61677865..afc62d5af 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -17,6 +17,7 @@ import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.backend.Samplers; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; +import dev.engine_room.flywheel.backend.compile.PipelineCompiler; import dev.engine_room.flywheel.backend.engine.AbstractInstancer; import dev.engine_room.flywheel.backend.engine.CommonCrumbling; import dev.engine_room.flywheel.backend.engine.DrawManager; @@ -48,7 +49,7 @@ public class IndirectDrawManager extends DrawManager> { private final DepthPyramid depthPyramid; - private final WboitFrameBuffer wboitFrameBuffer; + private final MboitFramebuffer wboitFrameBuffer; public IndirectDrawManager(IndirectPrograms programs) { this.programs = programs; @@ -65,7 +66,7 @@ public class IndirectDrawManager extends DrawManager> { depthPyramid = new DepthPyramid(programs); - wboitFrameBuffer = new WboitFrameBuffer(programs); + wboitFrameBuffer = new MboitFramebuffer(programs); } @Override @@ -145,10 +146,16 @@ public class IndirectDrawManager extends DrawManager> { group.submitSolid(); } - wboitFrameBuffer.setup(); + wboitFrameBuffer.generateMoments(); for (var group : cullingGroups.values()) { - group.submitTransparent(); + group.submitTransparent(PipelineCompiler.OitMode.GENERATE); + } + + wboitFrameBuffer.resolveMoments(); + + for (var group : cullingGroups.values()) { + group.submitTransparent(PipelineCompiler.OitMode.RESOLVE); } wboitFrameBuffer.composite(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MboitFramebuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MboitFramebuffer.java new file mode 100644 index 000000000..06d283f20 --- /dev/null +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MboitFramebuffer.java @@ -0,0 +1,142 @@ +package dev.engine_room.flywheel.backend.engine.indirect; + +import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL46; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; + +import dev.engine_room.flywheel.backend.Samplers; +import dev.engine_room.flywheel.backend.compile.IndirectPrograms; +import dev.engine_room.flywheel.backend.gl.GlTextureUnit; +import net.minecraft.client.Minecraft; + +public class MboitFramebuffer { + + public final int fbo; + private final IndirectPrograms programs; + private final int vao; + + public int zerothMoment; + public int moments; + public int accumulate; + + private int lastWidth = -1; + private int lastHeight = -1; + + public MboitFramebuffer(IndirectPrograms programs) { + this.programs = programs; + fbo = GL46.glCreateFramebuffers(); + vao = GL46.glCreateVertexArrays(); + } + + public void generateMoments() { + var mainRenderTarget = Minecraft.getInstance() + .getMainRenderTarget(); + + createTextures(mainRenderTarget.width, mainRenderTarget.height); + + // No depth writes, but we'll still use the depth test + RenderSystem.depthMask(false); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); + RenderSystem.blendEquation(GL46.GL_FUNC_ADD); + + GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, mainRenderTarget.getDepthTextureId(), 0); + + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT0, GL46.GL_COLOR_ATTACHMENT1}); + + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 1, new float[]{0, 0, 0, 0}); + + GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); + } + + public void resolveMoments() { + // No depth writes, but we'll still use the depth test + RenderSystem.depthMask(false); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); + RenderSystem.blendEquation(GL46.GL_FUNC_ADD); + + Samplers.ZEROTH_MOMENT.makeActive(); + GlStateManager._bindTexture(zerothMoment); + + Samplers.MOMENTS.makeActive(); + GlStateManager._bindTexture(moments); + + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT2}); + + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); + + GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); + } + + public void composite() { + var mainRenderTarget = Minecraft.getInstance() + .getMainRenderTarget(); + + mainRenderTarget.bindWrite(false); + + var oitCompositeProgram = programs.getOitCompositeProgram(); + + GlStateManager._depthMask(false); + GlStateManager._depthFunc(GL46.GL_ALWAYS); + GlStateManager._enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.DestFactor.SRC_ALPHA); + + oitCompositeProgram.bind(); + + GlTextureUnit.T0.makeActive(); + GlStateManager._bindTexture(zerothMoment); + + GlTextureUnit.T1.makeActive(); + GlStateManager._bindTexture(accumulate); + + // Empty VAO, the actual full screen triangle is generated in the vertex shader + GlStateManager._glBindVertexArray(vao); + + GL46.glDrawArrays(GL46.GL_TRIANGLES, 0, 3); + } + + public void delete() { + GL46.glDeleteTextures(zerothMoment); + GL46.glDeleteTextures(moments); + GL46.glDeleteTextures(accumulate); + GL46.glDeleteFramebuffers(fbo); + GL46.glDeleteVertexArrays(vao); + } + + private void createTextures(int width, int height) { + if (lastWidth == width && lastHeight == height) { + return; + } + + lastWidth = width; + lastHeight = height; + + GL46.glDeleteTextures(zerothMoment); + GL46.glDeleteTextures(moments); + GL46.glDeleteTextures(accumulate); + + zerothMoment = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); + moments = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); + accumulate = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); + + GL46.glTextureStorage2D(zerothMoment, 1, GL32.GL_R16F, width, height); + GL46.glTextureStorage2D(moments, 1, GL32.GL_RGBA16F, width, height); + GL46.glTextureStorage2D(accumulate, 1, GL32.GL_RGBA16F, width, height); + + // for (int tex : new int[]{zerothMoment, moments, composite}) { + // GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST); + // GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST); + // GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_COMPARE_MODE, GL32.GL_NONE); + // GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE); + // GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE); + // } + + GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT0, zerothMoment, 0); + GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT1, moments, 0); + GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT2, accumulate, 0); + } +} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/WboitFrameBuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/WboitFrameBuffer.java deleted file mode 100644 index c76133709..000000000 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/WboitFrameBuffer.java +++ /dev/null @@ -1,117 +0,0 @@ -package dev.engine_room.flywheel.backend.engine.indirect; - -import org.lwjgl.opengl.ARBDrawBuffersBlend; -import org.lwjgl.opengl.GL32; -import org.lwjgl.opengl.GL46; - -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; - -import dev.engine_room.flywheel.backend.compile.IndirectPrograms; -import dev.engine_room.flywheel.backend.gl.GlTextureUnit; -import net.minecraft.client.Minecraft; - -public class WboitFrameBuffer { - - public final int fbo; - private final IndirectPrograms programs; - private final int vao; - - public int accum; - public int reveal; - - private int lastWidth = -1; - private int lastHeight = -1; - - public WboitFrameBuffer(IndirectPrograms programs) { - this.programs = programs; - fbo = GL46.glCreateFramebuffers(); - vao = GL46.glCreateVertexArrays(); - } - - public void setup() { - var mainRenderTarget = Minecraft.getInstance() - .getMainRenderTarget(); - - createTextures(mainRenderTarget.width, mainRenderTarget.height); - - // No depth writes, but we'll still use the depth test - GlStateManager._depthMask(false); - GlStateManager._enableBlend(); - ARBDrawBuffersBlend.glBlendFunciARB(0, GL46.GL_ONE, GL46.GL_ONE); // accumulation blend target - ARBDrawBuffersBlend.glBlendFunciARB(1, GL46.GL_ZERO, GL46.GL_ONE_MINUS_SRC_COLOR); // revealage blend target - GlStateManager._blendEquation(GL46.GL_FUNC_ADD); - - GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, mainRenderTarget.getDepthTextureId(), 0); - - GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); - - GL46.glClearBufferfv(GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); - GL46.glClearBufferfv(GL46.GL_COLOR, 1, new float[]{1, 1, 1, 1}); - } - - public void composite() { - var mainRenderTarget = Minecraft.getInstance() - .getMainRenderTarget(); - - mainRenderTarget.bindWrite(false); - - var oitCompositeProgram = programs.getOitCompositeProgram(); - - GlStateManager._depthMask(false); - GlStateManager._depthFunc(GL46.GL_ALWAYS); - GlStateManager._enableBlend(); - RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - - oitCompositeProgram.bind(); - - GlTextureUnit.T0.makeActive(); - GlStateManager._bindTexture(accum); - - GlTextureUnit.T1.makeActive(); - GlStateManager._bindTexture(reveal); - - // Empty VAO, the actual full screen triangle is generated in the vertex shader - GlStateManager._glBindVertexArray(vao); - - GL46.glDrawArrays(GL46.GL_TRIANGLES, 0, 3); - } - - public void delete() { - GL46.glDeleteTextures(accum); - GL46.glDeleteTextures(reveal); - GL46.glDeleteFramebuffers(fbo); - GL46.glDeleteVertexArrays(vao); - } - - private void createTextures(int width, int height) { - if (lastWidth == width && lastHeight == height) { - return; - } - - lastWidth = width; - lastHeight = height; - - GL46.glDeleteTextures(accum); - GL46.glDeleteTextures(reveal); - - accum = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); - reveal = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); - - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT0, GL46.GL_COLOR_ATTACHMENT1}); - - GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT0, accum, 0); - GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT1, reveal, 0); - - GL46.glTextureStorage2D(accum, 1, GL32.GL_RGBA32F, width, height); - GL46.glTextureStorage2D(reveal, 1, GL32.GL_R8, width, height); - - for (int tex : new int[]{accum, reveal}) { - GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST); - GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST); - GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_COMPARE_MODE, GL32.GL_NONE); - GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE); - GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE); - } - } -} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java b/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java index 636b9ac4c..b07070bc7 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java @@ -1,7 +1,5 @@ package dev.engine_room.flywheel.backend.gl; -import java.nio.ByteBuffer; - import org.jetbrains.annotations.UnknownNullability; import org.lwjgl.PointerBuffer; import org.lwjgl.opengl.GL; @@ -13,6 +11,7 @@ import org.lwjgl.opengl.GL43; import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.KHRShaderSubgroup; import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; import dev.engine_room.flywheel.backend.FlwBackend; import dev.engine_room.flywheel.backend.compile.core.Compilation; @@ -67,10 +66,11 @@ public final class GlCompat { */ public static void safeShaderSource(int glId, CharSequence source) { try (MemoryStack stack = MemoryStack.stackPush()) { - final ByteBuffer sourceBuffer = stack.UTF8(source, true); + var sourceBuffer = MemoryUtil.memUTF8(source, true); final PointerBuffer pointers = stack.mallocPointer(1); pointers.put(sourceBuffer); GL20C.nglShaderSource(glId, 1, pointers.address0(), 0); + MemoryUtil.memFree(sourceBuffer); } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderField.java b/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderField.java index 3f7871d43..c36bf4f7f 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderField.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderField.java @@ -1,6 +1,5 @@ package dev.engine_room.flywheel.backend.glsl.parse; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jetbrains.annotations.Nullable; @@ -34,20 +33,21 @@ public class ShaderField { * Scan the source for function definitions and "parse" them into objects that contain properties of the function. */ public static ImmutableMap parseFields(SourceLines source) { - Matcher matcher = PATTERN.matcher(source); + // Matcher matcher = PATTERN.matcher(source); + // + // ImmutableMap.Builder fields = ImmutableMap.builder(); + // while (matcher.find()) { + // Span self = Span.fromMatcher(source, matcher); + // Span location = Span.fromMatcher(source, matcher, 1); + // Span decoration = Span.fromMatcher(source, matcher, 2); + // Span type = Span.fromMatcher(source, matcher, 3); + // Span name = Span.fromMatcher(source, matcher, 4); + // + // fields.put(location.get(), new ShaderField(self, location, decoration, type, name)); + // } - ImmutableMap.Builder fields = ImmutableMap.builder(); - while (matcher.find()) { - Span self = Span.fromMatcher(source, matcher); - Span location = Span.fromMatcher(source, matcher, 1); - Span decoration = Span.fromMatcher(source, matcher, 2); - Span type = Span.fromMatcher(source, matcher, 3); - Span name = Span.fromMatcher(source, matcher, 4); - - fields.put(location.get(), new ShaderField(self, location, decoration, type, name)); - } - - return fields.build(); + return ImmutableMap.builder() + .build(); } public enum Qualifier { diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag index 7bc4ec54d..afd8ae647 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag @@ -1,6 +1,7 @@ #include "flywheel:internal/packed_material.glsl" #include "flywheel:internal/diffuse.glsl" #include "flywheel:internal/colorizer.glsl" +#include "flywheel:internal/mboit/moment_oit.glsl" // optimize discard usage #if defined(GL_ARB_conservative_depth) && defined(_FLW_USE_DISCARD) @@ -18,13 +19,13 @@ flat in uvec2 _flw_ids; #endif #ifdef _FLW_OIT - -// your first render target which is used to accumulate pre-multiplied color values -layout (location = 0) out vec4 accum; - -// your second render target which is used to store pixel revealage -layout (location = 1) out float reveal; - +#ifdef _FLW_GENERATE_MOMENTS +layout (location = 0) out float _flw_zerothMoment_out; +layout (location = 1) out vec4 _flw_moments_out; +#endif +#ifdef _FLW_RESOLVE_MOMENTS +layout (location = 0) out vec4 _flw_accumulate_out; +#endif #else out vec4 _flw_outputColor; @@ -118,26 +119,40 @@ void _flw_main() { color = flw_fogFilter(color); - color.a = 0.9; - #ifdef _FLW_OIT + float linearDepth = linearize_depth(gl_FragCoord.z, _flw_cullData.znear, _flw_cullData.zfar); - float depth = linearize_depth(gl_FragCoord.z, _flw_cullData.znear, _flw_cullData.zfar); + float lnNear = log(_flw_cullData.znear); + float lnFar = log(_flw_cullData.zfar); - // insert your favorite weighting function here. the color-based factor - // avoids color pollution from the edges of wispy clouds. the z-based - // factor gives precedence to nearer surfaces - //float weight = clamp(pow(min(1.0, color.a * 10.0) + 0.01, 3.0) * 1e8 * pow(1.0 - gl_FragCoord.z * 0.9, 3.0), 1e-2, 3e3); - float weight = max(min(1.0, max(max(color.r, color.g), color.b) * color.a), color.a) * - clamp(0.03 / (1e-5 + pow(depth / 200, 4.0)), 1e-2, 3e3); + float depth = (log(linearDepth) - lnNear); - // blend func: GL_ONE, GL_ONE - // switch to pre-multiplied alpha and weight - accum = vec4(color.rgb * color.a, color.a) * weight; + depth /= lnFar - lnNear; - // blend func: GL_ZERO, GL_ONE_MINUS_SRC_ALPHA - reveal = color.a; + depth = clamp(depth * 2. - 1., -1., 1.); + #ifdef _FLW_GENERATE_MOMENTS + + generateMoments(depth, 1 - color.a, vec4(0), _flw_zerothMoment_out, _flw_moments_out); + + #endif + #ifdef _FLW_RESOLVE_MOMENTS + + float tt; + float td; + resolveMoments(td, tt, depth, gl_FragCoord.xy); + + if (abs(td) < 1e-5) { + discard; + } + + color.rgb *= color.a; + + color *= td; + + _flw_accumulate_out = color; + + #endif #else _flw_outputColor = color; diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag index 6c5a67419..3afe92839 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag @@ -1,48 +1,20 @@ -// shader outputs layout (location = 0) out vec4 frag; -// color accumulation buffer -layout (binding = 0) uniform sampler2D accum; - -// revealage threshold buffer -layout (binding = 1) uniform sampler2D reveal; - -// epsilon number -const float EPSILON = 0.00001f; - -// calculate floating point numbers equality accurately -bool isApproximatelyEqual(float a, float b) { - return abs(a - b) <= (abs(a) < abs(b) ? abs(b) : abs(a)) * EPSILON; -} - -// get the max value between three values -float max3(vec3 v) { - return max(max(v.x, v.y), v.z); -} +layout (binding = 0) uniform sampler2D zerothMoment; +layout (binding = 1) uniform sampler2D accumulate; void main() { - // fragment coordination ivec2 coords = ivec2(gl_FragCoord.xy); - // fragment revealage - float revealage = texelFetch(reveal, coords, 0).r; + float b0 = texelFetch(zerothMoment, coords, 0).r; - // save the blending and color texture fetch cost if there is not a transparent fragment - if (isApproximatelyEqual(revealage, 1.0f)) { + if (b0 < 1e-5) { discard; } - // fragment color - vec4 accumulation = texelFetch(accum, coords, 0); + vec4 accumulation = texelFetch(accumulate, coords, 0); - // suppress overflow - if (isinf(max3(abs(accumulation.rgb)))) { - accumulation.rgb = vec3(accumulation.a); - } + vec3 normalizedAccumulation = accumulation.rgb / max(accumulation.a, 1e-5); - // prevent floating point precision bug - vec3 average_color = accumulation.rgb / max(accumulation.a, EPSILON); - - // blend pixels - frag = vec4(average_color, 1.0f - revealage); + frag = vec4(normalizedAccumulation, exp(-b0)); } diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl new file mode 100644 index 000000000..b2cf49485 --- /dev/null +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl @@ -0,0 +1,203 @@ +/*! \file + This header defines utility functions to deal with complex numbers and + complex polynomials.*/ + +/*! Returns the complex conjugate of the given complex number (i.e. it changes + the sign of the y-component).*/ +vec2 Conjugate(vec2 Z){ + return vec2(Z.x, -Z.y); +} +/*! This function implements complex multiplication.*/ +vec2 Multiply(vec2 LHS, vec2 RHS){ + return vec2(LHS.x*RHS.x-LHS.y*RHS.y, LHS.x*RHS.y+LHS.y*RHS.x); +} +/*! This function computes the magnitude of the given complex number.*/ +float Magnitude(vec2 Z){ + return sqrt(dot(Z, Z)); +} +/*! This function computes the quotient of two complex numbers. The denominator + must not be zero.*/ +vec2 Divide(vec2 Numerator, vec2 Denominator){ + return vec2(Numerator.x*Denominator.x+Numerator.y*Denominator.y, -Numerator.x*Denominator.y+Numerator.y*Denominator.x)/dot(Denominator, Denominator); +} +/*! This function divides a real number by a complex number. The denominator + must not be zero.*/ +vec2 Divide(float Numerator, vec2 Denominator){ + return vec2(Numerator*Denominator.x, -Numerator*Denominator.y)/dot(Denominator, Denominator); +} +/*! This function implements computation of the reciprocal of the given non- + zero complex number.*/ +vec2 Reciprocal(vec2 Z){ + return vec2(Z.x, -Z.y)/dot(Z, Z); +} +/*! This utility function implements complex squaring.*/ +vec2 Square(vec2 Z){ + return vec2(Z.x*Z.x-Z.y*Z.y, 2.0f*Z.x*Z.y); +} +/*! This utility function implements complex computation of the third power.*/ +vec2 Cube(vec2 Z){ + return Multiply(Square(Z), Z); +} +/*! This utility function computes one square root of the given complex value. + The other one can be found using the unary minus operator. + \warning This function is continuous but not defined on the negative real + axis (and cannot be continued continuously there). + \sa SquareRoot() */ +vec2 SquareRootUnsafe(vec2 Z){ + float ZLengthSq=dot(Z, Z); + float ZLengthInv=rsqrt(ZLengthSq); + vec2 UnnormalizedRoot=Z*ZLengthInv+vec2(1.0f, 0.0f); + float UnnormalizedRootLengthSq=dot(UnnormalizedRoot, UnnormalizedRoot); + float NormalizationFactorInvSq=UnnormalizedRootLengthSq*ZLengthInv; + float NormalizationFactor=rsqrt(NormalizationFactorInvSq); + return NormalizationFactor*UnnormalizedRoot; +} +/*! This utility function computes one square root of the given complex value. + The other one can be found using the unary minus operator. + \note This function has discontinuities for values with real part zero. + \sa SquareRootUnsafe() */ +vec2 SquareRoot(vec2 Z){ + vec2 ZPositiveRealPart=vec2(abs(Z.x), Z.y); + vec2 ComputedRoot=SquareRootUnsafe(ZPositiveRealPart); + return (Z.x>=0.0)?ComputedRoot:ComputedRoot.yx; +} +/*! This utility function computes one cubic root of the given complex value. The + other roots can be found by multiplication by cubic roots of unity. + \note This function has various discontinuities.*/ +vec2 CubicRoot(vec2 Z){ + float Argument=atan2(Z.y, Z.x); + float NewArgument=Argument/3.0f; + vec2 NormalizedRoot; + sincos(NewArgument, NormalizedRoot.y, NormalizedRoot.x); + return NormalizedRoot*pow(dot(Z, Z), 1.0f/6.0f); +} + +/*! @{ + Returns the complex conjugate of the given complex vector (i.e. it changes the + second column resp the y-component).*/ +mat2x2 Conjugate(mat2x2 Vector){ + return mat2x2(Vector[0].x, -Vector[0].y, Vector[1].x, -Vector[1].y); +} +mat3x2 Conjugate(mat3x2 Vector){ + return mat3x2(Vector[0].x, -Vector[0].y, Vector[1].x, -Vector[1].y, Vector[2].x, -Vector[2].y); +} +mat4x2 Conjugate(mat4x2 Vector){ + return mat4x2(Vector[0].x, -Vector[0].y, Vector[1].x, -Vector[1].y, Vector[2].x, -Vector[2].y, Vector[3].x, -Vector[3].y); +} +void Conjugate(out vec2 OutConjugateVector[5], vec2 Vector[5]){ + for (int i=0;i!=5;++i){ + OutConjugateVector[i]=vec2(Vector[i].x, -Vector[i].x); + } +} +//!@} + +/*! Returns the real part of a complex number as real.*/ +float RealPart(vec2 Z){ + return Z.x; +} + +/*! Given coefficients of a quadratic polynomial A*x^2+B*x+C, this function + outputs its two complex roots.*/ +void SolveQuadratic(out vec2 pOutRoot[2], vec2 A, vec2 B, vec2 C) +{ + // Normalize the coefficients + vec2 InvA=Reciprocal(A); + B=Multiply(B, InvA); + C=Multiply(C, InvA); + // Divide the middle coefficient by two + B*=0.5f; + // Apply the quadratic formula + vec2 DiscriminantRoot=SquareRoot(Square(B)-C); + pOutRoot[0]=-B-DiscriminantRoot; + pOutRoot[1]=-B+DiscriminantRoot; +} + +/*! Given coefficients of a cubic polynomial A*x^3+B*x^2+C*x+D, this function + outputs its three complex roots.*/ +void SolveCubicBlinn(out vec2 pOutRoot[3], vec2 A, vec2 B, vec2 C, vec2 D) +{ + // Normalize the polynomial + vec2 InvA=Reciprocal(A); + B=Multiply(B, InvA); + C=Multiply(C, InvA); + D=Multiply(D, InvA); + // Divide middle coefficients by three + B/=3.0f; + C/=3.0f; + // Compute the Hessian and the discriminant + vec2 Delta00=-Square(B)+C; + vec2 Delta01=-Multiply(C, B)+D; + vec2 Delta11=Multiply(B, D)-Square(C); + vec2 Discriminant=4.0f*Multiply(Delta00, Delta11)-Square(Delta01); + // Compute coefficients of the depressed cubic + // (third is zero, fourth is one) + vec2 DepressedD=-2.0f*Multiply(B, Delta00)+Delta01; + vec2 DepressedC=Delta00; + // Take the cubic root of a complex number avoiding cancellation + vec2 DiscriminantRoot=SquareRoot(-Discriminant); + DiscriminantRoot=faceforward(DiscriminantRoot, DiscriminantRoot, DepressedD); + vec2 CubedRoot=DiscriminantRoot-DepressedD; + vec2 FirstRoot=CubicRoot(0.5f*CubedRoot); + vec2 pCubicRoot[3]={ + FirstRoot, + Multiply(vec2(-0.5f, -0.5f*sqrt(3.0f)), FirstRoot), + Multiply(vec2(-0.5f, 0.5f*sqrt(3.0f)), FirstRoot) + }; + // Also compute the reciprocal cubic roots + vec2 InvFirstRoot=Reciprocal(FirstRoot); + vec2 pInvCubicRoot[3]={ + InvFirstRoot, + Multiply(vec2(-0.5f, 0.5f*sqrt(3.0f)), InvFirstRoot), + Multiply(vec2(-0.5f, -0.5f*sqrt(3.0f)), InvFirstRoot) + }; + // Turn them into roots of the depressed cubic and revert the depression + // transform + + for (int i=0;i!=3;++i) + { + pOutRoot[i]=pCubicRoot[i]-Multiply(DepressedC, pInvCubicRoot[i])-B; + } +} + + +/*! Given coefficients of a quartic polynomial A*x^4+B*x^3+C*x^2+D*x+E, this + function outputs its four complex roots.*/ +void SolveQuarticNeumark(out vec2 pOutRoot[4], vec2 A, vec2 B, vec2 C, vec2 D, vec2 E) +{ + // Normalize the polynomial + vec2 InvA=Reciprocal(A); + B=Multiply(B, InvA); + C=Multiply(C, InvA); + D=Multiply(D, InvA); + E=Multiply(E, InvA); + // Construct a normalized cubic + vec2 P=-2.0f*C; + vec2 Q=Square(C)+Multiply(B, D)-4.0f*E; + vec2 R=Square(D)+Multiply(Square(B), E)-Multiply(Multiply(B, C), D); + // Compute a root that is not the smallest of the cubic + vec2 pCubicRoot[3]; + SolveCubicBlinn(pCubicRoot, vec2(1.0f, 0.0f), P, Q, R); + vec2 y=(dot(pCubicRoot[1], pCubicRoot[1])>dot(pCubicRoot[0], pCubicRoot[0]))?pCubicRoot[1]:pCubicRoot[0]; + + // Solve a quadratic to obtain linear coefficients for quadratic polynomials + vec2 BB=Square(B); + vec2 fy=4.0f*y; + vec2 BB_fy=BB-fy; + vec2 tmp=SquareRoot(BB_fy); + vec2 G=(B+tmp)*0.5f; + vec2 g=(B-tmp)*0.5f; + // Construct the corresponding constant coefficients + vec2 Z=C-y; + tmp=Divide(0.5f*Multiply(B, Z)-D, tmp); + vec2 H=Z*0.5f+tmp; + vec2 h=Z*0.5f-tmp; + + // Compute the roots + vec2 pQuadraticRoot[2]; + SolveQuadratic(pQuadraticRoot, vec2(1.0f, 0.0f), G, H); + pOutRoot[0]=pQuadraticRoot[0]; + pOutRoot[1]=pQuadraticRoot[1]; + SolveQuadratic(pQuadraticRoot, vec2(1.0f, 0.0f), g, h); + pOutRoot[2]=pQuadraticRoot[0]; + pOutRoot[3]=pQuadraticRoot[1]; +} diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl new file mode 100644 index 000000000..0c2d23b72 --- /dev/null +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl @@ -0,0 +1,500 @@ +/*! \file + This header provides utility functions to reconstruct the transmittance + from a given vector of power moments (4, 6 or 8 power moments) at a + specified depth. As prerequisite, utility functions for computing the real + roots of polynomials up to degree four are defined. +*/ + +//#include "flywheel:internal/mboit/trigonometric_moment_math.glsl" + +void sincos(float theta, out float s, out float c) { + s = sin(theta); + c = cos(theta); +} + +float saturate(float a) { + return clamp(a, 0., 1.); +} + + + +/*! Given coefficients of a quadratic polynomial A*x^2+B*x+C, this function + outputs its two real roots.*/ +vec2 solveQuadratic(vec3 coeffs) +{ + coeffs[1] *= 0.5; + + float x1, x2, tmp; + + tmp = (coeffs[1] * coeffs[1] - coeffs[0] * coeffs[2]); + if (coeffs[1] >= 0) { + tmp = sqrt(tmp); + x1 = (-coeffs[2]) / (coeffs[1] + tmp); + x2 = (-coeffs[1] - tmp) / coeffs[0]; + } else { + tmp = sqrt(tmp); + x1 = (-coeffs[1] + tmp) / coeffs[0]; + x2 = coeffs[2] / (-coeffs[1] + tmp); + } + return vec2(x1, x2); +} + +/*! Code taken from the blog "Moments in Graphics" by Christoph Peters. + http://momentsingraphics.de/?p=105 + This function computes the three real roots of a cubic polynomial + Coefficient[0]+Coefficient[1]*x+Coefficient[2]*x^2+Coefficient[3]*x^3.*/ +vec3 SolveCubic(vec4 Coefficient) { + // Normalize the polynomial + Coefficient.xyz /= Coefficient.w; + // Divide middle coefficients by three + Coefficient.yz /= 3.0f; + // Compute the Hessian and the discrimant + vec3 Delta = vec3( + fma(-Coefficient.z, Coefficient.z, Coefficient.y), + fma(-Coefficient.y, Coefficient.z, Coefficient.x), + dot(vec2(Coefficient.z, -Coefficient.y), Coefficient.xy) + ); + float Discriminant = dot(vec2(4.0f*Delta.x, -Delta.y), Delta.zy); + // Compute coefficients of the depressed cubic + // (third is zero, fourth is one) + vec2 Depressed = vec2( + fma(-2.0f*Coefficient.z, Delta.x, Delta.y), + Delta.x + ); + // Take the cubic root of a normalized complex number + float Theta = atan(sqrt(Discriminant), -Depressed.x) / 3.0f; + vec2 CubicRoot; + sincos(Theta, CubicRoot.y, CubicRoot.x); + // Compute the three roots, scale appropriately and + // revert the depression transform + vec3 Root = vec3( + CubicRoot.x, + dot(vec2(-0.5f, -0.5f*sqrt(3.0f)), CubicRoot), + dot(vec2(-0.5f, 0.5f*sqrt(3.0f)), CubicRoot) + ); + Root = fma(vec3(2.0f*sqrt(-Depressed.y)), Root, vec3(-Coefficient.z)); + return Root; +} + +/*! Given coefficients of a cubic polynomial + coeffs[0]+coeffs[1]*x+coeffs[2]*x^2+coeffs[3]*x^3 with three real roots, + this function returns the root of least magnitude.*/ +float solveCubicBlinnSmallest(vec4 coeffs) +{ + coeffs.xyz /= coeffs.w; + coeffs.yz /= 3.0; + + vec3 delta = vec3(fma(-coeffs.z, coeffs.z, coeffs.y), fma(-coeffs.z, coeffs.y, coeffs.x), coeffs.z * coeffs.x - coeffs.y * coeffs.y); + float discriminant = 4.0 * delta.x * delta.z - delta.y * delta.y; + + vec2 depressed = vec2(delta.z, -coeffs.x * delta.y + 2.0 * coeffs.y * delta.z); + float theta = abs(atan(coeffs.x * sqrt(discriminant), -depressed.y)) / 3.0; + vec2 sin_cos; + sincos(theta, sin_cos.x, sin_cos.y); + float tmp = 2.0 * sqrt(-depressed.x); + vec2 x = vec2(tmp * sin_cos.y, tmp * (-0.5 * sin_cos.y - 0.5 * sqrt(3.0) * sin_cos.x)); + vec2 s = (x.x + x.y < 2.0 * coeffs.y) ? vec2(-coeffs.x, x.x + coeffs.y) : vec2(-coeffs.x, x.y + coeffs.y); + + return s.x / s.y; +} + +/*! Given coefficients of a quartic polynomial + coeffs[0]+coeffs[1]*x+coeffs[2]*x^2+coeffs[3]*x^3+coeffs[4]*x^4 with four + real roots, this function returns all roots.*/ +vec4 solveQuarticNeumark(float coeffs[5]) +{ + // Normalization + float B = coeffs[3] / coeffs[4]; + float C = coeffs[2] / coeffs[4]; + float D = coeffs[1] / coeffs[4]; + float E = coeffs[0] / coeffs[4]; + + // Compute coefficients of the cubic resolvent + float P = -2.0*C; + float Q = C*C + B*D - 4.0*E; + float R = D*D + B*B*E -B*C*D; + + // Obtain the smallest cubic root + float y = solveCubicBlinnSmallest(vec4(R, Q, P, 1.0)); + + float BB = B*B; + float fy = 4.0 * y; + float BB_fy = BB - fy; + + float Z = C - y; + float ZZ = Z*Z; + float fE = 4.0 * E; + float ZZ_fE = ZZ - fE; + + float G, g, H, h; + // Compute the coefficients of the quadratics adaptively using the two + // proposed factorizations by Neumark. Choose the appropriate + // factorizations using the heuristic proposed by Herbison-Evans. + if (y < 0 || (ZZ + fE) * BB_fy > ZZ_fE * (BB + fy)) { + float tmp = sqrt(BB_fy); + G = (B + tmp) * 0.5; + g = (B - tmp) * 0.5; + + tmp = (B*Z - 2.0*D) / (2.0*tmp); + H = fma(Z, 0.5, tmp); + h = fma(Z, 0.5, -tmp); + } else { + float tmp = sqrt(ZZ_fE); + H = (Z + tmp) * 0.5; + h = (Z - tmp) * 0.5; + + tmp = (B*Z - 2.0*D) / (2.0*tmp); + G = fma(B, 0.5, tmp); + g = fma(B, 0.5, -tmp); + } + // Solve the quadratics + return vec4(solveQuadratic(vec3(1.0, G, H)), solveQuadratic(vec3(1.0, g, h))); +} + +/*! Definition of utility functions for quantization and dequantization of + power moments stored in 16 bits per moment. */ +void offsetMoments(inout vec2 b_even, inout vec2 b_odd, float sign) +{ + b_odd += 0.5 * sign; +} + +void quantizeMoments(out vec2 b_even_q, out vec2 b_odd_q, vec2 b_even, vec2 b_odd) +{ + b_odd_q = b_odd * mat2x2(1.5f, sqrt(3.0f)*0.5f, -2.0f, -sqrt(3.0f)*2.0f / 9.0f); + b_even_q = b_even * mat2x2(4.0f, 0.5f, -4.0f, 0.5f); +} + +void offsetAndDequantizeMoments(out vec2 b_even, out vec2 b_odd, vec2 b_even_q, vec2 b_odd_q) +{ + offsetMoments(b_even_q, b_odd_q, -1.0); + b_odd = b_odd_q * mat2x2(-1.0f / 3.0f, -0.75f, sqrt(3.0f), 0.75f*sqrt(3.0f)); + b_even = b_even_q * mat2x2(0.125f, -0.125f, 1.0f, 1.0f); +} + +void offsetMoments(inout vec3 b_even, inout vec3 b_odd, float sign) +{ + b_odd += 0.5 * sign; + b_even.z += 0.018888946f * sign; +} + +void quantizeMoments(out vec3 b_even_q, out vec3 b_odd_q, vec3 b_even, vec3 b_odd) +{ + const mat3x3 QuantizationMatrixOdd = mat3x3( + 2.5f, -1.87499864450f, 1.26583039016f, + -10.0f, 4.20757543111f, -1.47644882902f, + 8.0f, -1.83257678661f, 0.71061660238f); + const mat3x3 QuantizationMatrixEven = mat3x3( + 4.0f, 9.0f, -0.57759806484f, + -4.0f, -24.0f, 4.61936647543f, + 0.0f, 16.0f, -3.07953906655f); + b_odd_q = b_odd * QuantizationMatrixOdd; + b_even_q = b_even * QuantizationMatrixEven; +} + +void offsetAndDequantizeMoments(out vec3 b_even, out vec3 b_odd, vec3 b_even_q, vec3 b_odd_q) +{ + const mat3x3 QuantizationMatrixOdd = mat3x3( + -0.02877789192f, 0.09995235706f, 0.25893353755f, + 0.47635550422f, 0.84532580931f, 0.90779616657f, + 1.55242808973f, 1.05472570761f, 0.83327335647f); + const mat3x3 QuantizationMatrixEven = mat3x3( + 0.00001253044f, -0.24998746956f, -0.37498825271f, + 0.16668494186f, 0.16668494186f, 0.21876713299f, + 0.86602540579f, 0.86602540579f, 0.81189881793f); + offsetMoments(b_even_q, b_odd_q, -1.0); + b_odd = b_odd_q * QuantizationMatrixOdd; + b_even = b_even_q * QuantizationMatrixEven; +} + +void offsetMoments(inout vec4 b_even, inout vec4 b_odd, float sign) +{ + b_odd += 0.5 * sign; + b_even += vec4(0.972481993925964, 1.0, 0.999179192513328, 0.991778293073131) * sign; +} + +void quantizeMoments(out vec4 b_even_q, out vec4 b_odd_q, vec4 b_even, vec4 b_odd) +{ + const mat4x4 mat_odd = mat4x4(3.48044635732474, -27.5760737514826, 55.1267384344761, -31.5311110403183, + 1.26797185782836, -0.928755808743913, -2.07520453231032, 1.23598848322588, + -2.1671560004294, 6.17950199592966, -0.276515571579297, -4.23583042392097, + 0.974332879165755, -0.443426830933027, -0.360491648368785, 0.310149466050223); + const mat4x4 mat_even = mat4x4(0.280504133158527, -0.757633844606942, 0.392179589334688, -0.887531871812237, + -2.01362265883247, 0.221551373038988, -1.06107954265125, 2.83887201588367, + -7.31010494985321, 13.9855979699139, -0.114305766176437, -7.4361899359832, + -15.8954215629556, 79.6186327084103, -127.457278992502, 63.7349456687829); + b_odd_q = mat_odd * b_odd; + b_even_q = mat_even * b_even; +} + +void offsetAndDequantizeMoments(out vec4 b_even, out vec4 b_odd, vec4 b_even_q, vec4 b_odd_q) +{ + const mat4x4 mat_odd = mat4x4(-0.00482399708502382, -0.423201508674231, 0.0348312382605129, 1.67179208266592, + -0.0233402218644408, -0.832829097046478, 0.0193406040499625, 1.21021509068975, + -0.010888537031885, -0.926393772997063, -0.11723394414779, 0.983723301818275, + -0.0308713357806732, -0.937989172670245, -0.218033377677099, 0.845991731322996); + const mat4x4 mat_even = mat4x4(-0.976220278891035, -0.456139260269401, -0.0504335521016742, 0.000838800390651085, + -1.04828341778299, -0.229726640510149, 0.0259608334616091, -0.00133632693205861, + -1.03115268628604, -0.077844420809897, 0.00443408851014257, -0.0103744938457406, + -0.996038443434636, 0.0175438624416783, -0.0361414253243963, -0.00317839994022725); + offsetMoments(b_even_q, b_odd_q, -1.0); + b_odd = mat_odd * b_odd_q; + b_even = mat_even * b_even_q; +} + +/*! This function reconstructs the transmittance at the given depth from four + normalized power moments and the given zeroth moment.*/ +float computeTransmittanceAtDepthFrom4PowerMoments(float b_0, vec2 b_even, vec2 b_odd, float depth, float bias, float overestimation, vec4 bias_vector) +{ + vec4 b = vec4(b_odd.x, b_even.x, b_odd.y, b_even.y); + // Bias input data to avoid artifacts + b = mix(b, bias_vector, bias); + vec3 z; + z[0] = depth; + + // Compute a Cholesky factorization of the Hankel matrix B storing only non- + // trivial entries or related products + float L21D11=fma(-b[0], b[1], b[2]); + float D11=fma(-b[0], b[0], b[1]); + float InvD11=1.0f/D11; + float L21=L21D11*InvD11; + float SquaredDepthVariance=fma(-b[1], b[1], b[3]); + float D22=fma(-L21D11, L21, SquaredDepthVariance); + + // Obtain a scaled inverse image of bz=(1,z[0],z[0]*z[0])^T + vec3 c=vec3(1.0f, z[0], z[0]*z[0]); + // Forward substitution to solve L*c1=bz + c[1]-=b.x; + c[2]-=b.y+L21*c[1]; + // Scaling to solve D*c2=c1 + c[1]*=InvD11; + c[2]/=D22; + // Backward substitution to solve L^T*c3=c2 + c[1]-=L21*c[2]; + c[0]-=dot(c.yz, b.xy); + // Solve the quadratic equation c[0]+c[1]*z+c[2]*z^2 to obtain solutions + // z[1] and z[2] + float InvC2=1.0f/c[2]; + float p=c[1]*InvC2; + float q=c[0]*InvC2; + float D=(p*p*0.25f)-q; + float r=sqrt(D); + z[1]=-p*0.5f-r; + z[2]=-p*0.5f+r; + // Compute the absorbance by summing the appropriate weights + vec3 polynomial; + vec3 weight_factor = vec3(overestimation, (z[1] < z[0])?1.0f:0.0f, (z[2] < z[0])?1.0f:0.0f); + float f0=weight_factor[0]; + float f1=weight_factor[1]; + float f2=weight_factor[2]; + float f01=(f1-f0)/(z[1]-z[0]); + float f12=(f2-f1)/(z[2]-z[1]); + float f012=(f12-f01)/(z[2]-z[0]); + polynomial[0]=f012; + polynomial[1]=polynomial[0]; + polynomial[0]=f01-polynomial[0]*z[1]; + polynomial[2]=polynomial[1]; + polynomial[1]=polynomial[0]-polynomial[1]*z[0]; + polynomial[0]=f0-polynomial[0]*z[0]; + float absorbance = polynomial[0] + dot(b.xy, polynomial.yz);; + // Turn the normalized absorbance into transmittance + return saturate(exp(-b_0 * absorbance)); +} + +/*! This function reconstructs the transmittance at the given depth from six + normalized power moments and the given zeroth moment.*/ +float computeTransmittanceAtDepthFrom6PowerMoments(float b_0, vec3 b_even, vec3 b_odd, float depth, float bias, float overestimation, float bias_vector[6]) +{ + float b[6] = { b_odd.x, b_even.x, b_odd.y, b_even.y, b_odd.z, b_even.z }; + // Bias input data to avoid artifacts + for (int i = 0; i != 6; ++i) { + b[i] = mix(b[i], bias_vector[i], bias); + } + + vec4 z; + z[0] = depth; + + // Compute a Cholesky factorization of the Hankel matrix B storing only non- + // trivial entries or related products + float InvD11 = 1.0f / fma(-b[0], b[0], b[1]); + float L21D11 = fma(-b[0], b[1], b[2]); + float L21 = L21D11*InvD11; + float D22 = fma(-L21D11, L21, fma(-b[1], b[1], b[3])); + float L31D11 = fma(-b[0], b[2], b[3]); + float L31 = L31D11*InvD11; + float InvD22 = 1.0f / D22; + float L32D22 = fma(-L21D11, L31, fma(-b[1], b[2], b[4])); + float L32 = L32D22*InvD22; + float D33 = fma(-b[2], b[2], b[5]) - dot(vec2(L31D11, L32D22), vec2(L31, L32)); + float InvD33 = 1.0f / D33; + + // Construct the polynomial whose roots have to be points of support of the + // canonical distribution: bz=(1,z[0],z[0]*z[0],z[0]*z[0]*z[0])^T + vec4 c; + c[0] = 1.0f; + c[1] = z[0]; + c[2] = c[1] * z[0]; + c[3] = c[2] * z[0]; + // Forward substitution to solve L*c1=bz + c[1] -= b[0]; + c[2] -= fma(L21, c[1], b[1]); + c[3] -= b[2] + dot(vec2(L31, L32), c.yz); + // Scaling to solve D*c2=c1 + c.yzw *= vec3(InvD11, InvD22, InvD33); + // Backward substitution to solve L^T*c3=c2 + c[2] -= L32*c[3]; + c[1] -= dot(vec2(L21, L31), c.zw); + c[0] -= dot(vec3(b[0], b[1], b[2]), c.yzw); + + // Solve the cubic equation + z.yzw = SolveCubic(c); + + // Compute the absorbance by summing the appropriate weights + vec4 weigth_factor; + weigth_factor[0] = overestimation; + weigth_factor.yzw = vec3(greaterThan(z.yzw, z.xxx)); + // Construct an interpolation polynomial + float f0 = weigth_factor[0]; + float f1 = weigth_factor[1]; + float f2 = weigth_factor[2]; + float f3 = weigth_factor[3]; + float f01 = (f1 - f0) / (z[1] - z[0]); + float f12 = (f2 - f1) / (z[2] - z[1]); + float f23 = (f3 - f2) / (z[3] - z[2]); + float f012 = (f12 - f01) / (z[2] - z[0]); + float f123 = (f23 - f12) / (z[3] - z[1]); + float f0123 = (f123 - f012) / (z[3] - z[0]); + vec4 polynomial; + // f012+f0123 *(z-z2) + polynomial[0] = fma(-f0123, z[2], f012); + polynomial[1] = f0123; + // *(z-z1) +f01 + polynomial[2] = polynomial[1]; + polynomial[1] = fma(polynomial[1], -z[1], polynomial[0]); + polynomial[0] = fma(polynomial[0], -z[1], f01); + // *(z-z0) +f0 + polynomial[3] = polynomial[2]; + polynomial[2] = fma(polynomial[2], -z[0], polynomial[1]); + polynomial[1] = fma(polynomial[1], -z[0], polynomial[0]); + polynomial[0] = fma(polynomial[0], -z[0], f0); + float absorbance = dot(polynomial, vec4 (1.0, b[0], b[1], b[2])); + // Turn the normalized absorbance into transmittance + return saturate(exp(-b_0 * absorbance)); +} + +/*! This function reconstructs the transmittance at the given depth from eight + normalized power moments and the given zeroth moment.*/ +float computeTransmittanceAtDepthFrom8PowerMoments(float b_0, vec4 b_even, vec4 b_odd, float depth, float bias, float overestimation, float bias_vector[8]) +{ + float b[8] = { b_odd.x, b_even.x, b_odd.y, b_even.y, b_odd.z, b_even.z, b_odd.w, b_even.w }; + // Bias input data to avoid artifacts + for (int i = 0; i != 8; ++i) { + b[i] = mix(b[i], bias_vector[i], bias); + } + + float z[5]; + z[0] = depth; + + // Compute a Cholesky factorization of the Hankel matrix B storing only non-trivial entries or related products + float D22 = fma(-b[0], b[0], b[1]); + float InvD22 = 1.0 / D22; + float L32D22 = fma(-b[1], b[0], b[2]); + float L32 = L32D22 * InvD22; + float L42D22 = fma(-b[2], b[0], b[3]); + float L42 = L42D22 * InvD22; + float L52D22 = fma(-b[3], b[0], b[4]); + float L52 = L52D22 * InvD22; + + float D33 = fma(-L32, L32D22, fma(-b[1], b[1], b[3])); + float InvD33 = 1.0 / D33; + float L43D33 = fma(-L42, L32D22, fma(-b[2], b[1], b[4])); + float L43 = L43D33 * InvD33; + float L53D33 = fma(-L52, L32D22, fma(-b[3], b[1], b[5])); + float L53 = L53D33 * InvD33; + + float D44 = fma(-b[2], b[2], b[5]) - dot(vec2(L42, L43), vec2(L42D22, L43D33)); + float InvD44 = 1.0 / D44; + float L54D44 = fma(-b[3], b[2], b[6]) - dot(vec2(L52, L53), vec2(L42D22, L43D33)); + float L54 = L54D44 * InvD44; + + float D55 = fma(-b[3], b[3], b[7]) - dot(vec3(L52, L53, L54), vec3(L52D22, L53D33, L54D44)); + float InvD55 = 1.0 / D55; + + // Construct the polynomial whose roots have to be points of support of the + // Canonical distribution: + // bz = (1,z[0],z[0]^2,z[0]^3,z[0]^4)^T + float c[5]; + c[0] = 1.0; + c[1] = z[0]; + c[2] = c[1] * z[0]; + c[3] = c[2] * z[0]; + c[4] = c[3] * z[0]; + + // Forward substitution to solve L*c1 = bz + c[1] -= b[0]; + c[2] -= fma(L32, c[1], b[1]); + c[3] -= b[2] + dot(vec2(L42, L43), vec2(c[1], c[2])); + c[4] -= b[3] + dot(vec3(L52, L53, L54), vec3(c[1], c[2], c[3])); + + // Scaling to solve D*c2 = c1 + //c = c .*[1, InvD22, InvD33, InvD44, InvD55]; + c[1] *= InvD22; + c[2] *= InvD33; + c[3] *= InvD44; + c[4] *= InvD55; + + // Backward substitution to solve L^T*c3 = c2 + c[3] -= L54 * c[4]; + c[2] -= dot(vec2(L53, L43), vec2(c[4], c[3])); + c[1] -= dot(vec3(L52, L42, L32), vec3(c[4], c[3], c[2])); + c[0] -= dot(vec4(b[3], b[2], b[1], b[0]), vec4(c[4], c[3], c[2], c[1])); + + // Solve the quartic equation + vec4 zz = solveQuarticNeumark(c); + z[1] = zz[0]; + z[2] = zz[1]; + z[3] = zz[2]; + z[4] = zz[3]; + + // Compute the absorbance by summing the appropriate weights + vec4 weigth_factor = vec4(lessThanEqual(vec4(z[1], z[2], z[3], z[4]), z[0].xxxx)); + // Construct an interpolation polynomial + float f0 = overestimation; + float f1 = weigth_factor[0]; + float f2 = weigth_factor[1]; + float f3 = weigth_factor[2]; + float f4 = weigth_factor[3]; + float f01 = (f1 - f0) / (z[1] - z[0]); + float f12 = (f2 - f1) / (z[2] - z[1]); + float f23 = (f3 - f2) / (z[3] - z[2]); + float f34 = (f4 - f3) / (z[4] - z[3]); + float f012 = (f12 - f01) / (z[2] - z[0]); + float f123 = (f23 - f12) / (z[3] - z[1]); + float f234 = (f34 - f23) / (z[4] - z[2]); + float f0123 = (f123 - f012) / (z[3] - z[0]); + float f1234 = (f234 - f123) / (z[4] - z[1]); + float f01234 = (f1234 - f0123) / (z[4] - z[0]); + + float Polynomial_0; + vec4 Polynomial; + // f0123 + f01234 * (z - z3) + Polynomial_0 = fma(-f01234, z[3], f0123); + Polynomial[0] = f01234; + // * (z - z2) + f012 + Polynomial[1] = Polynomial[0]; + Polynomial[0] = fma(-Polynomial[0], z[2], Polynomial_0); + Polynomial_0 = fma(-Polynomial_0, z[2], f012); + // * (z - z1) + f01 + Polynomial[2] = Polynomial[1]; + Polynomial[1] = fma(-Polynomial[1], z[1], Polynomial[0]); + Polynomial[0] = fma(-Polynomial[0], z[1], Polynomial_0); + Polynomial_0 = fma(-Polynomial_0, z[1], f01); + // * (z - z0) + f1 + Polynomial[3] = Polynomial[2]; + Polynomial[2] = fma(-Polynomial[2], z[0], Polynomial[1]); + Polynomial[1] = fma(-Polynomial[1], z[0], Polynomial[0]); + Polynomial[0] = fma(-Polynomial[0], z[0], Polynomial_0); + Polynomial_0 = fma(-Polynomial_0, z[0], f0); + float absorbance = Polynomial_0 + dot(Polynomial, vec4(b[0], b[1], b[2], b[3])); + // Turn the normalized absorbance into transmittance + return saturate(exp(-b_0 * absorbance)); +} diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl new file mode 100644 index 000000000..f26d5582e --- /dev/null +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl @@ -0,0 +1,253 @@ +/*! \file + This header provides the functionality to create the vectors of moments and + to blend surfaces together with an appropriately reconstructed + transmittance. It is needed for both additive passes of moment-based OIT. +*/ + +//cbuffer MomentOIT +//{ +// struct { +// vec4 wrapping_zone_parameters; +// float overestimation; +// float moment_bias; +// }MomentOIT; +//}; + +#include "flywheel:internal/mboit/moment_math.glsl" + +const float moment_bias = 0.25; +const float overestimation = 0.25; +const vec4 wrapping_zone_parameters = vec4(0.); + + +void clip(float a) { + if (a < 0.) { + discard; + } +} + +// jozu: The trigonometric moments and higher order power moments rely on a second render target +// which the java side is not set up to support. Trying to enable them as is will cause compile errors also. +#define NUM_MOMENTS 4 + +#define SINGLE_PRECISION 1 + +#ifdef _FLW_GENERATE_MOMENTS +/*! Generation of moments in case that rasterizer ordered views are used. + This includes the case if moments are stored in 16 bits. */ + +/*! This functions relies on fixed function additive blending to compute the + vector of moments.moment vector. The shader that calls this function must + provide the required render targets.*/ +#if NUM_MOMENTS == 4 +void generateMoments(float depth, float transmittance, vec4 wrapping_zone_parameters, out float b_0, out vec4 b) +#elif NUM_MOMENTS == 6 +#if USE_R_RG_RBBA_FOR_MBOIT6 +void generateMoments(float depth, float transmittance, vec4 wrapping_zone_parameters, out float b_0, out vec2 b_12, out vec4 b_3456) +#else +void generateMoments(float depth, float transmittance, vec4 wrapping_zone_parameters, out float b_0, out vec2 b_12, out vec2 b_34, out vec2 b_56) +#endif +#elif NUM_MOMENTS == 8 +void generateMoments(float depth, float transmittance, vec4 wrapping_zone_parameters, out float b_0, out vec4 b_even, out vec4 b_odd) +#endif +{ + transmittance = max(transmittance, 0.000001); + float absorbance = -log(transmittance); + + b_0 = absorbance; + #if TRIGONOMETRIC + float phase = fma(depth, wrapping_zone_parameters.y, wrapping_zone_parameters.y); + vec2 circle_point = vec2(sin(phas), cos(phase)); + + vec2 circle_point_pow2 = Multiply(circle_point, circle_point); + #if NUM_MOMENTS == 4 + b = vec4(circle_point, circle_point_pow2) * absorbance; + #elif NUM_MOMENTS == 6 + b_12 = circle_point * absorbance; + #if USE_R_RG_RBBA_FOR_MBOIT6 + b_3456 = vec4(circle_point_pow2, Multiply(circle_point, circle_point_pow2)) * absorbance; + #else + b_34 = circle_point_pow2 * absorbance; + b_56 = Multiply(circle_point, circle_point_pow2) * absorbance; + #endif + #elif NUM_MOMENTS == 8 + b_even = vec4(circle_point_pow2, Multiply(circle_point_pow2, circle_point_pow2)) * absorbance; + b_odd = vec4(circle_point, Multiply(circle_point, circle_point_pow2)) * absorbance; + #endif + #else + float depth_pow2 = depth * depth; + float depth_pow4 = depth_pow2 * depth_pow2; + #if NUM_MOMENTS == 4 + b = vec4(depth, depth_pow2, depth_pow2 * depth, depth_pow4) * absorbance; + #elif NUM_MOMENTS == 6 + b_12 = vec2(depth, depth_pow2) * absorbance; + #if USE_R_RG_RBBA_FOR_MBOIT6 + b_3456 = vec4(depth_pow2 * depth, depth_pow4, depth_pow4 * depth, depth_pow4 * depth_pow2) * absorbance; + #else + b_34 = vec2(depth_pow2 * depth, depth_pow4) * absorbance; + b_56 = vec2(depth_pow4 * depth, depth_pow4 * depth_pow2) * absorbance; + #endif + #elif NUM_MOMENTS == 8 + float depth_pow6 = depth_pow4 * depth_pow2; + b_even = vec4(depth_pow2, depth_pow4, depth_pow6, depth_pow6 * depth_pow2) * absorbance; + b_odd = vec4(depth, depth_pow2 * depth, depth_pow4 * depth, depth_pow6 * depth) * absorbance; + #endif + #endif +} + +#else//MOMENT_GENERATION is disabled + +layout (binding = 7) uniform sampler2D _flw_zeroth_moment_sampler; +layout (binding = 8) uniform sampler2D _flw_moments_sampler; +#if USE_R_RG_RBBA_FOR_MBOIT6 +uniform sampler2D extra_moments; +#endif + +/*! This function is to be called from the shader that composites the + transparent fragments. It reads the moments and calls the appropriate + function to reconstruct the transmittance at the specified depth.*/ +void resolveMoments(out float transmittance_at_depth, out float total_transmittance, float depth, vec2 sv_pos) +{ + ivec2 idx0 = ivec2(sv_pos); + ivec2 idx1 = idx0; + + transmittance_at_depth = 1; + total_transmittance = 1; + + float b_0 = texelFetch(_flw_zeroth_moment_sampler, idx0, 0).x; + clip(b_0 - 0.00100050033f); + total_transmittance = exp(-b_0); + + #if NUM_MOMENTS == 4 + #if TRIGONOMETRIC + vec4 b_tmp = texelFetch(_flw_moments_sampler, idx0, 0); + vec2 trig_b[2]; + trig_b[0] = b_tmp.xy; + trig_b[1] = b_tmp.zw; + #if SINGLE_PRECISION + trig_b[0] /= b_0; + trig_b[1] /= b_0; + #else + trig_b[0] = fma(trig_b[0], 2.0, -1.0); + trig_b[1] = fma(trig_b[1], 2.0, -1.0); + #endif + transmittance_at_depth = computeTransmittanceAtDepthFrom2TrigonometricMoments(b_0, trig_b, depth, moment_bias, overestimation, wrapping_zone_parameters); + #else + vec4 b_1234 = texelFetch(_flw_moments_sampler, idx0, 0).xyzw; + #if SINGLE_PRECISION + vec2 b_even = b_1234.yw; + vec2 b_odd = b_1234.xz; + + b_even /= b_0; + b_odd /= b_0; + + const vec4 bias_vector = vec4(0, 0.375, 0, 0.375); + #else + vec2 b_even_q = b_1234.yw; + vec2 b_odd_q = b_1234.xz; + + // Dequantize the moments + vec2 b_even; + vec2 b_odd; + offsetAndDequantizeMoments(b_even, b_odd, b_even_q, b_odd_q); + const vec4 bias_vector = vec4(0, 0.628, 0, 0.628); + #endif + transmittance_at_depth = computeTransmittanceAtDepthFrom4PowerMoments(b_0, b_even, b_odd, depth, moment_bias, overestimation, bias_vector); + #endif + #elif NUM_MOMENTS == 6 + ivec2 idx2 = idx0; + #if TRIGONOMETRIC + vec2 trig_b[3]; + trig_b[0] = texelFetch(_flw_moments_sampler, idx0, 0).xy; + #if USE_R_RG_RBBA_FOR_MBOIT6 + vec4 tmp = texelFetch(extra_moments, idx0, 0); + trig_b[1] = tmp.xy; + trig_b[2] = tmp.zw; + #else + trig_b[1] = texelFetch(_flw_moments_sampler, idx1, 0).xy; + trig_b[2] = texelFetch(_flw_moments_sampler, idx2, 0).xy; + #endif + #if SINGLE_PRECISION + trig_b[0] /= b_0; + trig_b[1] /= b_0; + trig_b[2] /= b_0; + #else + trig_b[0] = fma(trig_b[0], 2.0, -1.0); + trig_b[1] = fma(trig_b[1], 2.0, -1.0); + trig_b[2] = fma(trig_b[2], 2.0, -1.0); + #endif + transmittance_at_depth = computeTransmittanceAtDepthFrom3TrigonometricMoments(b_0, trig_b, depth, moment_bias, overestimation, wrapping_zone_parameters); + #else + vec2 b_12 = texelFetch(_flw_moments_sampler, idx0, 0).xy; + #if USE_R_RG_RBBA_FOR_MBOIT6 + vec4 tmp = texelFetch(extra_moments, idx0, 0); + vec2 b_34 = tmp.xy; + vec2 b_56 = tmp.zw; + #else + vec2 b_34 = texelFetch(_flw_moments_sampler, idx1, 0).xy; + vec2 b_56 = texelFetch(_flw_moments_sampler, idx2, 0).xy; + #endif + #if SINGLE_PRECISION + vec3 b_even = vec3(b_12.y, b_34.y, b_56.y); + vec3 b_odd = vec3(b_12.x, b_34.x, b_56.x); + + b_even /= b_0; + b_odd /= b_0; + + const float bias_vector[6] = { 0, 0.48, 0, 0.451, 0, 0.45 }; + #else + vec3 b_even_q = vec3(b_12.y, b_34.y, b_56.y); + vec3 b_odd_q = vec3(b_12.x, b_34.x, b_56.x); + // Dequantize b_0 and the other moments + vec3 b_even; + vec3 b_odd; + offsetAndDequantizeMoments(b_even, b_odd, b_even_q, b_odd_q); + + const float bias_vector[6] = { 0, 0.5566, 0, 0.489, 0, 0.47869382 }; + #endif + transmittance_at_depth = computeTransmittanceAtDepthFrom6PowerMoments(b_0, b_even, b_odd, depth, moment_bias, overestimation, bias_vector); + #endif + #elif NUM_MOMENTS == 8 + #if TRIGONOMETRIC + vec4 b_tmp = texelFetch(_flw_moments_sampler, idx0, 0); + vec4 b_tmp2 = texelFetch(_flw_moments_sampler, idx1, 0); + #if SINGLE_PRECISION + vec2 trig_b[4] = { + b_tmp2.xy / b_0, + b_tmp.xy / b_0, + b_tmp2.zw / b_0, + b_tmp.zw / b_0 + }; + #else + vec2 trig_b[4] = { + fma(b_tmp2.xy, 2.0, -1.0), + fma(b_tmp.xy, 2.0, -1.0), + fma(b_tmp2.zw, 2.0, -1.0), + fma(b_tmp.zw, 2.0, -1.0) + }; + #endif + transmittance_at_depth = computeTransmittanceAtDepthFrom4TrigonometricMoments(b_0, trig_b, depth, moment_bias, overestimation, wrapping_zone_parameters); + #else + #if SINGLE_PRECISION + vec4 b_even = texelFetch(_flw_moments_sampler, idx0, 0); + vec4 b_odd = texelFetch(_flw_moments_sampler, idx1, 0); + + b_even /= b_0; + b_odd /= b_0; + const float bias_vector[8] = { 0, 0.75, 0, 0.67666666666666664, 0, 0.63, 0, 0.60030303030303034 }; + #else + vec4 b_even_q = texelFetch(_flw_moments_sampler, idx0, 0); + vec4 b_odd_q = texelFetch(_flw_moments_sampler, idx1, 0); + + // Dequantize the moments + vec4 b_even; + vec4 b_odd; + offsetAndDequantizeMoments(b_even, b_odd, b_even_q, b_odd_q); + const float bias_vector[8] = { 0, 0.42474916387959866, 0, 0.22407802675585284, 0, 0.15369230769230768, 0, 0.12900440529089119 }; + #endif + transmittance_at_depth = computeTransmittanceAtDepthFrom8PowerMoments(b_0, b_even, b_odd, depth, moment_bias, overestimation, bias_vector); + #endif + #endif + +} +#endif diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl new file mode 100644 index 000000000..4fc142d8f --- /dev/null +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl @@ -0,0 +1,311 @@ +/*! \file + This header provides the utility functions to reconstruct the transmittance + from a given vector of trigonometric moments (2, 3 or 4 trigonometric + moments) at a specified depth.*/ +#include "flywheel:internal/mboit/complex_algebra.glsl" + +/*! This utility function turns a point on the unit circle into a scalar + parameter. It is guaranteed to grow monotonically for (cos(phi),sin(phi)) + with phi in 0 to 2*pi. There are no other guarantees. In particular it is + not an arclength parametrization. If you change this function, you must + also change circleToParameter() in MomentOIT.cpp.*/ +float circleToParameter(vec2 circle_point){ + float result=abs(circle_point.y)-abs(circle_point.x); + result=(circle_point.x<0.0f)?(2.0f-result):result; + return (circle_point.y<0.0f)?(6.0f-result):result; +} + +/*! This utility function returns the appropriate weight factor for a root at + the given location. Both inputs are supposed to be unit vectors. If a + circular arc going counter clockwise from (1.0,0.0) meets root first, it + returns 1.0, otherwise 0.0 or a linear ramp in the wrapping zone.*/ +float getRootWeightFactor(float reference_parameter, float root_parameter, vec4 wrapping_zone_parameters){ + float binary_weight_factor=(root_parameter Date: Mon, 17 Feb 2025 20:12:17 -0800 Subject: [PATCH 07/23] 8 moments - Use 8 power moments - Fix compile errors with trig moments --- .../flywheel/backend/Samplers.java | 3 +- .../engine/indirect/MboitFramebuffer.java | 73 +++++++++++++++---- .../flywheel/flywheel/internal/common.frag | 5 +- .../internal/mboit/complex_algebra.glsl | 16 +++- .../flywheel/internal/mboit/moment_math.glsl | 12 +-- .../flywheel/internal/mboit/moment_oit.glsl | 48 ++++++------ .../mboit/trigonometric_moment_math.glsl | 8 +- .../vanillin/visuals/ShulkerBoxVisual.java | 7 -- 8 files changed, 104 insertions(+), 68 deletions(-) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java b/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java index b6e40e291..e89acccbb 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java @@ -12,5 +12,6 @@ public class Samplers { public static final GlTextureUnit LIGHT_SECTIONS = GlTextureUnit.T6; public static final GlTextureUnit ZEROTH_MOMENT = GlTextureUnit.T7; - public static final GlTextureUnit MOMENTS = GlTextureUnit.T8; + public static final GlTextureUnit MOMENTS0 = GlTextureUnit.T8; + public static final GlTextureUnit MOMENTS1 = GlTextureUnit.T9; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MboitFramebuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MboitFramebuffer.java index 06d283f20..747020e73 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MboitFramebuffer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MboitFramebuffer.java @@ -10,6 +10,7 @@ import dev.engine_room.flywheel.backend.Samplers; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; import dev.engine_room.flywheel.backend.gl.GlTextureUnit; import net.minecraft.client.Minecraft; +import net.minecraft.util.Mth; public class MboitFramebuffer { @@ -18,7 +19,8 @@ public class MboitFramebuffer { private final int vao; public int zerothMoment; - public int moments; + public int moments0; + public int moments1; public int accumulate; private int lastWidth = -1; @@ -44,10 +46,11 @@ public class MboitFramebuffer { GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, mainRenderTarget.getDepthTextureId(), 0); - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT0, GL46.GL_COLOR_ATTACHMENT1}); + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT0, GL46.GL_COLOR_ATTACHMENT1, GL46.GL_COLOR_ATTACHMENT2}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 1, new float[]{0, 0, 0, 0}); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 2, new float[]{0, 0, 0, 0}); GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); } @@ -62,10 +65,13 @@ public class MboitFramebuffer { Samplers.ZEROTH_MOMENT.makeActive(); GlStateManager._bindTexture(zerothMoment); - Samplers.MOMENTS.makeActive(); - GlStateManager._bindTexture(moments); + Samplers.MOMENTS0.makeActive(); + GlStateManager._bindTexture(moments0); - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT2}); + Samplers.MOMENTS1.makeActive(); + GlStateManager._bindTexture(moments1); + + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT3}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); @@ -100,13 +106,18 @@ public class MboitFramebuffer { } public void delete() { - GL46.glDeleteTextures(zerothMoment); - GL46.glDeleteTextures(moments); - GL46.glDeleteTextures(accumulate); + deleteTextures(); GL46.glDeleteFramebuffers(fbo); GL46.glDeleteVertexArrays(vao); } + private void deleteTextures() { + GL46.glDeleteTextures(zerothMoment); + GL46.glDeleteTextures(moments0); + GL46.glDeleteTextures(moments1); + GL46.glDeleteTextures(accumulate); + } + private void createTextures(int width, int height) { if (lastWidth == width && lastHeight == height) { return; @@ -115,16 +126,17 @@ public class MboitFramebuffer { lastWidth = width; lastHeight = height; - GL46.glDeleteTextures(zerothMoment); - GL46.glDeleteTextures(moments); - GL46.glDeleteTextures(accumulate); + deleteTextures(); zerothMoment = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); - moments = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); + moments0 = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); + moments1 = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); accumulate = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); GL46.glTextureStorage2D(zerothMoment, 1, GL32.GL_R16F, width, height); - GL46.glTextureStorage2D(moments, 1, GL32.GL_RGBA16F, width, height); + GL46.glTextureStorage2D(moments0, 1, GL32.GL_RGBA16F, width, height); + GL46.glTextureStorage2D(moments1, 1, GL32.GL_RGBA16F, width, height); + GL46.glTextureStorage2D(accumulate, 1, GL32.GL_RGBA16F, width, height); // for (int tex : new int[]{zerothMoment, moments, composite}) { @@ -136,7 +148,38 @@ public class MboitFramebuffer { // } GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT0, zerothMoment, 0); - GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT1, moments, 0); - GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT2, accumulate, 0); + GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT1, moments0, 0); + GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT2, moments1, 0); + GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT3, accumulate, 0); + } + + float circleToParameter(float angle) { + float x = Mth.cos(angle); + float y = Mth.sin(angle); + float result = Mth.abs(y) - Mth.abs(x); + result = (x < 0.0f) ? (2.0f - result) : result; + result = (y < 0.0f) ? (6.0f - result) : result; + result += (angle >= 2.0f * Mth.PI) ? 8.0f : 0.0f; + return result; + } + + void computeWrappingZoneParameters(float[] out) { + computeWrappingZoneParameters(out, 0.1f * Mth.PI); + } + + /*! Given an angle in radians providing the size of the wrapping zone, this + function computes all constants required by the shader.*/ + void computeWrappingZoneParameters(float[] p_out_wrapping_zone_parameters, float new_wrapping_zone_angle) { + p_out_wrapping_zone_parameters[0] = new_wrapping_zone_angle; + p_out_wrapping_zone_parameters[1] = Mth.PI - 0.5f * new_wrapping_zone_angle; + if (new_wrapping_zone_angle <= 0.0f) { + p_out_wrapping_zone_parameters[2] = 0.0f; + p_out_wrapping_zone_parameters[3] = 0.0f; + } else { + float zone_end_parameter = 7; + float zone_begin_parameter = circleToParameter(2.0f * Mth.PI - new_wrapping_zone_angle); + p_out_wrapping_zone_parameters[2] = 1.0f / (zone_end_parameter - zone_begin_parameter); + p_out_wrapping_zone_parameters[3] = 1.0f - zone_end_parameter * p_out_wrapping_zone_parameters[2]; + } } } diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag index afd8ae647..3cb61f8a5 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag @@ -21,7 +21,8 @@ flat in uvec2 _flw_ids; #ifdef _FLW_OIT #ifdef _FLW_GENERATE_MOMENTS layout (location = 0) out float _flw_zerothMoment_out; -layout (location = 1) out vec4 _flw_moments_out; +layout (location = 1) out vec4 _flw_moments0_out; +layout (location = 2) out vec4 _flw_moments1_out; #endif #ifdef _FLW_RESOLVE_MOMENTS layout (location = 0) out vec4 _flw_accumulate_out; @@ -133,7 +134,7 @@ void _flw_main() { #ifdef _FLW_GENERATE_MOMENTS - generateMoments(depth, 1 - color.a, vec4(0), _flw_zerothMoment_out, _flw_moments_out); + generateMoments(depth, 1 - color.a, _flw_zerothMoment_out, _flw_moments0_out, _flw_moments1_out); #endif #ifdef _FLW_RESOLVE_MOMENTS diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl index b2cf49485..30848e3f7 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl @@ -2,6 +2,16 @@ This header defines utility functions to deal with complex numbers and complex polynomials.*/ +void sincos(float theta, out float s, out float c) { + s = sin(theta); + c = cos(theta); +} + +float saturate(float a) { + return clamp(a, 0., 1.); +} + + /*! Returns the complex conjugate of the given complex number (i.e. it changes the sign of the y-component).*/ vec2 Conjugate(vec2 Z){ @@ -45,11 +55,11 @@ vec2 Cube(vec2 Z){ \sa SquareRoot() */ vec2 SquareRootUnsafe(vec2 Z){ float ZLengthSq=dot(Z, Z); - float ZLengthInv=rsqrt(ZLengthSq); + float ZLengthInv=inversesqrt(ZLengthSq); vec2 UnnormalizedRoot=Z*ZLengthInv+vec2(1.0f, 0.0f); float UnnormalizedRootLengthSq=dot(UnnormalizedRoot, UnnormalizedRoot); float NormalizationFactorInvSq=UnnormalizedRootLengthSq*ZLengthInv; - float NormalizationFactor=rsqrt(NormalizationFactorInvSq); + float NormalizationFactor=inversesqrt(NormalizationFactorInvSq); return NormalizationFactor*UnnormalizedRoot; } /*! This utility function computes one square root of the given complex value. @@ -65,7 +75,7 @@ vec2 SquareRoot(vec2 Z){ other roots can be found by multiplication by cubic roots of unity. \note This function has various discontinuities.*/ vec2 CubicRoot(vec2 Z){ - float Argument=atan2(Z.y, Z.x); + float Argument=atan(Z.y, Z.x); float NewArgument=Argument/3.0f; vec2 NormalizedRoot; sincos(NewArgument, NormalizedRoot.y, NormalizedRoot.x); diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl index 0c2d23b72..4f730eada 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl @@ -5,17 +5,7 @@ roots of polynomials up to degree four are defined. */ -//#include "flywheel:internal/mboit/trigonometric_moment_math.glsl" - -void sincos(float theta, out float s, out float c) { - s = sin(theta); - c = cos(theta); -} - -float saturate(float a) { - return clamp(a, 0., 1.); -} - +#include "flywheel:internal/mboit/trigonometric_moment_math.glsl" /*! Given coefficients of a quadratic polynomial A*x^2+B*x+C, this function diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl index f26d5582e..23ef4ec03 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl @@ -17,7 +17,7 @@ const float moment_bias = 0.25; const float overestimation = 0.25; -const vec4 wrapping_zone_parameters = vec4(0.); +const vec4 wrapping_zone_parameters = vec4(0.31415927, 2.984513, 2.7934167, -18.553917); void clip(float a) { @@ -28,7 +28,7 @@ void clip(float a) { // jozu: The trigonometric moments and higher order power moments rely on a second render target // which the java side is not set up to support. Trying to enable them as is will cause compile errors also. -#define NUM_MOMENTS 4 +#define NUM_MOMENTS 8 #define SINGLE_PRECISION 1 @@ -40,15 +40,15 @@ void clip(float a) { vector of moments.moment vector. The shader that calls this function must provide the required render targets.*/ #if NUM_MOMENTS == 4 -void generateMoments(float depth, float transmittance, vec4 wrapping_zone_parameters, out float b_0, out vec4 b) +void generateMoments(float depth, float transmittance, out float b_0, out vec4 b) #elif NUM_MOMENTS == 6 #if USE_R_RG_RBBA_FOR_MBOIT6 -void generateMoments(float depth, float transmittance, vec4 wrapping_zone_parameters, out float b_0, out vec2 b_12, out vec4 b_3456) +void generateMoments(float depth, float transmittance, out float b_0, out vec2 b_12, out vec4 b_3456) #else -void generateMoments(float depth, float transmittance, vec4 wrapping_zone_parameters, out float b_0, out vec2 b_12, out vec2 b_34, out vec2 b_56) +void generateMoments(float depth, float transmittance, out float b_0, out vec2 b_12, out vec2 b_34, out vec2 b_56) #endif #elif NUM_MOMENTS == 8 -void generateMoments(float depth, float transmittance, vec4 wrapping_zone_parameters, out float b_0, out vec4 b_even, out vec4 b_odd) +void generateMoments(float depth, float transmittance, out float b_0, out vec4 b_even, out vec4 b_odd) #endif { transmittance = max(transmittance, 0.000001); @@ -57,7 +57,7 @@ void generateMoments(float depth, float transmittance, vec4 wrapping_zone_parame b_0 = absorbance; #if TRIGONOMETRIC float phase = fma(depth, wrapping_zone_parameters.y, wrapping_zone_parameters.y); - vec2 circle_point = vec2(sin(phas), cos(phase)); + vec2 circle_point = vec2(sin(phase), cos(phase)); vec2 circle_point_pow2 = Multiply(circle_point, circle_point); #if NUM_MOMENTS == 4 @@ -98,10 +98,8 @@ void generateMoments(float depth, float transmittance, vec4 wrapping_zone_parame #else//MOMENT_GENERATION is disabled layout (binding = 7) uniform sampler2D _flw_zeroth_moment_sampler; -layout (binding = 8) uniform sampler2D _flw_moments_sampler; -#if USE_R_RG_RBBA_FOR_MBOIT6 -uniform sampler2D extra_moments; -#endif +layout (binding = 8) uniform sampler2D _flw_moments0_sampler; +layout (binding = 9) uniform sampler2D _flw_moments1_sampler; /*! This function is to be called from the shader that composites the transparent fragments. It reads the moments and calls the appropriate @@ -120,7 +118,7 @@ void resolveMoments(out float transmittance_at_depth, out float total_transmitta #if NUM_MOMENTS == 4 #if TRIGONOMETRIC - vec4 b_tmp = texelFetch(_flw_moments_sampler, idx0, 0); + vec4 b_tmp = texelFetch(_flw_moments0_sampler, idx0, 0); vec2 trig_b[2]; trig_b[0] = b_tmp.xy; trig_b[1] = b_tmp.zw; @@ -133,7 +131,7 @@ void resolveMoments(out float transmittance_at_depth, out float total_transmitta #endif transmittance_at_depth = computeTransmittanceAtDepthFrom2TrigonometricMoments(b_0, trig_b, depth, moment_bias, overestimation, wrapping_zone_parameters); #else - vec4 b_1234 = texelFetch(_flw_moments_sampler, idx0, 0).xyzw; + vec4 b_1234 = texelFetch(_flw_moments0_sampler, idx0, 0).xyzw; #if SINGLE_PRECISION vec2 b_even = b_1234.yw; vec2 b_odd = b_1234.xz; @@ -158,14 +156,14 @@ void resolveMoments(out float transmittance_at_depth, out float total_transmitta ivec2 idx2 = idx0; #if TRIGONOMETRIC vec2 trig_b[3]; - trig_b[0] = texelFetch(_flw_moments_sampler, idx0, 0).xy; + trig_b[0] = texelFetch(_flw_moments0_sampler, idx0, 0).xy; #if USE_R_RG_RBBA_FOR_MBOIT6 vec4 tmp = texelFetch(extra_moments, idx0, 0); trig_b[1] = tmp.xy; trig_b[2] = tmp.zw; #else - trig_b[1] = texelFetch(_flw_moments_sampler, idx1, 0).xy; - trig_b[2] = texelFetch(_flw_moments_sampler, idx2, 0).xy; + trig_b[1] = texelFetch(_flw_moments1_sampler, idx1, 0).xy; + trig_b[2] = texelFetch(_flw_moments0_sampler, idx2, 0).xy; #endif #if SINGLE_PRECISION trig_b[0] /= b_0; @@ -178,14 +176,14 @@ void resolveMoments(out float transmittance_at_depth, out float total_transmitta #endif transmittance_at_depth = computeTransmittanceAtDepthFrom3TrigonometricMoments(b_0, trig_b, depth, moment_bias, overestimation, wrapping_zone_parameters); #else - vec2 b_12 = texelFetch(_flw_moments_sampler, idx0, 0).xy; + vec2 b_12 = texelFetch(_flw_moments0_sampler, idx0, 0).xy; #if USE_R_RG_RBBA_FOR_MBOIT6 vec4 tmp = texelFetch(extra_moments, idx0, 0); vec2 b_34 = tmp.xy; vec2 b_56 = tmp.zw; #else - vec2 b_34 = texelFetch(_flw_moments_sampler, idx1, 0).xy; - vec2 b_56 = texelFetch(_flw_moments_sampler, idx2, 0).xy; + vec2 b_34 = texelFetch(_flw_moments1_sampler, idx1, 0).xy; + vec2 b_56 = texelFetch(_flw_moments0_sampler, idx2, 0).xy; #endif #if SINGLE_PRECISION vec3 b_even = vec3(b_12.y, b_34.y, b_56.y); @@ -209,8 +207,8 @@ void resolveMoments(out float transmittance_at_depth, out float total_transmitta #endif #elif NUM_MOMENTS == 8 #if TRIGONOMETRIC - vec4 b_tmp = texelFetch(_flw_moments_sampler, idx0, 0); - vec4 b_tmp2 = texelFetch(_flw_moments_sampler, idx1, 0); + vec4 b_tmp = texelFetch(_flw_moments0_sampler, idx0, 0); + vec4 b_tmp2 = texelFetch(_flw_moments1_sampler, idx1, 0); #if SINGLE_PRECISION vec2 trig_b[4] = { b_tmp2.xy / b_0, @@ -229,15 +227,15 @@ void resolveMoments(out float transmittance_at_depth, out float total_transmitta transmittance_at_depth = computeTransmittanceAtDepthFrom4TrigonometricMoments(b_0, trig_b, depth, moment_bias, overestimation, wrapping_zone_parameters); #else #if SINGLE_PRECISION - vec4 b_even = texelFetch(_flw_moments_sampler, idx0, 0); - vec4 b_odd = texelFetch(_flw_moments_sampler, idx1, 0); + vec4 b_even = texelFetch(_flw_moments0_sampler, idx0, 0); + vec4 b_odd = texelFetch(_flw_moments1_sampler, idx1, 0); b_even /= b_0; b_odd /= b_0; const float bias_vector[8] = { 0, 0.75, 0, 0.67666666666666664, 0, 0.63, 0, 0.60030303030303034 }; #else - vec4 b_even_q = texelFetch(_flw_moments_sampler, idx0, 0); - vec4 b_odd_q = texelFetch(_flw_moments_sampler, idx1, 0); + vec4 b_even_q = texelFetch(_flw_moments0_sampler, idx0, 0); + vec4 b_odd_q = texelFetch(_flw_moments1_sampler, idx1, 0); // Dequantize the moments vec4 b_even; diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl index 4fc142d8f..30fe6a65e 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl @@ -21,7 +21,7 @@ float circleToParameter(vec2 circle_point){ returns 1.0, otherwise 0.0 or a linear ramp in the wrapping zone.*/ float getRootWeightFactor(float reference_parameter, float root_parameter, vec4 wrapping_zone_parameters){ float binary_weight_factor=(root_parameter Date: Mon, 17 Feb 2025 23:16:39 -0800 Subject: [PATCH 08/23] Wave goodbye to order - Implement wavelet OIT - Needed to do the same normalization step as in MBOIT but that's not described in the blog post I followed - Use an expensive pseudo blue noise function to slightly correct banding artifacts --- .../flywheel/backend/Samplers.java | 5 +- .../backend/compile/PipelineCompiler.java | 5 +- .../engine/indirect/IndirectDrawManager.java | 22 +- ...itFramebuffer.java => OitFramebuffer.java} | 133 +++-- .../flywheel/flywheel/internal/common.frag | 268 ++++++++-- .../internal/indirect/oit_composite.frag | 79 ++- .../internal/mboit/complex_algebra.glsl | 213 -------- .../flywheel/internal/mboit/moment_math.glsl | 490 ------------------ .../flywheel/internal/mboit/moment_oit.glsl | 251 --------- .../mboit/trigonometric_moment_math.glsl | 311 ----------- 10 files changed, 386 insertions(+), 1391 deletions(-) rename common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/{MboitFramebuffer.java => OitFramebuffer.java} (56%) delete mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl delete mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl delete mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl delete mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java b/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java index e89acccbb..272314894 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java @@ -11,7 +11,6 @@ public class Samplers { public static final GlTextureUnit LIGHT_LUT = GlTextureUnit.T5; public static final GlTextureUnit LIGHT_SECTIONS = GlTextureUnit.T6; - public static final GlTextureUnit ZEROTH_MOMENT = GlTextureUnit.T7; - public static final GlTextureUnit MOMENTS0 = GlTextureUnit.T8; - public static final GlTextureUnit MOMENTS1 = GlTextureUnit.T9; + public static final GlTextureUnit DEPTH_RANGE = GlTextureUnit.T7; + public static final GlTextureUnit COEFFICIENTS = GlTextureUnit.T8; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java index f33486315..388563f49 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java @@ -230,8 +230,9 @@ public final class PipelineCompiler { public enum OitMode { OFF("", ""), - GENERATE("_FLW_GENERATE_MOMENTS", "_generate"), - RESOLVE("_FLW_RESOLVE_MOMENTS", "_resolve"), + DEPTH_RANGE("_FLW_DEPTH_RANGE", "_depth_range"), + GENERATE_COEFFICIENTS("_FLW_COLLECT_COEFFS", "_generate_coefficients"), + EVALUATE("_FLW_EVALUATE", "_resolve"), ; public final String define; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index afc62d5af..09b5b3201 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -49,7 +49,7 @@ public class IndirectDrawManager extends DrawManager> { private final DepthPyramid depthPyramid; - private final MboitFramebuffer wboitFrameBuffer; + private final OitFramebuffer wboitFrameBuffer; public IndirectDrawManager(IndirectPrograms programs) { this.programs = programs; @@ -66,7 +66,7 @@ public class IndirectDrawManager extends DrawManager> { depthPyramid = new DepthPyramid(programs); - wboitFrameBuffer = new MboitFramebuffer(programs); + wboitFrameBuffer = new OitFramebuffer(programs); } @Override @@ -146,16 +146,26 @@ public class IndirectDrawManager extends DrawManager> { group.submitSolid(); } - wboitFrameBuffer.generateMoments(); + wboitFrameBuffer.depthRange(); for (var group : cullingGroups.values()) { - group.submitTransparent(PipelineCompiler.OitMode.GENERATE); + group.submitTransparent(PipelineCompiler.OitMode.DEPTH_RANGE); } - wboitFrameBuffer.resolveMoments(); + wboitFrameBuffer.renderTransmittance(); for (var group : cullingGroups.values()) { - group.submitTransparent(PipelineCompiler.OitMode.RESOLVE); + group.submitTransparent(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS); + } + + // wboitFrameBuffer.adjustBackgroundForTotalTransmittance(); + + // vertexArray.bindForDraw(); + + wboitFrameBuffer.shade(); + + for (var group : cullingGroups.values()) { + group.submitTransparent(PipelineCompiler.OitMode.EVALUATE); } wboitFrameBuffer.composite(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MboitFramebuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java similarity index 56% rename from common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MboitFramebuffer.java rename to common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java index 747020e73..3d50fb76e 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MboitFramebuffer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java @@ -10,29 +10,27 @@ import dev.engine_room.flywheel.backend.Samplers; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; import dev.engine_room.flywheel.backend.gl.GlTextureUnit; import net.minecraft.client.Minecraft; -import net.minecraft.util.Mth; -public class MboitFramebuffer { +public class OitFramebuffer { public final int fbo; private final IndirectPrograms programs; private final int vao; - public int zerothMoment; - public int moments0; - public int moments1; + public int depthBounds; + public int coefficients; public int accumulate; private int lastWidth = -1; private int lastHeight = -1; - public MboitFramebuffer(IndirectPrograms programs) { + public OitFramebuffer(IndirectPrograms programs) { this.programs = programs; fbo = GL46.glCreateFramebuffers(); vao = GL46.glCreateVertexArrays(); } - public void generateMoments() { + public void depthRange() { var mainRenderTarget = Minecraft.getInstance() .getMainRenderTarget(); @@ -42,36 +40,55 @@ public class MboitFramebuffer { RenderSystem.depthMask(false); RenderSystem.enableBlend(); RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); - RenderSystem.blendEquation(GL46.GL_FUNC_ADD); + RenderSystem.blendEquation(GL46.GL_MAX); GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, mainRenderTarget.getDepthTextureId(), 0); - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT0, GL46.GL_COLOR_ATTACHMENT1, GL46.GL_COLOR_ATTACHMENT2}); + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT0}); - GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); - GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 1, new float[]{0, 0, 0, 0}); - GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 2, new float[]{0, 0, 0, 0}); + var far = Minecraft.getInstance().gameRenderer.getDepthFar(); + + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{-far, -far, 0, 0}); GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); } - public void resolveMoments() { + public void renderTransmittance() { // No depth writes, but we'll still use the depth test RenderSystem.depthMask(false); RenderSystem.enableBlend(); RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); RenderSystem.blendEquation(GL46.GL_FUNC_ADD); - Samplers.ZEROTH_MOMENT.makeActive(); - GlStateManager._bindTexture(zerothMoment); + Samplers.DEPTH_RANGE.makeActive(); + GlStateManager._bindTexture(depthBounds); - Samplers.MOMENTS0.makeActive(); - GlStateManager._bindTexture(moments0); + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT1, GL46.GL_COLOR_ATTACHMENT2, GL46.GL_COLOR_ATTACHMENT3, GL46.GL_COLOR_ATTACHMENT4}); - Samplers.MOMENTS1.makeActive(); - GlStateManager._bindTexture(moments1); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 1, new float[]{0, 0, 0, 0}); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 2, new float[]{0, 0, 0, 0}); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 3, new float[]{0, 0, 0, 0}); - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT3}); + GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); + } + + public void shade() { + // No depth writes, but we'll still use the depth test + RenderSystem.depthMask(false); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); + RenderSystem.blendEquation(GL46.GL_FUNC_ADD); + + Samplers.DEPTH_RANGE.makeActive(); + GlStateManager._bindTexture(depthBounds); + + Samplers.COEFFICIENTS.makeActive(); + GlStateManager._bindTexture(0); + + GL46.glBindTextureUnit(Samplers.COEFFICIENTS.number, coefficients); + + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT5}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); @@ -79,26 +96,27 @@ public class MboitFramebuffer { } public void composite() { + // No depth writes, but we'll still use the depth test + RenderSystem.depthMask(false); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.DestFactor.SRC_ALPHA); + RenderSystem.blendEquation(GL46.GL_FUNC_ADD); + var mainRenderTarget = Minecraft.getInstance() .getMainRenderTarget(); mainRenderTarget.bindWrite(false); - var oitCompositeProgram = programs.getOitCompositeProgram(); - - GlStateManager._depthMask(false); - GlStateManager._depthFunc(GL46.GL_ALWAYS); - GlStateManager._enableBlend(); - RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.DestFactor.SRC_ALPHA); - - oitCompositeProgram.bind(); - GlTextureUnit.T0.makeActive(); - GlStateManager._bindTexture(zerothMoment); + GlStateManager._bindTexture(0); + GL46.glBindTextureUnit(0, coefficients); GlTextureUnit.T1.makeActive(); GlStateManager._bindTexture(accumulate); + programs.getOitCompositeProgram() + .bind(); + // Empty VAO, the actual full screen triangle is generated in the vertex shader GlStateManager._glBindVertexArray(vao); @@ -112,9 +130,8 @@ public class MboitFramebuffer { } private void deleteTextures() { - GL46.glDeleteTextures(zerothMoment); - GL46.glDeleteTextures(moments0); - GL46.glDeleteTextures(moments1); + GL46.glDeleteTextures(depthBounds); + GL46.glDeleteTextures(coefficients); GL46.glDeleteTextures(accumulate); } @@ -128,14 +145,12 @@ public class MboitFramebuffer { deleteTextures(); - zerothMoment = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); - moments0 = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); - moments1 = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); + depthBounds = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); + coefficients = GL46.glCreateTextures(GL46.GL_TEXTURE_2D_ARRAY); accumulate = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); - GL46.glTextureStorage2D(zerothMoment, 1, GL32.GL_R16F, width, height); - GL46.glTextureStorage2D(moments0, 1, GL32.GL_RGBA16F, width, height); - GL46.glTextureStorage2D(moments1, 1, GL32.GL_RGBA16F, width, height); + GL46.glTextureStorage2D(depthBounds, 1, GL32.GL_RG32F, width, height); + GL46.glTextureStorage3D(coefficients, 1, GL32.GL_RGBA16F, width, height, 4); GL46.glTextureStorage2D(accumulate, 1, GL32.GL_RGBA16F, width, height); @@ -147,39 +162,11 @@ public class MboitFramebuffer { // GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE); // } - GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT0, zerothMoment, 0); - GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT1, moments0, 0); - GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT2, moments1, 0); - GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT3, accumulate, 0); - } - - float circleToParameter(float angle) { - float x = Mth.cos(angle); - float y = Mth.sin(angle); - float result = Mth.abs(y) - Mth.abs(x); - result = (x < 0.0f) ? (2.0f - result) : result; - result = (y < 0.0f) ? (6.0f - result) : result; - result += (angle >= 2.0f * Mth.PI) ? 8.0f : 0.0f; - return result; - } - - void computeWrappingZoneParameters(float[] out) { - computeWrappingZoneParameters(out, 0.1f * Mth.PI); - } - - /*! Given an angle in radians providing the size of the wrapping zone, this - function computes all constants required by the shader.*/ - void computeWrappingZoneParameters(float[] p_out_wrapping_zone_parameters, float new_wrapping_zone_angle) { - p_out_wrapping_zone_parameters[0] = new_wrapping_zone_angle; - p_out_wrapping_zone_parameters[1] = Mth.PI - 0.5f * new_wrapping_zone_angle; - if (new_wrapping_zone_angle <= 0.0f) { - p_out_wrapping_zone_parameters[2] = 0.0f; - p_out_wrapping_zone_parameters[3] = 0.0f; - } else { - float zone_end_parameter = 7; - float zone_begin_parameter = circleToParameter(2.0f * Mth.PI - new_wrapping_zone_angle); - p_out_wrapping_zone_parameters[2] = 1.0f / (zone_end_parameter - zone_begin_parameter); - p_out_wrapping_zone_parameters[3] = 1.0f - zone_end_parameter * p_out_wrapping_zone_parameters[2]; - } + GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT0, depthBounds, 0); + GL46.glNamedFramebufferTextureLayer(fbo, GL46.GL_COLOR_ATTACHMENT1, coefficients, 0, 0); + GL46.glNamedFramebufferTextureLayer(fbo, GL46.GL_COLOR_ATTACHMENT2, coefficients, 0, 1); + GL46.glNamedFramebufferTextureLayer(fbo, GL46.GL_COLOR_ATTACHMENT3, coefficients, 0, 2); + GL46.glNamedFramebufferTextureLayer(fbo, GL46.GL_COLOR_ATTACHMENT4, coefficients, 0, 3); + GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT5, accumulate, 0); } } diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag index 3cb61f8a5..d4715ff3a 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag @@ -1,7 +1,6 @@ #include "flywheel:internal/packed_material.glsl" #include "flywheel:internal/diffuse.glsl" #include "flywheel:internal/colorizer.glsl" -#include "flywheel:internal/mboit/moment_oit.glsl" // optimize discard usage #if defined(GL_ARB_conservative_depth) && defined(_FLW_USE_DISCARD) @@ -19,14 +18,222 @@ flat in uvec2 _flw_ids; #endif #ifdef _FLW_OIT -#ifdef _FLW_GENERATE_MOMENTS -layout (location = 0) out float _flw_zerothMoment_out; -layout (location = 1) out vec4 _flw_moments0_out; -layout (location = 2) out vec4 _flw_moments1_out; + +#define TRANSPARENCY_WAVELET_RANK 3 +#define TRANSPARENCY_WAVELET_COEFFICIENT_COUNT 16 +#define floatN float +#define all(e) (e) +#define mad fma +#define lerp mix +#define Coefficients_Out vec4[4] +#define Coefficients_In sampler2DArray + +layout (binding = 7) uniform sampler2D _flw_depthRange; + +layout (binding = 8) uniform sampler2DArray _flw_coefficients; + +#define REMOVE_SIGNAL true + +#ifdef _FLW_DEPTH_RANGE + +layout (location = 0) out vec2 _flw_depthRange_out; + #endif -#ifdef _FLW_RESOLVE_MOMENTS -layout (location = 0) out vec4 _flw_accumulate_out; + + +#ifdef _FLW_COLLECT_COEFFS + + +layout (location = 0) out vec4 _flw_coeffs0; +layout (location = 1) out vec4 _flw_coeffs1; +layout (location = 2) out vec4 _flw_coeffs2; +layout (location = 3) out vec4 _flw_coeffs3; + +void add_to_index(inout Coefficients_Out coefficients, uint index, floatN addend) { + coefficients[index >> 2][index & 3u] = addend; +} + +void add_event_to_wavelets(inout Coefficients_Out coefficients, floatN signal, float depth) +{ + depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; + + int index = clamp(int(floor(depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + index += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + + for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) + { + int power = TRANSPARENCY_WAVELET_RANK - i; + int new_index = (index - 1) >> 1; + float k = float((new_index + 1) & ((1 << power) - 1)); + + int wavelet_sign = ((index & 1) << 1) - 1; + float wavelet_phase = ((index + 1) & 1) * exp2(-power); + floatN addend = mad(mad(-exp2(-power), k, depth), wavelet_sign, wavelet_phase) * exp2(power * 0.5) * signal; + add_to_index(coefficients, new_index, addend); + + index = new_index; + } + + floatN addend = mad(signal, -depth, signal); + add_to_index(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1, addend); +} + +void add_transmittance_event_to_wavelets(inout Coefficients_Out coefficients, floatN transmittance, float depth) +{ + float absorbance = -log(max(transmittance, 0.00001));// transforming the signal from multiplicative transmittance to additive absorbance + add_event_to_wavelets(coefficients, absorbance, depth); +} + #endif + +#ifdef _FLW_EVALUATE + +layout (location = 0) out vec4 _flw_accumulate; + + +floatN get_coefficients(in Coefficients_In coefficients, uint index) { + return texelFetch(coefficients, ivec3(gl_FragCoord.xy, index >> 2), 0)[index & 3u]; +} + + floatN evaluate_wavelets(in Coefficients_In coefficients, float depth, floatN signal) +{ + floatN scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + if (all(scale_coefficient == 0)) + { + return 0; + } + if (REMOVE_SIGNAL) + { + floatN scale_coefficient_addend = mad(signal, -depth, signal); + scale_coefficient -= scale_coefficient_addend; + } + + depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; + + float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; + int index_b = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + bool sample_a = index_b >= 1; + int index_a = sample_a ? (index_b - 1) : index_b; + + index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + index_a += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + + floatN b = scale_coefficient; +floatN a = sample_a ? scale_coefficient : 0; + + for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) + { + int power = TRANSPARENCY_WAVELET_RANK - i; + + int new_index_b = (index_b - 1) >> 1; + int wavelet_sign_b = ((index_b & 1) << 1) - 1; + floatN coeff_b = get_coefficients(coefficients, new_index_b); + if (REMOVE_SIGNAL) + { + float wavelet_phase_b = ((index_b + 1) & 1) * exp2(-power); + float k = float((new_index_b + 1) & ((1 << power) - 1)); + floatN addend = mad(mad(-exp2(-power), k, depth), wavelet_sign_b, wavelet_phase_b) * exp2(power * 0.5) * signal; + coeff_b -= addend; + } + b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b; + index_b = new_index_b; + + if (sample_a) + { + int new_index_a = (index_a - 1) >> 1; + int wavelet_sign_a = ((index_a & 1) << 1) - 1; + floatN coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a);// No addend here on purpose, the original signal didn't contribute to this coefficient + a -= exp2(float(power) * 0.5) * coeff_a * wavelet_sign_a; + index_a = new_index_a; + } + } + + float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth); + + return lerp(a, b, t); +} + + floatN evaluate_transmittance_wavelets(in Coefficients_In coefficients, float depth, floatN signal) +{ + floatN absorbance = evaluate_wavelets(coefficients, depth, signal); + return clamp(exp(-absorbance), 0., 1.);// undoing the transformation from absorbance back to transmittance +} + +#endif + +// TODO: blue noise texture +uint HilbertIndex(uvec2 p) { + uint i = 0u; + for (uint l = 0x4000u; l > 0u; l >>= 1u) { + uvec2 r = min(p & l, 1u); + + i = (i << 2u) | ((r.x * 3u) ^ r.y); + p = r.y == 0u ? (0x7FFFu * r.x) ^ p.yx : p; + } + return i; +} + +uint ReverseBits(uint x) { + x = ((x & 0xaaaaaaaau) >> 1) | ((x & 0x55555555u) << 1); + x = ((x & 0xccccccccu) >> 2) | ((x & 0x33333333u) << 2); + x = ((x & 0xf0f0f0f0u) >> 4) | ((x & 0x0f0f0f0fu) << 4); + x = ((x & 0xff00ff00u) >> 8) | ((x & 0x00ff00ffu) << 8); + return (x >> 16) | (x << 16); +} + +// from: https://psychopath.io/post/2021_01_30_building_a_better_lk_hash +uint OwenHash(uint x, uint seed) { // seed is any random number + x ^= x * 0x3d20adeau; + x += seed; + x *= (seed >> 16) | 1u; + x ^= x * 0x05526c56u; + x ^= x * 0x53a22864u; + return x; +} + +// https://www.shadertoy.com/view/ssBBW1 +float blue() { + uint m = HilbertIndex(uvec2(gl_FragCoord.xy));// map pixel coords to hilbert curve index + m = OwenHash(ReverseBits(m), 0xe7843fbfu);// owen-scramble hilbert index + m = OwenHash(ReverseBits(m), 0x8d8fb1e0u);// map hilbert index to sobol sequence and owen-scramble + float mask = float(ReverseBits(m)) / 4294967296.0;// convert to float + + return mask; +} + +uniform vec3 _flw_depthAdjust; + +float adjust_depth(float normalizedDepth) { + + float tentIn = abs(normalizedDepth * 2. - 1); + float tentIn2 = tentIn * tentIn; + float tentIn4 = tentIn2 * tentIn2; + float tent = 1 - (tentIn2 * tentIn4); + + float b = blue(); + + return normalizedDepth - b * tent * 0.08; +} + +float linearize_depth(float d, float zNear, float zFar) { + float z_n = 2.0 * d - 1.0; + return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); +} + +float linear_depth() { + return linearize_depth(gl_FragCoord.z, _flw_cullData.znear, _flw_cullData.zfar); +} + +float depth() { + float linearDepth = linear_depth(); + + vec2 depthRange = texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).rg; + float depth = (linearDepth + depthRange.x) / (depthRange.x + depthRange.y); + + return adjust_depth(depth); +} + + #else out vec4 _flw_outputColor; @@ -47,11 +254,6 @@ float _flw_diffuseFactor() { } } -float linearize_depth(float d, float zNear, float zFar) { - float z_n = 2.0 * d - 1.0; - return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); -} - void _flw_main() { flw_sampleColor = texture(flw_diffuseTex, flw_vertexTexCoord); flw_fragColor = flw_vertexColor * flw_sampleColor; @@ -121,39 +323,41 @@ void _flw_main() { color = flw_fogFilter(color); #ifdef _FLW_OIT - float linearDepth = linearize_depth(gl_FragCoord.z, _flw_cullData.znear, _flw_cullData.zfar); - float lnNear = log(_flw_cullData.znear); - float lnFar = log(_flw_cullData.zfar); + #ifdef _FLW_DEPTH_RANGE + float linearDepth = linear_depth(); - float depth = (log(linearDepth) - lnNear); + // Pad the depth by some unbalanced epsilons because minecraft has a lot of single-quad tranparency. + // The unbalance means our fragment will be considered closer to the screen in the normalization, + // which helps prevent unnecessary noise as it'll be closer to the edge of our tent function. + _flw_depthRange_out = vec2(-linearDepth + 1e-5, linearDepth + 1e-2); + #endif - depth /= lnFar - lnNear; + #ifdef _FLW_COLLECT_COEFFS - depth = clamp(depth * 2. - 1., -1., 1.); + Coefficients_Out result; + result[0] = vec4(0.); + result[1] = vec4(0.); + result[2] = vec4(0.); + result[3] = vec4(0.); - #ifdef _FLW_GENERATE_MOMENTS + add_transmittance_event_to_wavelets(result, 1. - color.a, depth()); - generateMoments(depth, 1 - color.a, _flw_zerothMoment_out, _flw_moments0_out, _flw_moments1_out); + _flw_coeffs0 = result[0]; + _flw_coeffs1 = result[1]; + _flw_coeffs2 = result[2]; + _flw_coeffs3 = result[3]; #endif - #ifdef _FLW_RESOLVE_MOMENTS - float tt; - float td; - resolveMoments(td, tt, depth, gl_FragCoord.xy); + #ifdef _FLW_EVALUATE - if (abs(td) < 1e-5) { - discard; - } + floatN transmittance = evaluate_transmittance_wavelets(_flw_coefficients, depth(), 1. - color.a); - color.rgb *= color.a; - - color *= td; - - _flw_accumulate_out = color; + _flw_accumulate = vec4(color.rgb * color.a, color.a) * transmittance; #endif + #else _flw_outputColor = color; diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag index 3afe92839..1fee5f927 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag @@ -1,20 +1,79 @@ layout (location = 0) out vec4 frag; -layout (binding = 0) uniform sampler2D zerothMoment; -layout (binding = 1) uniform sampler2D accumulate; +layout (binding = 0) uniform sampler2DArray _flw_coefficients; +layout (binding = 1) uniform sampler2D _flw_accumulate; + +#define TRANSPARENCY_WAVELET_RANK 3 +#define TRANSPARENCY_WAVELET_COEFFICIENT_COUNT 16 +#define floatN float +#define all(e) (e) +#define mad fma +#define lerp mix +#define Coefficients_Out vec4[4] +#define Coefficients_In sampler2DArray + + +floatN get_coefficients(in Coefficients_In coefficients, uint index) { + return texelFetch(coefficients, ivec3(gl_FragCoord.xy, index >> 2), 0)[index & 3u]; +} + + floatN evaluate_wavelet_index(in Coefficients_In coefficients, int index) +{ + floatN result = 0; + + index += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + + for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) + { + int power = TRANSPARENCY_WAVELET_RANK - i; + int new_index = (index - 1) >> 1; + floatN coeff = get_coefficients(coefficients, new_index); + int wavelet_sign = ((index & 1) << 1) - 1; + result -= exp2(float(power) * 0.5) * coeff * wavelet_sign; + index = new_index; + } + return result; +} + + + floatN evaluate_wavelets(in Coefficients_In coefficients, float depth) +{ + floatN scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + if (all(scale_coefficient == 0)) + { + return 0; + } + + depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; + + float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; + int index = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + + floatN a = 0; +floatN b = scale_coefficient + evaluate_wavelet_index(coefficients, index); + if (index > 0) { a = scale_coefficient + evaluate_wavelet_index(coefficients, index - 1); } + + float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth); + floatN signal = lerp(a, b, t);// You can experiment here with different types of interpolation as well + return signal; +} + + floatN evaluate_transmittance_wavelets(in Coefficients_In coefficients, float depth) +{ + floatN absorbance = evaluate_wavelets(coefficients, depth); + return clamp(exp(-absorbance), 0., 1.);// undoing the transformation from absorbance back to transmittance +} + +const float infinity = 1. / 0.; void main() { - ivec2 coords = ivec2(gl_FragCoord.xy); + vec4 texel = texelFetch(_flw_accumulate, ivec2(gl_FragCoord.xy), 0); - float b0 = texelFetch(zerothMoment, coords, 0).r; - - if (b0 < 1e-5) { + if (texel.a < 1e-5) { discard; } - vec4 accumulation = texelFetch(accumulate, coords, 0); + floatN total_transmittance = evaluate_transmittance_wavelets(_flw_coefficients, infinity); - vec3 normalizedAccumulation = accumulation.rgb / max(accumulation.a, 1e-5); - - frag = vec4(normalizedAccumulation, exp(-b0)); + frag = vec4(texel.rgb / texel.a, total_transmittance); } diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl deleted file mode 100644 index 30848e3f7..000000000 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/complex_algebra.glsl +++ /dev/null @@ -1,213 +0,0 @@ -/*! \file - This header defines utility functions to deal with complex numbers and - complex polynomials.*/ - -void sincos(float theta, out float s, out float c) { - s = sin(theta); - c = cos(theta); -} - -float saturate(float a) { - return clamp(a, 0., 1.); -} - - -/*! Returns the complex conjugate of the given complex number (i.e. it changes - the sign of the y-component).*/ -vec2 Conjugate(vec2 Z){ - return vec2(Z.x, -Z.y); -} -/*! This function implements complex multiplication.*/ -vec2 Multiply(vec2 LHS, vec2 RHS){ - return vec2(LHS.x*RHS.x-LHS.y*RHS.y, LHS.x*RHS.y+LHS.y*RHS.x); -} -/*! This function computes the magnitude of the given complex number.*/ -float Magnitude(vec2 Z){ - return sqrt(dot(Z, Z)); -} -/*! This function computes the quotient of two complex numbers. The denominator - must not be zero.*/ -vec2 Divide(vec2 Numerator, vec2 Denominator){ - return vec2(Numerator.x*Denominator.x+Numerator.y*Denominator.y, -Numerator.x*Denominator.y+Numerator.y*Denominator.x)/dot(Denominator, Denominator); -} -/*! This function divides a real number by a complex number. The denominator - must not be zero.*/ -vec2 Divide(float Numerator, vec2 Denominator){ - return vec2(Numerator*Denominator.x, -Numerator*Denominator.y)/dot(Denominator, Denominator); -} -/*! This function implements computation of the reciprocal of the given non- - zero complex number.*/ -vec2 Reciprocal(vec2 Z){ - return vec2(Z.x, -Z.y)/dot(Z, Z); -} -/*! This utility function implements complex squaring.*/ -vec2 Square(vec2 Z){ - return vec2(Z.x*Z.x-Z.y*Z.y, 2.0f*Z.x*Z.y); -} -/*! This utility function implements complex computation of the third power.*/ -vec2 Cube(vec2 Z){ - return Multiply(Square(Z), Z); -} -/*! This utility function computes one square root of the given complex value. - The other one can be found using the unary minus operator. - \warning This function is continuous but not defined on the negative real - axis (and cannot be continued continuously there). - \sa SquareRoot() */ -vec2 SquareRootUnsafe(vec2 Z){ - float ZLengthSq=dot(Z, Z); - float ZLengthInv=inversesqrt(ZLengthSq); - vec2 UnnormalizedRoot=Z*ZLengthInv+vec2(1.0f, 0.0f); - float UnnormalizedRootLengthSq=dot(UnnormalizedRoot, UnnormalizedRoot); - float NormalizationFactorInvSq=UnnormalizedRootLengthSq*ZLengthInv; - float NormalizationFactor=inversesqrt(NormalizationFactorInvSq); - return NormalizationFactor*UnnormalizedRoot; -} -/*! This utility function computes one square root of the given complex value. - The other one can be found using the unary minus operator. - \note This function has discontinuities for values with real part zero. - \sa SquareRootUnsafe() */ -vec2 SquareRoot(vec2 Z){ - vec2 ZPositiveRealPart=vec2(abs(Z.x), Z.y); - vec2 ComputedRoot=SquareRootUnsafe(ZPositiveRealPart); - return (Z.x>=0.0)?ComputedRoot:ComputedRoot.yx; -} -/*! This utility function computes one cubic root of the given complex value. The - other roots can be found by multiplication by cubic roots of unity. - \note This function has various discontinuities.*/ -vec2 CubicRoot(vec2 Z){ - float Argument=atan(Z.y, Z.x); - float NewArgument=Argument/3.0f; - vec2 NormalizedRoot; - sincos(NewArgument, NormalizedRoot.y, NormalizedRoot.x); - return NormalizedRoot*pow(dot(Z, Z), 1.0f/6.0f); -} - -/*! @{ - Returns the complex conjugate of the given complex vector (i.e. it changes the - second column resp the y-component).*/ -mat2x2 Conjugate(mat2x2 Vector){ - return mat2x2(Vector[0].x, -Vector[0].y, Vector[1].x, -Vector[1].y); -} -mat3x2 Conjugate(mat3x2 Vector){ - return mat3x2(Vector[0].x, -Vector[0].y, Vector[1].x, -Vector[1].y, Vector[2].x, -Vector[2].y); -} -mat4x2 Conjugate(mat4x2 Vector){ - return mat4x2(Vector[0].x, -Vector[0].y, Vector[1].x, -Vector[1].y, Vector[2].x, -Vector[2].y, Vector[3].x, -Vector[3].y); -} -void Conjugate(out vec2 OutConjugateVector[5], vec2 Vector[5]){ - for (int i=0;i!=5;++i){ - OutConjugateVector[i]=vec2(Vector[i].x, -Vector[i].x); - } -} -//!@} - -/*! Returns the real part of a complex number as real.*/ -float RealPart(vec2 Z){ - return Z.x; -} - -/*! Given coefficients of a quadratic polynomial A*x^2+B*x+C, this function - outputs its two complex roots.*/ -void SolveQuadratic(out vec2 pOutRoot[2], vec2 A, vec2 B, vec2 C) -{ - // Normalize the coefficients - vec2 InvA=Reciprocal(A); - B=Multiply(B, InvA); - C=Multiply(C, InvA); - // Divide the middle coefficient by two - B*=0.5f; - // Apply the quadratic formula - vec2 DiscriminantRoot=SquareRoot(Square(B)-C); - pOutRoot[0]=-B-DiscriminantRoot; - pOutRoot[1]=-B+DiscriminantRoot; -} - -/*! Given coefficients of a cubic polynomial A*x^3+B*x^2+C*x+D, this function - outputs its three complex roots.*/ -void SolveCubicBlinn(out vec2 pOutRoot[3], vec2 A, vec2 B, vec2 C, vec2 D) -{ - // Normalize the polynomial - vec2 InvA=Reciprocal(A); - B=Multiply(B, InvA); - C=Multiply(C, InvA); - D=Multiply(D, InvA); - // Divide middle coefficients by three - B/=3.0f; - C/=3.0f; - // Compute the Hessian and the discriminant - vec2 Delta00=-Square(B)+C; - vec2 Delta01=-Multiply(C, B)+D; - vec2 Delta11=Multiply(B, D)-Square(C); - vec2 Discriminant=4.0f*Multiply(Delta00, Delta11)-Square(Delta01); - // Compute coefficients of the depressed cubic - // (third is zero, fourth is one) - vec2 DepressedD=-2.0f*Multiply(B, Delta00)+Delta01; - vec2 DepressedC=Delta00; - // Take the cubic root of a complex number avoiding cancellation - vec2 DiscriminantRoot=SquareRoot(-Discriminant); - DiscriminantRoot=faceforward(DiscriminantRoot, DiscriminantRoot, DepressedD); - vec2 CubedRoot=DiscriminantRoot-DepressedD; - vec2 FirstRoot=CubicRoot(0.5f*CubedRoot); - vec2 pCubicRoot[3]={ - FirstRoot, - Multiply(vec2(-0.5f, -0.5f*sqrt(3.0f)), FirstRoot), - Multiply(vec2(-0.5f, 0.5f*sqrt(3.0f)), FirstRoot) - }; - // Also compute the reciprocal cubic roots - vec2 InvFirstRoot=Reciprocal(FirstRoot); - vec2 pInvCubicRoot[3]={ - InvFirstRoot, - Multiply(vec2(-0.5f, 0.5f*sqrt(3.0f)), InvFirstRoot), - Multiply(vec2(-0.5f, -0.5f*sqrt(3.0f)), InvFirstRoot) - }; - // Turn them into roots of the depressed cubic and revert the depression - // transform - - for (int i=0;i!=3;++i) - { - pOutRoot[i]=pCubicRoot[i]-Multiply(DepressedC, pInvCubicRoot[i])-B; - } -} - - -/*! Given coefficients of a quartic polynomial A*x^4+B*x^3+C*x^2+D*x+E, this - function outputs its four complex roots.*/ -void SolveQuarticNeumark(out vec2 pOutRoot[4], vec2 A, vec2 B, vec2 C, vec2 D, vec2 E) -{ - // Normalize the polynomial - vec2 InvA=Reciprocal(A); - B=Multiply(B, InvA); - C=Multiply(C, InvA); - D=Multiply(D, InvA); - E=Multiply(E, InvA); - // Construct a normalized cubic - vec2 P=-2.0f*C; - vec2 Q=Square(C)+Multiply(B, D)-4.0f*E; - vec2 R=Square(D)+Multiply(Square(B), E)-Multiply(Multiply(B, C), D); - // Compute a root that is not the smallest of the cubic - vec2 pCubicRoot[3]; - SolveCubicBlinn(pCubicRoot, vec2(1.0f, 0.0f), P, Q, R); - vec2 y=(dot(pCubicRoot[1], pCubicRoot[1])>dot(pCubicRoot[0], pCubicRoot[0]))?pCubicRoot[1]:pCubicRoot[0]; - - // Solve a quadratic to obtain linear coefficients for quadratic polynomials - vec2 BB=Square(B); - vec2 fy=4.0f*y; - vec2 BB_fy=BB-fy; - vec2 tmp=SquareRoot(BB_fy); - vec2 G=(B+tmp)*0.5f; - vec2 g=(B-tmp)*0.5f; - // Construct the corresponding constant coefficients - vec2 Z=C-y; - tmp=Divide(0.5f*Multiply(B, Z)-D, tmp); - vec2 H=Z*0.5f+tmp; - vec2 h=Z*0.5f-tmp; - - // Compute the roots - vec2 pQuadraticRoot[2]; - SolveQuadratic(pQuadraticRoot, vec2(1.0f, 0.0f), G, H); - pOutRoot[0]=pQuadraticRoot[0]; - pOutRoot[1]=pQuadraticRoot[1]; - SolveQuadratic(pQuadraticRoot, vec2(1.0f, 0.0f), g, h); - pOutRoot[2]=pQuadraticRoot[0]; - pOutRoot[3]=pQuadraticRoot[1]; -} diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl deleted file mode 100644 index 4f730eada..000000000 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_math.glsl +++ /dev/null @@ -1,490 +0,0 @@ -/*! \file - This header provides utility functions to reconstruct the transmittance - from a given vector of power moments (4, 6 or 8 power moments) at a - specified depth. As prerequisite, utility functions for computing the real - roots of polynomials up to degree four are defined. -*/ - -#include "flywheel:internal/mboit/trigonometric_moment_math.glsl" - - -/*! Given coefficients of a quadratic polynomial A*x^2+B*x+C, this function - outputs its two real roots.*/ -vec2 solveQuadratic(vec3 coeffs) -{ - coeffs[1] *= 0.5; - - float x1, x2, tmp; - - tmp = (coeffs[1] * coeffs[1] - coeffs[0] * coeffs[2]); - if (coeffs[1] >= 0) { - tmp = sqrt(tmp); - x1 = (-coeffs[2]) / (coeffs[1] + tmp); - x2 = (-coeffs[1] - tmp) / coeffs[0]; - } else { - tmp = sqrt(tmp); - x1 = (-coeffs[1] + tmp) / coeffs[0]; - x2 = coeffs[2] / (-coeffs[1] + tmp); - } - return vec2(x1, x2); -} - -/*! Code taken from the blog "Moments in Graphics" by Christoph Peters. - http://momentsingraphics.de/?p=105 - This function computes the three real roots of a cubic polynomial - Coefficient[0]+Coefficient[1]*x+Coefficient[2]*x^2+Coefficient[3]*x^3.*/ -vec3 SolveCubic(vec4 Coefficient) { - // Normalize the polynomial - Coefficient.xyz /= Coefficient.w; - // Divide middle coefficients by three - Coefficient.yz /= 3.0f; - // Compute the Hessian and the discrimant - vec3 Delta = vec3( - fma(-Coefficient.z, Coefficient.z, Coefficient.y), - fma(-Coefficient.y, Coefficient.z, Coefficient.x), - dot(vec2(Coefficient.z, -Coefficient.y), Coefficient.xy) - ); - float Discriminant = dot(vec2(4.0f*Delta.x, -Delta.y), Delta.zy); - // Compute coefficients of the depressed cubic - // (third is zero, fourth is one) - vec2 Depressed = vec2( - fma(-2.0f*Coefficient.z, Delta.x, Delta.y), - Delta.x - ); - // Take the cubic root of a normalized complex number - float Theta = atan(sqrt(Discriminant), -Depressed.x) / 3.0f; - vec2 CubicRoot; - sincos(Theta, CubicRoot.y, CubicRoot.x); - // Compute the three roots, scale appropriately and - // revert the depression transform - vec3 Root = vec3( - CubicRoot.x, - dot(vec2(-0.5f, -0.5f*sqrt(3.0f)), CubicRoot), - dot(vec2(-0.5f, 0.5f*sqrt(3.0f)), CubicRoot) - ); - Root = fma(vec3(2.0f*sqrt(-Depressed.y)), Root, vec3(-Coefficient.z)); - return Root; -} - -/*! Given coefficients of a cubic polynomial - coeffs[0]+coeffs[1]*x+coeffs[2]*x^2+coeffs[3]*x^3 with three real roots, - this function returns the root of least magnitude.*/ -float solveCubicBlinnSmallest(vec4 coeffs) -{ - coeffs.xyz /= coeffs.w; - coeffs.yz /= 3.0; - - vec3 delta = vec3(fma(-coeffs.z, coeffs.z, coeffs.y), fma(-coeffs.z, coeffs.y, coeffs.x), coeffs.z * coeffs.x - coeffs.y * coeffs.y); - float discriminant = 4.0 * delta.x * delta.z - delta.y * delta.y; - - vec2 depressed = vec2(delta.z, -coeffs.x * delta.y + 2.0 * coeffs.y * delta.z); - float theta = abs(atan(coeffs.x * sqrt(discriminant), -depressed.y)) / 3.0; - vec2 sin_cos; - sincos(theta, sin_cos.x, sin_cos.y); - float tmp = 2.0 * sqrt(-depressed.x); - vec2 x = vec2(tmp * sin_cos.y, tmp * (-0.5 * sin_cos.y - 0.5 * sqrt(3.0) * sin_cos.x)); - vec2 s = (x.x + x.y < 2.0 * coeffs.y) ? vec2(-coeffs.x, x.x + coeffs.y) : vec2(-coeffs.x, x.y + coeffs.y); - - return s.x / s.y; -} - -/*! Given coefficients of a quartic polynomial - coeffs[0]+coeffs[1]*x+coeffs[2]*x^2+coeffs[3]*x^3+coeffs[4]*x^4 with four - real roots, this function returns all roots.*/ -vec4 solveQuarticNeumark(float coeffs[5]) -{ - // Normalization - float B = coeffs[3] / coeffs[4]; - float C = coeffs[2] / coeffs[4]; - float D = coeffs[1] / coeffs[4]; - float E = coeffs[0] / coeffs[4]; - - // Compute coefficients of the cubic resolvent - float P = -2.0*C; - float Q = C*C + B*D - 4.0*E; - float R = D*D + B*B*E -B*C*D; - - // Obtain the smallest cubic root - float y = solveCubicBlinnSmallest(vec4(R, Q, P, 1.0)); - - float BB = B*B; - float fy = 4.0 * y; - float BB_fy = BB - fy; - - float Z = C - y; - float ZZ = Z*Z; - float fE = 4.0 * E; - float ZZ_fE = ZZ - fE; - - float G, g, H, h; - // Compute the coefficients of the quadratics adaptively using the two - // proposed factorizations by Neumark. Choose the appropriate - // factorizations using the heuristic proposed by Herbison-Evans. - if (y < 0 || (ZZ + fE) * BB_fy > ZZ_fE * (BB + fy)) { - float tmp = sqrt(BB_fy); - G = (B + tmp) * 0.5; - g = (B - tmp) * 0.5; - - tmp = (B*Z - 2.0*D) / (2.0*tmp); - H = fma(Z, 0.5, tmp); - h = fma(Z, 0.5, -tmp); - } else { - float tmp = sqrt(ZZ_fE); - H = (Z + tmp) * 0.5; - h = (Z - tmp) * 0.5; - - tmp = (B*Z - 2.0*D) / (2.0*tmp); - G = fma(B, 0.5, tmp); - g = fma(B, 0.5, -tmp); - } - // Solve the quadratics - return vec4(solveQuadratic(vec3(1.0, G, H)), solveQuadratic(vec3(1.0, g, h))); -} - -/*! Definition of utility functions for quantization and dequantization of - power moments stored in 16 bits per moment. */ -void offsetMoments(inout vec2 b_even, inout vec2 b_odd, float sign) -{ - b_odd += 0.5 * sign; -} - -void quantizeMoments(out vec2 b_even_q, out vec2 b_odd_q, vec2 b_even, vec2 b_odd) -{ - b_odd_q = b_odd * mat2x2(1.5f, sqrt(3.0f)*0.5f, -2.0f, -sqrt(3.0f)*2.0f / 9.0f); - b_even_q = b_even * mat2x2(4.0f, 0.5f, -4.0f, 0.5f); -} - -void offsetAndDequantizeMoments(out vec2 b_even, out vec2 b_odd, vec2 b_even_q, vec2 b_odd_q) -{ - offsetMoments(b_even_q, b_odd_q, -1.0); - b_odd = b_odd_q * mat2x2(-1.0f / 3.0f, -0.75f, sqrt(3.0f), 0.75f*sqrt(3.0f)); - b_even = b_even_q * mat2x2(0.125f, -0.125f, 1.0f, 1.0f); -} - -void offsetMoments(inout vec3 b_even, inout vec3 b_odd, float sign) -{ - b_odd += 0.5 * sign; - b_even.z += 0.018888946f * sign; -} - -void quantizeMoments(out vec3 b_even_q, out vec3 b_odd_q, vec3 b_even, vec3 b_odd) -{ - const mat3x3 QuantizationMatrixOdd = mat3x3( - 2.5f, -1.87499864450f, 1.26583039016f, - -10.0f, 4.20757543111f, -1.47644882902f, - 8.0f, -1.83257678661f, 0.71061660238f); - const mat3x3 QuantizationMatrixEven = mat3x3( - 4.0f, 9.0f, -0.57759806484f, - -4.0f, -24.0f, 4.61936647543f, - 0.0f, 16.0f, -3.07953906655f); - b_odd_q = b_odd * QuantizationMatrixOdd; - b_even_q = b_even * QuantizationMatrixEven; -} - -void offsetAndDequantizeMoments(out vec3 b_even, out vec3 b_odd, vec3 b_even_q, vec3 b_odd_q) -{ - const mat3x3 QuantizationMatrixOdd = mat3x3( - -0.02877789192f, 0.09995235706f, 0.25893353755f, - 0.47635550422f, 0.84532580931f, 0.90779616657f, - 1.55242808973f, 1.05472570761f, 0.83327335647f); - const mat3x3 QuantizationMatrixEven = mat3x3( - 0.00001253044f, -0.24998746956f, -0.37498825271f, - 0.16668494186f, 0.16668494186f, 0.21876713299f, - 0.86602540579f, 0.86602540579f, 0.81189881793f); - offsetMoments(b_even_q, b_odd_q, -1.0); - b_odd = b_odd_q * QuantizationMatrixOdd; - b_even = b_even_q * QuantizationMatrixEven; -} - -void offsetMoments(inout vec4 b_even, inout vec4 b_odd, float sign) -{ - b_odd += 0.5 * sign; - b_even += vec4(0.972481993925964, 1.0, 0.999179192513328, 0.991778293073131) * sign; -} - -void quantizeMoments(out vec4 b_even_q, out vec4 b_odd_q, vec4 b_even, vec4 b_odd) -{ - const mat4x4 mat_odd = mat4x4(3.48044635732474, -27.5760737514826, 55.1267384344761, -31.5311110403183, - 1.26797185782836, -0.928755808743913, -2.07520453231032, 1.23598848322588, - -2.1671560004294, 6.17950199592966, -0.276515571579297, -4.23583042392097, - 0.974332879165755, -0.443426830933027, -0.360491648368785, 0.310149466050223); - const mat4x4 mat_even = mat4x4(0.280504133158527, -0.757633844606942, 0.392179589334688, -0.887531871812237, - -2.01362265883247, 0.221551373038988, -1.06107954265125, 2.83887201588367, - -7.31010494985321, 13.9855979699139, -0.114305766176437, -7.4361899359832, - -15.8954215629556, 79.6186327084103, -127.457278992502, 63.7349456687829); - b_odd_q = mat_odd * b_odd; - b_even_q = mat_even * b_even; -} - -void offsetAndDequantizeMoments(out vec4 b_even, out vec4 b_odd, vec4 b_even_q, vec4 b_odd_q) -{ - const mat4x4 mat_odd = mat4x4(-0.00482399708502382, -0.423201508674231, 0.0348312382605129, 1.67179208266592, - -0.0233402218644408, -0.832829097046478, 0.0193406040499625, 1.21021509068975, - -0.010888537031885, -0.926393772997063, -0.11723394414779, 0.983723301818275, - -0.0308713357806732, -0.937989172670245, -0.218033377677099, 0.845991731322996); - const mat4x4 mat_even = mat4x4(-0.976220278891035, -0.456139260269401, -0.0504335521016742, 0.000838800390651085, - -1.04828341778299, -0.229726640510149, 0.0259608334616091, -0.00133632693205861, - -1.03115268628604, -0.077844420809897, 0.00443408851014257, -0.0103744938457406, - -0.996038443434636, 0.0175438624416783, -0.0361414253243963, -0.00317839994022725); - offsetMoments(b_even_q, b_odd_q, -1.0); - b_odd = mat_odd * b_odd_q; - b_even = mat_even * b_even_q; -} - -/*! This function reconstructs the transmittance at the given depth from four - normalized power moments and the given zeroth moment.*/ -float computeTransmittanceAtDepthFrom4PowerMoments(float b_0, vec2 b_even, vec2 b_odd, float depth, float bias, float overestimation, vec4 bias_vector) -{ - vec4 b = vec4(b_odd.x, b_even.x, b_odd.y, b_even.y); - // Bias input data to avoid artifacts - b = mix(b, bias_vector, bias); - vec3 z; - z[0] = depth; - - // Compute a Cholesky factorization of the Hankel matrix B storing only non- - // trivial entries or related products - float L21D11=fma(-b[0], b[1], b[2]); - float D11=fma(-b[0], b[0], b[1]); - float InvD11=1.0f/D11; - float L21=L21D11*InvD11; - float SquaredDepthVariance=fma(-b[1], b[1], b[3]); - float D22=fma(-L21D11, L21, SquaredDepthVariance); - - // Obtain a scaled inverse image of bz=(1,z[0],z[0]*z[0])^T - vec3 c=vec3(1.0f, z[0], z[0]*z[0]); - // Forward substitution to solve L*c1=bz - c[1]-=b.x; - c[2]-=b.y+L21*c[1]; - // Scaling to solve D*c2=c1 - c[1]*=InvD11; - c[2]/=D22; - // Backward substitution to solve L^T*c3=c2 - c[1]-=L21*c[2]; - c[0]-=dot(c.yz, b.xy); - // Solve the quadratic equation c[0]+c[1]*z+c[2]*z^2 to obtain solutions - // z[1] and z[2] - float InvC2=1.0f/c[2]; - float p=c[1]*InvC2; - float q=c[0]*InvC2; - float D=(p*p*0.25f)-q; - float r=sqrt(D); - z[1]=-p*0.5f-r; - z[2]=-p*0.5f+r; - // Compute the absorbance by summing the appropriate weights - vec3 polynomial; - vec3 weight_factor = vec3(overestimation, (z[1] < z[0])?1.0f:0.0f, (z[2] < z[0])?1.0f:0.0f); - float f0=weight_factor[0]; - float f1=weight_factor[1]; - float f2=weight_factor[2]; - float f01=(f1-f0)/(z[1]-z[0]); - float f12=(f2-f1)/(z[2]-z[1]); - float f012=(f12-f01)/(z[2]-z[0]); - polynomial[0]=f012; - polynomial[1]=polynomial[0]; - polynomial[0]=f01-polynomial[0]*z[1]; - polynomial[2]=polynomial[1]; - polynomial[1]=polynomial[0]-polynomial[1]*z[0]; - polynomial[0]=f0-polynomial[0]*z[0]; - float absorbance = polynomial[0] + dot(b.xy, polynomial.yz);; - // Turn the normalized absorbance into transmittance - return saturate(exp(-b_0 * absorbance)); -} - -/*! This function reconstructs the transmittance at the given depth from six - normalized power moments and the given zeroth moment.*/ -float computeTransmittanceAtDepthFrom6PowerMoments(float b_0, vec3 b_even, vec3 b_odd, float depth, float bias, float overestimation, float bias_vector[6]) -{ - float b[6] = { b_odd.x, b_even.x, b_odd.y, b_even.y, b_odd.z, b_even.z }; - // Bias input data to avoid artifacts - for (int i = 0; i != 6; ++i) { - b[i] = mix(b[i], bias_vector[i], bias); - } - - vec4 z; - z[0] = depth; - - // Compute a Cholesky factorization of the Hankel matrix B storing only non- - // trivial entries or related products - float InvD11 = 1.0f / fma(-b[0], b[0], b[1]); - float L21D11 = fma(-b[0], b[1], b[2]); - float L21 = L21D11*InvD11; - float D22 = fma(-L21D11, L21, fma(-b[1], b[1], b[3])); - float L31D11 = fma(-b[0], b[2], b[3]); - float L31 = L31D11*InvD11; - float InvD22 = 1.0f / D22; - float L32D22 = fma(-L21D11, L31, fma(-b[1], b[2], b[4])); - float L32 = L32D22*InvD22; - float D33 = fma(-b[2], b[2], b[5]) - dot(vec2(L31D11, L32D22), vec2(L31, L32)); - float InvD33 = 1.0f / D33; - - // Construct the polynomial whose roots have to be points of support of the - // canonical distribution: bz=(1,z[0],z[0]*z[0],z[0]*z[0]*z[0])^T - vec4 c; - c[0] = 1.0f; - c[1] = z[0]; - c[2] = c[1] * z[0]; - c[3] = c[2] * z[0]; - // Forward substitution to solve L*c1=bz - c[1] -= b[0]; - c[2] -= fma(L21, c[1], b[1]); - c[3] -= b[2] + dot(vec2(L31, L32), c.yz); - // Scaling to solve D*c2=c1 - c.yzw *= vec3(InvD11, InvD22, InvD33); - // Backward substitution to solve L^T*c3=c2 - c[2] -= L32*c[3]; - c[1] -= dot(vec2(L21, L31), c.zw); - c[0] -= dot(vec3(b[0], b[1], b[2]), c.yzw); - - // Solve the cubic equation - z.yzw = SolveCubic(c); - - // Compute the absorbance by summing the appropriate weights - vec4 weigth_factor; - weigth_factor[0] = overestimation; - weigth_factor.yzw = vec3(greaterThan(z.yzw, z.xxx)); - // Construct an interpolation polynomial - float f0 = weigth_factor[0]; - float f1 = weigth_factor[1]; - float f2 = weigth_factor[2]; - float f3 = weigth_factor[3]; - float f01 = (f1 - f0) / (z[1] - z[0]); - float f12 = (f2 - f1) / (z[2] - z[1]); - float f23 = (f3 - f2) / (z[3] - z[2]); - float f012 = (f12 - f01) / (z[2] - z[0]); - float f123 = (f23 - f12) / (z[3] - z[1]); - float f0123 = (f123 - f012) / (z[3] - z[0]); - vec4 polynomial; - // f012+f0123 *(z-z2) - polynomial[0] = fma(-f0123, z[2], f012); - polynomial[1] = f0123; - // *(z-z1) +f01 - polynomial[2] = polynomial[1]; - polynomial[1] = fma(polynomial[1], -z[1], polynomial[0]); - polynomial[0] = fma(polynomial[0], -z[1], f01); - // *(z-z0) +f0 - polynomial[3] = polynomial[2]; - polynomial[2] = fma(polynomial[2], -z[0], polynomial[1]); - polynomial[1] = fma(polynomial[1], -z[0], polynomial[0]); - polynomial[0] = fma(polynomial[0], -z[0], f0); - float absorbance = dot(polynomial, vec4 (1.0, b[0], b[1], b[2])); - // Turn the normalized absorbance into transmittance - return saturate(exp(-b_0 * absorbance)); -} - -/*! This function reconstructs the transmittance at the given depth from eight - normalized power moments and the given zeroth moment.*/ -float computeTransmittanceAtDepthFrom8PowerMoments(float b_0, vec4 b_even, vec4 b_odd, float depth, float bias, float overestimation, float bias_vector[8]) -{ - float b[8] = { b_odd.x, b_even.x, b_odd.y, b_even.y, b_odd.z, b_even.z, b_odd.w, b_even.w }; - // Bias input data to avoid artifacts - for (int i = 0; i != 8; ++i) { - b[i] = mix(b[i], bias_vector[i], bias); - } - - float z[5]; - z[0] = depth; - - // Compute a Cholesky factorization of the Hankel matrix B storing only non-trivial entries or related products - float D22 = fma(-b[0], b[0], b[1]); - float InvD22 = 1.0 / D22; - float L32D22 = fma(-b[1], b[0], b[2]); - float L32 = L32D22 * InvD22; - float L42D22 = fma(-b[2], b[0], b[3]); - float L42 = L42D22 * InvD22; - float L52D22 = fma(-b[3], b[0], b[4]); - float L52 = L52D22 * InvD22; - - float D33 = fma(-L32, L32D22, fma(-b[1], b[1], b[3])); - float InvD33 = 1.0 / D33; - float L43D33 = fma(-L42, L32D22, fma(-b[2], b[1], b[4])); - float L43 = L43D33 * InvD33; - float L53D33 = fma(-L52, L32D22, fma(-b[3], b[1], b[5])); - float L53 = L53D33 * InvD33; - - float D44 = fma(-b[2], b[2], b[5]) - dot(vec2(L42, L43), vec2(L42D22, L43D33)); - float InvD44 = 1.0 / D44; - float L54D44 = fma(-b[3], b[2], b[6]) - dot(vec2(L52, L53), vec2(L42D22, L43D33)); - float L54 = L54D44 * InvD44; - - float D55 = fma(-b[3], b[3], b[7]) - dot(vec3(L52, L53, L54), vec3(L52D22, L53D33, L54D44)); - float InvD55 = 1.0 / D55; - - // Construct the polynomial whose roots have to be points of support of the - // Canonical distribution: - // bz = (1,z[0],z[0]^2,z[0]^3,z[0]^4)^T - float c[5]; - c[0] = 1.0; - c[1] = z[0]; - c[2] = c[1] * z[0]; - c[3] = c[2] * z[0]; - c[4] = c[3] * z[0]; - - // Forward substitution to solve L*c1 = bz - c[1] -= b[0]; - c[2] -= fma(L32, c[1], b[1]); - c[3] -= b[2] + dot(vec2(L42, L43), vec2(c[1], c[2])); - c[4] -= b[3] + dot(vec3(L52, L53, L54), vec3(c[1], c[2], c[3])); - - // Scaling to solve D*c2 = c1 - //c = c .*[1, InvD22, InvD33, InvD44, InvD55]; - c[1] *= InvD22; - c[2] *= InvD33; - c[3] *= InvD44; - c[4] *= InvD55; - - // Backward substitution to solve L^T*c3 = c2 - c[3] -= L54 * c[4]; - c[2] -= dot(vec2(L53, L43), vec2(c[4], c[3])); - c[1] -= dot(vec3(L52, L42, L32), vec3(c[4], c[3], c[2])); - c[0] -= dot(vec4(b[3], b[2], b[1], b[0]), vec4(c[4], c[3], c[2], c[1])); - - // Solve the quartic equation - vec4 zz = solveQuarticNeumark(c); - z[1] = zz[0]; - z[2] = zz[1]; - z[3] = zz[2]; - z[4] = zz[3]; - - // Compute the absorbance by summing the appropriate weights - vec4 weigth_factor = vec4(lessThanEqual(vec4(z[1], z[2], z[3], z[4]), z[0].xxxx)); - // Construct an interpolation polynomial - float f0 = overestimation; - float f1 = weigth_factor[0]; - float f2 = weigth_factor[1]; - float f3 = weigth_factor[2]; - float f4 = weigth_factor[3]; - float f01 = (f1 - f0) / (z[1] - z[0]); - float f12 = (f2 - f1) / (z[2] - z[1]); - float f23 = (f3 - f2) / (z[3] - z[2]); - float f34 = (f4 - f3) / (z[4] - z[3]); - float f012 = (f12 - f01) / (z[2] - z[0]); - float f123 = (f23 - f12) / (z[3] - z[1]); - float f234 = (f34 - f23) / (z[4] - z[2]); - float f0123 = (f123 - f012) / (z[3] - z[0]); - float f1234 = (f234 - f123) / (z[4] - z[1]); - float f01234 = (f1234 - f0123) / (z[4] - z[0]); - - float Polynomial_0; - vec4 Polynomial; - // f0123 + f01234 * (z - z3) - Polynomial_0 = fma(-f01234, z[3], f0123); - Polynomial[0] = f01234; - // * (z - z2) + f012 - Polynomial[1] = Polynomial[0]; - Polynomial[0] = fma(-Polynomial[0], z[2], Polynomial_0); - Polynomial_0 = fma(-Polynomial_0, z[2], f012); - // * (z - z1) + f01 - Polynomial[2] = Polynomial[1]; - Polynomial[1] = fma(-Polynomial[1], z[1], Polynomial[0]); - Polynomial[0] = fma(-Polynomial[0], z[1], Polynomial_0); - Polynomial_0 = fma(-Polynomial_0, z[1], f01); - // * (z - z0) + f1 - Polynomial[3] = Polynomial[2]; - Polynomial[2] = fma(-Polynomial[2], z[0], Polynomial[1]); - Polynomial[1] = fma(-Polynomial[1], z[0], Polynomial[0]); - Polynomial[0] = fma(-Polynomial[0], z[0], Polynomial_0); - Polynomial_0 = fma(-Polynomial_0, z[0], f0); - float absorbance = Polynomial_0 + dot(Polynomial, vec4(b[0], b[1], b[2], b[3])); - // Turn the normalized absorbance into transmittance - return saturate(exp(-b_0 * absorbance)); -} diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl deleted file mode 100644 index 23ef4ec03..000000000 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/moment_oit.glsl +++ /dev/null @@ -1,251 +0,0 @@ -/*! \file - This header provides the functionality to create the vectors of moments and - to blend surfaces together with an appropriately reconstructed - transmittance. It is needed for both additive passes of moment-based OIT. -*/ - -//cbuffer MomentOIT -//{ -// struct { -// vec4 wrapping_zone_parameters; -// float overestimation; -// float moment_bias; -// }MomentOIT; -//}; - -#include "flywheel:internal/mboit/moment_math.glsl" - -const float moment_bias = 0.25; -const float overestimation = 0.25; -const vec4 wrapping_zone_parameters = vec4(0.31415927, 2.984513, 2.7934167, -18.553917); - - -void clip(float a) { - if (a < 0.) { - discard; - } -} - -// jozu: The trigonometric moments and higher order power moments rely on a second render target -// which the java side is not set up to support. Trying to enable them as is will cause compile errors also. -#define NUM_MOMENTS 8 - -#define SINGLE_PRECISION 1 - -#ifdef _FLW_GENERATE_MOMENTS -/*! Generation of moments in case that rasterizer ordered views are used. - This includes the case if moments are stored in 16 bits. */ - -/*! This functions relies on fixed function additive blending to compute the - vector of moments.moment vector. The shader that calls this function must - provide the required render targets.*/ -#if NUM_MOMENTS == 4 -void generateMoments(float depth, float transmittance, out float b_0, out vec4 b) -#elif NUM_MOMENTS == 6 -#if USE_R_RG_RBBA_FOR_MBOIT6 -void generateMoments(float depth, float transmittance, out float b_0, out vec2 b_12, out vec4 b_3456) -#else -void generateMoments(float depth, float transmittance, out float b_0, out vec2 b_12, out vec2 b_34, out vec2 b_56) -#endif -#elif NUM_MOMENTS == 8 -void generateMoments(float depth, float transmittance, out float b_0, out vec4 b_even, out vec4 b_odd) -#endif -{ - transmittance = max(transmittance, 0.000001); - float absorbance = -log(transmittance); - - b_0 = absorbance; - #if TRIGONOMETRIC - float phase = fma(depth, wrapping_zone_parameters.y, wrapping_zone_parameters.y); - vec2 circle_point = vec2(sin(phase), cos(phase)); - - vec2 circle_point_pow2 = Multiply(circle_point, circle_point); - #if NUM_MOMENTS == 4 - b = vec4(circle_point, circle_point_pow2) * absorbance; - #elif NUM_MOMENTS == 6 - b_12 = circle_point * absorbance; - #if USE_R_RG_RBBA_FOR_MBOIT6 - b_3456 = vec4(circle_point_pow2, Multiply(circle_point, circle_point_pow2)) * absorbance; - #else - b_34 = circle_point_pow2 * absorbance; - b_56 = Multiply(circle_point, circle_point_pow2) * absorbance; - #endif - #elif NUM_MOMENTS == 8 - b_even = vec4(circle_point_pow2, Multiply(circle_point_pow2, circle_point_pow2)) * absorbance; - b_odd = vec4(circle_point, Multiply(circle_point, circle_point_pow2)) * absorbance; - #endif - #else - float depth_pow2 = depth * depth; - float depth_pow4 = depth_pow2 * depth_pow2; - #if NUM_MOMENTS == 4 - b = vec4(depth, depth_pow2, depth_pow2 * depth, depth_pow4) * absorbance; - #elif NUM_MOMENTS == 6 - b_12 = vec2(depth, depth_pow2) * absorbance; - #if USE_R_RG_RBBA_FOR_MBOIT6 - b_3456 = vec4(depth_pow2 * depth, depth_pow4, depth_pow4 * depth, depth_pow4 * depth_pow2) * absorbance; - #else - b_34 = vec2(depth_pow2 * depth, depth_pow4) * absorbance; - b_56 = vec2(depth_pow4 * depth, depth_pow4 * depth_pow2) * absorbance; - #endif - #elif NUM_MOMENTS == 8 - float depth_pow6 = depth_pow4 * depth_pow2; - b_even = vec4(depth_pow2, depth_pow4, depth_pow6, depth_pow6 * depth_pow2) * absorbance; - b_odd = vec4(depth, depth_pow2 * depth, depth_pow4 * depth, depth_pow6 * depth) * absorbance; - #endif - #endif -} - -#else//MOMENT_GENERATION is disabled - -layout (binding = 7) uniform sampler2D _flw_zeroth_moment_sampler; -layout (binding = 8) uniform sampler2D _flw_moments0_sampler; -layout (binding = 9) uniform sampler2D _flw_moments1_sampler; - -/*! This function is to be called from the shader that composites the - transparent fragments. It reads the moments and calls the appropriate - function to reconstruct the transmittance at the specified depth.*/ -void resolveMoments(out float transmittance_at_depth, out float total_transmittance, float depth, vec2 sv_pos) -{ - ivec2 idx0 = ivec2(sv_pos); - ivec2 idx1 = idx0; - - transmittance_at_depth = 1; - total_transmittance = 1; - - float b_0 = texelFetch(_flw_zeroth_moment_sampler, idx0, 0).x; - clip(b_0 - 0.00100050033f); - total_transmittance = exp(-b_0); - - #if NUM_MOMENTS == 4 - #if TRIGONOMETRIC - vec4 b_tmp = texelFetch(_flw_moments0_sampler, idx0, 0); - vec2 trig_b[2]; - trig_b[0] = b_tmp.xy; - trig_b[1] = b_tmp.zw; - #if SINGLE_PRECISION - trig_b[0] /= b_0; - trig_b[1] /= b_0; - #else - trig_b[0] = fma(trig_b[0], 2.0, -1.0); - trig_b[1] = fma(trig_b[1], 2.0, -1.0); - #endif - transmittance_at_depth = computeTransmittanceAtDepthFrom2TrigonometricMoments(b_0, trig_b, depth, moment_bias, overestimation, wrapping_zone_parameters); - #else - vec4 b_1234 = texelFetch(_flw_moments0_sampler, idx0, 0).xyzw; - #if SINGLE_PRECISION - vec2 b_even = b_1234.yw; - vec2 b_odd = b_1234.xz; - - b_even /= b_0; - b_odd /= b_0; - - const vec4 bias_vector = vec4(0, 0.375, 0, 0.375); - #else - vec2 b_even_q = b_1234.yw; - vec2 b_odd_q = b_1234.xz; - - // Dequantize the moments - vec2 b_even; - vec2 b_odd; - offsetAndDequantizeMoments(b_even, b_odd, b_even_q, b_odd_q); - const vec4 bias_vector = vec4(0, 0.628, 0, 0.628); - #endif - transmittance_at_depth = computeTransmittanceAtDepthFrom4PowerMoments(b_0, b_even, b_odd, depth, moment_bias, overestimation, bias_vector); - #endif - #elif NUM_MOMENTS == 6 - ivec2 idx2 = idx0; - #if TRIGONOMETRIC - vec2 trig_b[3]; - trig_b[0] = texelFetch(_flw_moments0_sampler, idx0, 0).xy; - #if USE_R_RG_RBBA_FOR_MBOIT6 - vec4 tmp = texelFetch(extra_moments, idx0, 0); - trig_b[1] = tmp.xy; - trig_b[2] = tmp.zw; - #else - trig_b[1] = texelFetch(_flw_moments1_sampler, idx1, 0).xy; - trig_b[2] = texelFetch(_flw_moments0_sampler, idx2, 0).xy; - #endif - #if SINGLE_PRECISION - trig_b[0] /= b_0; - trig_b[1] /= b_0; - trig_b[2] /= b_0; - #else - trig_b[0] = fma(trig_b[0], 2.0, -1.0); - trig_b[1] = fma(trig_b[1], 2.0, -1.0); - trig_b[2] = fma(trig_b[2], 2.0, -1.0); - #endif - transmittance_at_depth = computeTransmittanceAtDepthFrom3TrigonometricMoments(b_0, trig_b, depth, moment_bias, overestimation, wrapping_zone_parameters); - #else - vec2 b_12 = texelFetch(_flw_moments0_sampler, idx0, 0).xy; - #if USE_R_RG_RBBA_FOR_MBOIT6 - vec4 tmp = texelFetch(extra_moments, idx0, 0); - vec2 b_34 = tmp.xy; - vec2 b_56 = tmp.zw; - #else - vec2 b_34 = texelFetch(_flw_moments1_sampler, idx1, 0).xy; - vec2 b_56 = texelFetch(_flw_moments0_sampler, idx2, 0).xy; - #endif - #if SINGLE_PRECISION - vec3 b_even = vec3(b_12.y, b_34.y, b_56.y); - vec3 b_odd = vec3(b_12.x, b_34.x, b_56.x); - - b_even /= b_0; - b_odd /= b_0; - - const float bias_vector[6] = { 0, 0.48, 0, 0.451, 0, 0.45 }; - #else - vec3 b_even_q = vec3(b_12.y, b_34.y, b_56.y); - vec3 b_odd_q = vec3(b_12.x, b_34.x, b_56.x); - // Dequantize b_0 and the other moments - vec3 b_even; - vec3 b_odd; - offsetAndDequantizeMoments(b_even, b_odd, b_even_q, b_odd_q); - - const float bias_vector[6] = { 0, 0.5566, 0, 0.489, 0, 0.47869382 }; - #endif - transmittance_at_depth = computeTransmittanceAtDepthFrom6PowerMoments(b_0, b_even, b_odd, depth, moment_bias, overestimation, bias_vector); - #endif - #elif NUM_MOMENTS == 8 - #if TRIGONOMETRIC - vec4 b_tmp = texelFetch(_flw_moments0_sampler, idx0, 0); - vec4 b_tmp2 = texelFetch(_flw_moments1_sampler, idx1, 0); - #if SINGLE_PRECISION - vec2 trig_b[4] = { - b_tmp2.xy / b_0, - b_tmp.xy / b_0, - b_tmp2.zw / b_0, - b_tmp.zw / b_0 - }; - #else - vec2 trig_b[4] = { - fma(b_tmp2.xy, 2.0, -1.0), - fma(b_tmp.xy, 2.0, -1.0), - fma(b_tmp2.zw, 2.0, -1.0), - fma(b_tmp.zw, 2.0, -1.0) - }; - #endif - transmittance_at_depth = computeTransmittanceAtDepthFrom4TrigonometricMoments(b_0, trig_b, depth, moment_bias, overestimation, wrapping_zone_parameters); - #else - #if SINGLE_PRECISION - vec4 b_even = texelFetch(_flw_moments0_sampler, idx0, 0); - vec4 b_odd = texelFetch(_flw_moments1_sampler, idx1, 0); - - b_even /= b_0; - b_odd /= b_0; - const float bias_vector[8] = { 0, 0.75, 0, 0.67666666666666664, 0, 0.63, 0, 0.60030303030303034 }; - #else - vec4 b_even_q = texelFetch(_flw_moments0_sampler, idx0, 0); - vec4 b_odd_q = texelFetch(_flw_moments1_sampler, idx1, 0); - - // Dequantize the moments - vec4 b_even; - vec4 b_odd; - offsetAndDequantizeMoments(b_even, b_odd, b_even_q, b_odd_q); - const float bias_vector[8] = { 0, 0.42474916387959866, 0, 0.22407802675585284, 0, 0.15369230769230768, 0, 0.12900440529089119 }; - #endif - transmittance_at_depth = computeTransmittanceAtDepthFrom8PowerMoments(b_0, b_even, b_odd, depth, moment_bias, overestimation, bias_vector); - #endif - #endif - -} -#endif diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl deleted file mode 100644 index 30fe6a65e..000000000 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/mboit/trigonometric_moment_math.glsl +++ /dev/null @@ -1,311 +0,0 @@ -/*! \file - This header provides the utility functions to reconstruct the transmittance - from a given vector of trigonometric moments (2, 3 or 4 trigonometric - moments) at a specified depth.*/ -#include "flywheel:internal/mboit/complex_algebra.glsl" - -/*! This utility function turns a point on the unit circle into a scalar - parameter. It is guaranteed to grow monotonically for (cos(phi),sin(phi)) - with phi in 0 to 2*pi. There are no other guarantees. In particular it is - not an arclength parametrization. If you change this function, you must - also change circleToParameter() in MomentOIT.cpp.*/ -float circleToParameter(vec2 circle_point){ - float result=abs(circle_point.y)-abs(circle_point.x); - result=(circle_point.x<0.0f)?(2.0f-result):result; - return (circle_point.y<0.0f)?(6.0f-result):result; -} - -/*! This utility function returns the appropriate weight factor for a root at - the given location. Both inputs are supposed to be unit vectors. If a - circular arc going counter clockwise from (1.0,0.0) meets root first, it - returns 1.0, otherwise 0.0 or a linear ramp in the wrapping zone.*/ -float getRootWeightFactor(float reference_parameter, float root_parameter, vec4 wrapping_zone_parameters){ - float binary_weight_factor=(root_parameter Date: Fri, 21 Feb 2025 20:33:46 -0800 Subject: [PATCH 09/23] Inline tuning - Inline most defines, I don't plan on changing them - Fix depth passed to REMOVE_SIGNAL block - Use (blindly copied) optimized function in compositing - Make the noise factor a uniform --- .../engine/indirect/IndirectCullingGroup.java | 2 + .../flywheel/flywheel/internal/common.frag | 69 ++++++++--------- .../internal/indirect/oit_composite.frag | 76 +++++++++---------- 3 files changed, 71 insertions(+), 76 deletions(-) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java index 25e38ad11..6f3a52d35 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -221,6 +221,8 @@ public class IndirectCullingGroup { // Don't need to do this unless the program changes. drawProgram.bind(); + + drawProgram.setFloat("_flw_blueNoiseFactor", 0.08f); } MaterialRenderState.setupOit(multiDraw.material); diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag index d4715ff3a..c823e617b 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag @@ -21,19 +21,12 @@ flat in uvec2 _flw_ids; #define TRANSPARENCY_WAVELET_RANK 3 #define TRANSPARENCY_WAVELET_COEFFICIENT_COUNT 16 -#define floatN float -#define all(e) (e) -#define mad fma -#define lerp mix -#define Coefficients_Out vec4[4] -#define Coefficients_In sampler2DArray +#define REMOVE_SIGNAL true layout (binding = 7) uniform sampler2D _flw_depthRange; layout (binding = 8) uniform sampler2DArray _flw_coefficients; -#define REMOVE_SIGNAL true - #ifdef _FLW_DEPTH_RANGE layout (location = 0) out vec2 _flw_depthRange_out; @@ -49,11 +42,11 @@ layout (location = 1) out vec4 _flw_coeffs1; layout (location = 2) out vec4 _flw_coeffs2; layout (location = 3) out vec4 _flw_coeffs3; -void add_to_index(inout Coefficients_Out coefficients, uint index, floatN addend) { +void add_to_index(inout vec4[4] coefficients, uint index, float addend) { coefficients[index >> 2][index & 3u] = addend; } -void add_event_to_wavelets(inout Coefficients_Out coefficients, floatN signal, float depth) +void add_event_to_wavelets(inout vec4[4] coefficients, float signal, float depth) { depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; @@ -68,17 +61,17 @@ void add_event_to_wavelets(inout Coefficients_Out coefficients, floatN signal, f int wavelet_sign = ((index & 1) << 1) - 1; float wavelet_phase = ((index + 1) & 1) * exp2(-power); - floatN addend = mad(mad(-exp2(-power), k, depth), wavelet_sign, wavelet_phase) * exp2(power * 0.5) * signal; + float addend = fma(fma(-exp2(-power), k, depth), wavelet_sign, wavelet_phase) * exp2(power * 0.5) * signal; add_to_index(coefficients, new_index, addend); index = new_index; } - floatN addend = mad(signal, -depth, signal); + float addend = fma(signal, -depth, signal); add_to_index(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1, addend); } -void add_transmittance_event_to_wavelets(inout Coefficients_Out coefficients, floatN transmittance, float depth) +void add_transmittance_event_to_wavelets(inout vec4[4] coefficients, float transmittance, float depth) { float absorbance = -log(max(transmittance, 0.00001));// transforming the signal from multiplicative transmittance to additive absorbance add_event_to_wavelets(coefficients, absorbance, depth); @@ -91,25 +84,26 @@ void add_transmittance_event_to_wavelets(inout Coefficients_Out coefficients, fl layout (location = 0) out vec4 _flw_accumulate; -floatN get_coefficients(in Coefficients_In coefficients, uint index) { +float get_coefficients(in sampler2DArray coefficients, uint index) { return texelFetch(coefficients, ivec3(gl_FragCoord.xy, index >> 2), 0)[index & 3u]; } - floatN evaluate_wavelets(in Coefficients_In coefficients, float depth, floatN signal) +float evaluate_wavelets(in sampler2DArray coefficients, float depth, float signal) { - floatN scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); - if (all(scale_coefficient == 0)) + float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + if (scale_coefficient == 0) { return 0; } - if (REMOVE_SIGNAL) - { - floatN scale_coefficient_addend = mad(signal, -depth, signal); - scale_coefficient -= scale_coefficient_addend; - } depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; + if (REMOVE_SIGNAL) + { + float scale_coefficient_addend = fma(signal, -depth, signal); + scale_coefficient -= scale_coefficient_addend; + } + float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; int index_b = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); bool sample_a = index_b >= 1; @@ -118,8 +112,8 @@ floatN get_coefficients(in Coefficients_In coefficients, uint index) { index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; index_a += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; - floatN b = scale_coefficient; -floatN a = sample_a ? scale_coefficient : 0; + float b = scale_coefficient; + float a = sample_a ? scale_coefficient : 0; for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) { @@ -127,12 +121,12 @@ floatN a = sample_a ? scale_coefficient : 0; int new_index_b = (index_b - 1) >> 1; int wavelet_sign_b = ((index_b & 1) << 1) - 1; - floatN coeff_b = get_coefficients(coefficients, new_index_b); + float coeff_b = get_coefficients(coefficients, new_index_b); if (REMOVE_SIGNAL) { float wavelet_phase_b = ((index_b + 1) & 1) * exp2(-power); float k = float((new_index_b + 1) & ((1 << power) - 1)); - floatN addend = mad(mad(-exp2(-power), k, depth), wavelet_sign_b, wavelet_phase_b) * exp2(power * 0.5) * signal; + float addend = fma(fma(-exp2(-power), k, depth), wavelet_sign_b, wavelet_phase_b) * exp2(power * 0.5) * signal; coeff_b -= addend; } b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b; @@ -142,7 +136,7 @@ floatN a = sample_a ? scale_coefficient : 0; { int new_index_a = (index_a - 1) >> 1; int wavelet_sign_a = ((index_a & 1) << 1) - 1; - floatN coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a);// No addend here on purpose, the original signal didn't contribute to this coefficient + float coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a);// No addend here on purpose, the original signal didn't contribute to this coefficient a -= exp2(float(power) * 0.5) * coeff_a * wavelet_sign_a; index_a = new_index_a; } @@ -150,12 +144,12 @@ floatN a = sample_a ? scale_coefficient : 0; float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth); - return lerp(a, b, t); + return mix(a, b, t); } - floatN evaluate_transmittance_wavelets(in Coefficients_In coefficients, float depth, floatN signal) +float evaluate_transmittance_wavelets(in sampler2DArray coefficients, float depth, float signal) { - floatN absorbance = evaluate_wavelets(coefficients, depth, signal); + float absorbance = evaluate_wavelets(coefficients, depth, signal); return clamp(exp(-absorbance), 0., 1.);// undoing the transformation from absorbance back to transmittance } @@ -201,9 +195,9 @@ float blue() { return mask; } -uniform vec3 _flw_depthAdjust; +uniform float _flw_blueNoiseFactor = 0.08; -float adjust_depth(float normalizedDepth) { +float tented_blue_noise(float normalizedDepth) { float tentIn = abs(normalizedDepth * 2. - 1); float tentIn2 = tentIn * tentIn; @@ -212,7 +206,7 @@ float adjust_depth(float normalizedDepth) { float b = blue(); - return normalizedDepth - b * tent * 0.08; + return b * tent; } float linearize_depth(float d, float zNear, float zFar) { @@ -228,9 +222,10 @@ float depth() { float linearDepth = linear_depth(); vec2 depthRange = texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).rg; - float depth = (linearDepth + depthRange.x) / (depthRange.x + depthRange.y); + float delta = depthRange.x + depthRange.y; + float depth = (linearDepth + depthRange.x) / delta; - return adjust_depth(depth); + return depth - tented_blue_noise(depth) * _flw_blueNoiseFactor; } @@ -335,7 +330,7 @@ void _flw_main() { #ifdef _FLW_COLLECT_COEFFS - Coefficients_Out result; + vec4[4] result; result[0] = vec4(0.); result[1] = vec4(0.); result[2] = vec4(0.); @@ -352,7 +347,7 @@ void _flw_main() { #ifdef _FLW_EVALUATE - floatN transmittance = evaluate_transmittance_wavelets(_flw_coefficients, depth(), 1. - color.a); + float transmittance = evaluate_transmittance_wavelets(_flw_coefficients, depth(), 1. - color.a); _flw_accumulate = vec4(color.rgb * color.a, color.a) * transmittance; diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag index 1fee5f927..b09ed5acd 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag @@ -5,41 +5,15 @@ layout (binding = 1) uniform sampler2D _flw_accumulate; #define TRANSPARENCY_WAVELET_RANK 3 #define TRANSPARENCY_WAVELET_COEFFICIENT_COUNT 16 -#define floatN float -#define all(e) (e) -#define mad fma -#define lerp mix -#define Coefficients_Out vec4[4] -#define Coefficients_In sampler2DArray - -floatN get_coefficients(in Coefficients_In coefficients, uint index) { +float get_coefficients(in sampler2DArray coefficients, uint index) { return texelFetch(coefficients, ivec3(gl_FragCoord.xy, index >> 2), 0)[index & 3u]; } - floatN evaluate_wavelet_index(in Coefficients_In coefficients, int index) +float evaluate_wavelets(in sampler2DArray coefficients, float depth) { - floatN result = 0; - - index += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; - - for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) - { - int power = TRANSPARENCY_WAVELET_RANK - i; - int new_index = (index - 1) >> 1; - floatN coeff = get_coefficients(coefficients, new_index); - int wavelet_sign = ((index & 1) << 1) - 1; - result -= exp2(float(power) * 0.5) * coeff * wavelet_sign; - index = new_index; - } - return result; -} - - - floatN evaluate_wavelets(in Coefficients_In coefficients, float depth) -{ - floatN scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); - if (all(scale_coefficient == 0)) + float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + if (scale_coefficient == 0) { return 0; } @@ -47,20 +21,44 @@ floatN get_coefficients(in Coefficients_In coefficients, uint index) { depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; - int index = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + int index_b = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + bool sample_a = index_b >= 1; + int index_a = sample_a ? (index_b - 1) : index_b; - floatN a = 0; -floatN b = scale_coefficient + evaluate_wavelet_index(coefficients, index); - if (index > 0) { a = scale_coefficient + evaluate_wavelet_index(coefficients, index - 1); } + index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + index_a += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + + float b = scale_coefficient; + float a = sample_a ? scale_coefficient : 0; + + for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) + { + int power = TRANSPARENCY_WAVELET_RANK - i; + + int new_index_b = (index_b - 1) >> 1; + int wavelet_sign_b = ((index_b & 1) << 1) - 1; + float coeff_b = get_coefficients(coefficients, new_index_b); + b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b; + index_b = new_index_b; + + if (sample_a) + { + int new_index_a = (index_a - 1) >> 1; + int wavelet_sign_a = ((index_a & 1) << 1) - 1; + float coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a); + a -= exp2(float(power) * 0.5) * coeff_a * wavelet_sign_a; + index_a = new_index_a; + } + } float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth); - floatN signal = lerp(a, b, t);// You can experiment here with different types of interpolation as well - return signal; + + return mix(a, b, t); } - floatN evaluate_transmittance_wavelets(in Coefficients_In coefficients, float depth) +float evaluate_transmittance_wavelets(in sampler2DArray coefficients, float depth) { - floatN absorbance = evaluate_wavelets(coefficients, depth); + float absorbance = evaluate_wavelets(coefficients, depth); return clamp(exp(-absorbance), 0., 1.);// undoing the transformation from absorbance back to transmittance } @@ -73,7 +71,7 @@ void main() { discard; } - floatN total_transmittance = evaluate_transmittance_wavelets(_flw_coefficients, infinity); + float total_transmittance = evaluate_transmittance_wavelets(_flw_coefficients, infinity); frag = vec4(texel.rgb / texel.a, total_transmittance); } From 8407d206baad49960b40e7ce1283109284b033d6 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 21 Feb 2025 22:27:05 -0800 Subject: [PATCH 10/23] Texture this - Add blue noise texture --- .../flywheel/backend/NoiseTextures.java | 43 +++++++ .../flywheel/backend/Samplers.java | 1 + .../engine/indirect/IndirectCullingGroup.java | 2 +- .../engine/indirect/OitFramebuffer.java | 13 ++- .../flywheel/flywheel/internal/common.frag | 110 ++++++------------ .../textures/flywheel/noise/blue/0.png | Bin 0 -> 7115 bytes .../backend/compile/FlwProgramsReloader.java | 2 + .../backend/compile/FlwProgramsReloader.java | 2 + 8 files changed, 97 insertions(+), 76 deletions(-) create mode 100644 common/src/backend/java/dev/engine_room/flywheel/backend/NoiseTextures.java create mode 100644 common/src/backend/resources/assets/flywheel/textures/flywheel/noise/blue/0.png diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/NoiseTextures.java b/common/src/backend/java/dev/engine_room/flywheel/backend/NoiseTextures.java new file mode 100644 index 000000000..157a64ded --- /dev/null +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/NoiseTextures.java @@ -0,0 +1,43 @@ +package dev.engine_room.flywheel.backend; + +import java.io.IOException; + +import org.jetbrains.annotations.UnknownNullability; + +import com.mojang.blaze3d.platform.NativeImage; + +import dev.engine_room.flywheel.api.Flywheel; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; + +public class NoiseTextures { + + public static final ResourceLocation NOISE_TEXTURE = Flywheel.rl("textures/flywheel/noise/blue/0.png"); + public static final int NOISE_LAYERS = 16; + + @UnknownNullability + public static DynamicTexture BLUE_NOISE; + + + public static void reload(ResourceManager manager) { + if (BLUE_NOISE != null) { + BLUE_NOISE.close(); + BLUE_NOISE = null; + } + var optional = manager.getResource(NOISE_TEXTURE); + + if (optional.isEmpty()) { + return; + } + + try (var is = optional.get() + .open()) { + var image = NativeImage.read(NativeImage.Format.LUMINANCE, is); + + BLUE_NOISE = new DynamicTexture(image); + } catch (IOException e) { + + } + } +} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java b/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java index 272314894..db27bc218 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/Samplers.java @@ -13,4 +13,5 @@ public class Samplers { public static final GlTextureUnit DEPTH_RANGE = GlTextureUnit.T7; public static final GlTextureUnit COEFFICIENTS = GlTextureUnit.T8; + public static final GlTextureUnit NOISE = GlTextureUnit.T9; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java index 6f3a52d35..886f76830 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -222,7 +222,7 @@ public class IndirectCullingGroup { // Don't need to do this unless the program changes. drawProgram.bind(); - drawProgram.setFloat("_flw_blueNoiseFactor", 0.08f); + drawProgram.setFloat("_flw_blueNoiseFactor", 0.07f); } MaterialRenderState.setupOit(multiDraw.material); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java index 3d50fb76e..23260f106 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java @@ -6,6 +6,7 @@ import org.lwjgl.opengl.GL46; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; +import dev.engine_room.flywheel.backend.NoiseTextures; import dev.engine_room.flywheel.backend.Samplers; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; import dev.engine_room.flywheel.backend.gl.GlTextureUnit; @@ -63,6 +64,14 @@ public class OitFramebuffer { Samplers.DEPTH_RANGE.makeActive(); GlStateManager._bindTexture(depthBounds); + Samplers.NOISE.makeActive(); + NoiseTextures.BLUE_NOISE.bind(); + + NoiseTextures.BLUE_NOISE.setFilter(true, false); + GL46.glTextureParameteri(NoiseTextures.BLUE_NOISE.getId(), GL32.GL_TEXTURE_WRAP_S, GL32.GL_REPEAT); + GL46.glTextureParameteri(NoiseTextures.BLUE_NOISE.getId(), GL32.GL_TEXTURE_WRAP_T, GL32.GL_REPEAT); + + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT1, GL46.GL_COLOR_ATTACHMENT2, GL46.GL_COLOR_ATTACHMENT3, GL46.GL_COLOR_ATTACHMENT4}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); @@ -85,9 +94,11 @@ public class OitFramebuffer { Samplers.COEFFICIENTS.makeActive(); GlStateManager._bindTexture(0); - GL46.glBindTextureUnit(Samplers.COEFFICIENTS.number, coefficients); + Samplers.NOISE.makeActive(); + NoiseTextures.BLUE_NOISE.bind(); + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT5}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag index c823e617b..fe476a9c5 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag @@ -27,6 +27,42 @@ layout (binding = 7) uniform sampler2D _flw_depthRange; layout (binding = 8) uniform sampler2DArray _flw_coefficients; +layout (binding = 9) uniform sampler2D _flw_blueNoise; + + +uniform float _flw_blueNoiseFactor = 0.08; + +float tented_blue_noise(float normalizedDepth) { + + float tentIn = abs(normalizedDepth * 2. - 1); + float tentIn2 = tentIn * tentIn; + float tentIn4 = tentIn2 * tentIn2; + float tent = 1 - (tentIn2 * tentIn4); + + float b = texture(_flw_blueNoise, gl_FragCoord.xy / vec2(64)).r; + + return b * tent; +} + +float linearize_depth(float d, float zNear, float zFar) { + float z_n = 2.0 * d - 1.0; + return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); +} + +float linear_depth() { + return linearize_depth(gl_FragCoord.z, _flw_cullData.znear, _flw_cullData.zfar); +} + +float depth() { + float linearDepth = linear_depth(); + + vec2 depthRange = texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).rg; + float delta = depthRange.x + depthRange.y; + float depth = (linearDepth + depthRange.x) / delta; + + return depth - tented_blue_noise(depth) * _flw_blueNoiseFactor; +} + #ifdef _FLW_DEPTH_RANGE layout (location = 0) out vec2 _flw_depthRange_out; @@ -155,80 +191,6 @@ float evaluate_transmittance_wavelets(in sampler2DArray coefficients, float dept #endif -// TODO: blue noise texture -uint HilbertIndex(uvec2 p) { - uint i = 0u; - for (uint l = 0x4000u; l > 0u; l >>= 1u) { - uvec2 r = min(p & l, 1u); - - i = (i << 2u) | ((r.x * 3u) ^ r.y); - p = r.y == 0u ? (0x7FFFu * r.x) ^ p.yx : p; - } - return i; -} - -uint ReverseBits(uint x) { - x = ((x & 0xaaaaaaaau) >> 1) | ((x & 0x55555555u) << 1); - x = ((x & 0xccccccccu) >> 2) | ((x & 0x33333333u) << 2); - x = ((x & 0xf0f0f0f0u) >> 4) | ((x & 0x0f0f0f0fu) << 4); - x = ((x & 0xff00ff00u) >> 8) | ((x & 0x00ff00ffu) << 8); - return (x >> 16) | (x << 16); -} - -// from: https://psychopath.io/post/2021_01_30_building_a_better_lk_hash -uint OwenHash(uint x, uint seed) { // seed is any random number - x ^= x * 0x3d20adeau; - x += seed; - x *= (seed >> 16) | 1u; - x ^= x * 0x05526c56u; - x ^= x * 0x53a22864u; - return x; -} - -// https://www.shadertoy.com/view/ssBBW1 -float blue() { - uint m = HilbertIndex(uvec2(gl_FragCoord.xy));// map pixel coords to hilbert curve index - m = OwenHash(ReverseBits(m), 0xe7843fbfu);// owen-scramble hilbert index - m = OwenHash(ReverseBits(m), 0x8d8fb1e0u);// map hilbert index to sobol sequence and owen-scramble - float mask = float(ReverseBits(m)) / 4294967296.0;// convert to float - - return mask; -} - -uniform float _flw_blueNoiseFactor = 0.08; - -float tented_blue_noise(float normalizedDepth) { - - float tentIn = abs(normalizedDepth * 2. - 1); - float tentIn2 = tentIn * tentIn; - float tentIn4 = tentIn2 * tentIn2; - float tent = 1 - (tentIn2 * tentIn4); - - float b = blue(); - - return b * tent; -} - -float linearize_depth(float d, float zNear, float zFar) { - float z_n = 2.0 * d - 1.0; - return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); -} - -float linear_depth() { - return linearize_depth(gl_FragCoord.z, _flw_cullData.znear, _flw_cullData.zfar); -} - -float depth() { - float linearDepth = linear_depth(); - - vec2 depthRange = texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).rg; - float delta = depthRange.x + depthRange.y; - float depth = (linearDepth + depthRange.x) / delta; - - return depth - tented_blue_noise(depth) * _flw_blueNoiseFactor; -} - - #else out vec4 _flw_outputColor; diff --git a/common/src/backend/resources/assets/flywheel/textures/flywheel/noise/blue/0.png b/common/src/backend/resources/assets/flywheel/textures/flywheel/noise/blue/0.png new file mode 100644 index 0000000000000000000000000000000000000000..d1920c63aab8bcee44df5a8f9f98cdf361b11a80 GIT binary patch literal 7115 zcmV;+8#LsJP)_5bKbV*nrp^yzx@_t$BvCV?zkhCTW-1N+qZA*w%cxT_St90@y8z@tF5+L9CXk@ zF@F5`n03}!6mxkdE>LsK8ySAyD$Fz_uqK#x#!~2 zOD~P7rkX0Y-FDkJ>#Vcl*I$2)C!TmBV|)Mo_haRiSB{-_+9}RBc92Tf@3-=+L40@Jut! z6sMeWN+#-0a4)|2Vr;(o=JCiQkHnpK-Wlhge}3G2^UX2E6jQ{y>#iH8pMHAWefQnD znZTD=Vu^U@p@+hnYpu0bCL*~_HrXUvYq{Zd*Ik#NU2@4KaqF$O#=iURn~4g1_uY5T z^*;an^B6Q}P#kf@5i!p^^TdW5ZWt3zIAMJI?YDXT{`>EbNhX;je){RBc=OFSW6n9} z%y`84^UptL@vJYt-+%u-_xa$14|1=szWOSrpMLs$?x>@ViuU!_UuP9Pbk$W?Wfv_r z%{0@*O*h??4=B!(OD-8bdi026jyWa+dF-*rVuu}e$UxqC=bfzje*5hgUAlD10v>zp zu^GfmFTE6}opxGmx7~JOFPks3%rf!IFTX^mPMtEjYp=aF_n36jN#o>`PmVjL9dTJerQF%qy?Fk`U_JwQF`tCtTOr2_~2z-h1!8 z`1|j_W0h4_$*$-mc&qrTtF9W)KmUB3dFGk1{r20(lTSVw^Uptjtgylgndt1Z&mO~u z4a-0dKm71a?B$nVPQ;BEF(M24`s=S}Q9&LGXPIS|`2PFv6HQAmy>vDQjkefgi}>S@ zKVr7oX3P7>U{IOm*m^14c2bkRi#r8(x9Bl*1k`s-(~g7En+ zyX=zOMGwf*sXg}CBlo~bMHD0iqL^6hB}nw^-Me>E2`%or>#jKW+;d~?wbxEAE21uf zIt|%$(@hgya)8|K-Mh!c6HgpJ{P06O{P4pu-E`CCeTN)!NPP6sN71WSuV^@;%lJlM zbP>#Sz-K$|xMOYzLeD(&Oa?>rBr@%^)25iICSkF;uGt7JKw1LVUw?f*OOWt=uDRxl z8*aEE3o~ZSm{@)F)no3t=l*}UUEgc3y|SZ69(iPT5X#&;-+c4MhaY|z#~pWEb``A_ zSYUy8>ZzwvLlp6Z6HdtWP{aP{Nn_-8%{A9#;fegh3ojfETqX6^TW>|ro;@=u)zR4n z7hEtNc;JB)k#D~FCV98)vdiW*d{UU(Zo4fzW>G;4Ku5=o8<&Zpm`)yj^wHUgd+xa> z`FG)k7iNJ4dF7Q?W>|xTQ*@#ykaxia7o{$s|3l(XE;)_o+SS|Hd==IiHFA?_FUw_36Gt7{Mr#oT>b4owiWRoSWHIrygJ@wQ% zo6rdbrgp^5Ajh+9kL;70VD7NXSS!}Wh?H*FNoqQMC;2hzf7ou zB{qc;SVF`^Fn@oCVV% z)0hbkLWmI!!C%5A4#^Nc9Yi}~N$3>Q0y4JtWK&H?xVl7vYjIB5XUB@>}Uhz;uj9l;V!qT?C%XhDe>omWr(A&Ef| zbm6Yg%@FtqDaLC$166bbx4{=e%?2l)cw#n$g%VvcKpxa64(f=N28oH~)|AR(%@)wh z@mMyLDQp8-Fa%4&R3Zn`Ho|F%s5l-A0N?2Y=uupqZSE^wW$Px)e?U zn@Pcn0MppqmPW zhOCtn*dImgM=wB{=R+e-i%F*mFCjH6KnM$`KSUFvG=zRYi~I;P7BDg;T;_2+&C6F3{hW9X2WW2QmF@FYMJBnKcocnu{KTL8`+xQnZJYT`TZyz`Py z3Z}D$Gl~s$rcU=#dWE4S&Hxg!C+cdH^_?$>D4Hpbd}s@CG{k8HfoyEaCZMW|c!7tM zl)_7hc=2xh(_J(%|Kk;r^1gt_x{uvZde18q-q}>pV2JA+2lD#&@1MhvlbbFQP_9Ez z70S8zfkTc_>e9P(13O#Xj9Z8q%28Co!|^4&|%tRPZCm40_R=@ zf&o_pkYUGoD=^ik4HT9*5=}Y+&Nk8w)0ulf({#p0ogl$fV)%{2tV+>PjC~PJf@(jF z;3i_Sre+aq0!bT8d_+{d8bXH%D8xY*VI`hy)UefzqAKVHouj0>0cI8^kbIs6aRfqS za;-QG*fn zB{~$FU+NoW^qt)k%gF$RFpm98x9f@$w>^{aR?j@NHjrk#${)3@KarZ z>M$xf&PC|HKS+(;gqOGK{9S@p6jkD zE^0wKG}9d@w|~AAwSmbA%q0i1#$rm!(sWW{cxRAMAd{L!JZl*7F2TKQ)!aaoSfNU@ zGVrq|fpcAV+^^t@EKq0!S7-xm_i-fwn-SO>rB_gN(^&%-)M%ha;kQDvbJMDrp@?>A zDvCn2Jy5af9Cf!i6~#4b4H-6~%wWxTL5)b-fJggW6?KnQN>Dd(lbY*}E(#E0YP2S( z+<JUV#> z^2{Ob0de%;MCJ^IwGRG55g4PI{lunGATB9xMF6=Jy4hEA>`8Ob5H*|;2rLvecEi3T z0_sE#p9E(V#cy0TkZ=r(sGI~X;vQZM>I$fvY>9QTLjkclZlW9P2iojOR8fz%uwcni zXdR{%V9X&Dhs$`I1U05Y!pjv5a+F>?oB+zq2Vy071}*n;Bm23Z?Q;mRGzS`Rb;M;` zlGR~VrL4SM#VD%@O?7eF*o#Zv*03$Fs~ld44UwZ2*2Z;6(lO{#BsR+KtmD|M@Cr@+ z9g55eY>vHh8AF#nsJ_7spHaj8lCU2HqlrJliMnV3MdGSA%;-$BXa&kN3Dvtj3Ry2h zbX8TsQ}@`Oz*JqpD(f?|IJ6japog_kaO$sk^n}|syB zN~9H@z0oJA!*A506A-B|IttS44_fOxM{95uR4N;V6k_b2R^O@Nc&RkbSlD$7P)i;S z&V)o~Yy`fl?Fj1#YR<-0kY#6{Ay6QRuSAZOfwDiHLp{&enceyt!i^}niIMP@yGV*l z`5ZFXu$Z}ng3>E<1r$RG6f3%E5E?ZI`osuHjlei87(56^y)(l#oibvAkwgW+ZYZj1 z3PjZ{PMyJ+lPD^kFvgnkK@PuhT`}1nuA>H2IAmFiYe1bLhky|~-Beu95Hba&F;1+I z%<67M2jv!fU(& zb)3RaaAh^D2UT>&M(*>xAOvbnz6>#D0fL2s*0&b4pov){{rZ0i&=4`v5by@%9?3|wYUW&(kO7JMaExqgJwCIO33VC$T50z z0%E7=XaXq^!Yz#6#&MdYkxg*KMb&i%;qGVrYUp9;^gZn@&IsMP{FhR!3lb zL@73Cey}z;N(4pN89k|U2YrwnMDcvh(HP?`(S;DG;6P|2p8954d>TVMp@xOZ2{jFD z)}#^kRTu-DA~qeRq9ChUDzB2(B}#$DVs>ln2TNl zGCUC=fzc80#Yaf0Z;}mRx(1Er3dm7#ZljCfPQdV+E-5ldgF22YwiwWtu0fZx4835F z#%idRSEHtQRfCUrZUX1$IIS>L)%4~lD;b>S6ogXs6^uyfC@$MrpaMiC9n(R9pogLx zr|nHk?BRYzC8+2{z0Db$nE(GQ%N&Fy8ap?zy)WJ(}6wL6{ zaL8slr3;{FT9c6A@t1%IUgf|O#00LR3aDTuX5fVu#8Y4(uIe~x;L%aT4ZbT3Q6zLe zhdO&IvQgSP)-3K4EwcfXKqO~UNX;?HqMM&)8PO}YUi{D>;rH4iuM2PL(a7Y4psT)+7CLU`%{ zYN~oV;yS2<891W}>gWU^R6u$H()0s=sj@;V7>EjlPT;cy6^lOTioJ9e#i+f4f;aBc z1@urL)FHqI9!TXDgjD=aeB1%DJWuc-gHy3n4I}zZpautXGf0Av!1*ceyGb_*6)RID zLlLwHmhG@ZG}0j=gC+uo6mZosMZ$ZXR8V>Y>QIG#+yq6?&3^8;CMu$nMnITD6HR~) z&H!=H~1V(5LFpSk?$Q_`u=@4jCl14@FaWetK^ZJ2-89`z?M|_Bh zpwYz|;91|j6RqMk&EOUVO`=6O4OvEUV&^(7K`X-*+HnS=rofQnzB;>j%VaMIaX{kl znx!FxmIV`Y1v4N)tEO7Zn6H6Q+XM;D6cbnRPhucQ^z1F1YUZFfOpUW!jxX3 zJeOGN0%vibV%ekYi<1Z(Tpgw$1KeFF!t}^GJ_l9ZBx3bW54Y(C$m=ri7nde^qogU~ zV#83)nibqJpU~4U6tPwlfJ%ADdpnw71Kv(4b-=)bzbw=IP01;~4Z%=H>I({s^Ijrl zG-g2{s<8MA8HOK)VYT(WWDO=JLC@JwX9=BihUt%%P;%5W3PY>{5m3i(&(RV>UcU#n zCrz@3y%Z91++!AKPG1y~cUrFw_V`FkRNB0)gCMQa z{;Wf9criQEB^Do1XSb;D8c-TS|RELQZu1m^nK+)Y-O3#v55y7O?0;VfqC?QZq zC#X^|`r*33>cuN$5nCO$2H|qXsNV=d5EM#MQsx1I3eB8IPGX}I3TL(;x(dbi#Rf$* zD1RzTe87T|+5}uEF7?DS0UA3wgzF9|M2X-~YMoh71FR z(H7)zl_=p9*ce|`7{uJ;k0sU5;B5ew;Lf*IUWf6`e4wa=M>hq)StLtz(Fuz9GK&IL zNRpfaqM?Fv3k9~PKei?|D3MUTZ)AHY!!=Mwfl-&n5ogpO*4At$z&KLuO}8LR9{ElXpD4CL$59l2xj-F7Ra|F5 zhJPC+xdY3>RZs*&R1yGDQWVNgThNJ`*S9@_gEo5#%8;bUc&>XAfkM~uRPl71HCkWr z1e8mGI$ePxvEw_mLM2p~4B51Gtj~HC9VNxzOi=vN282j;IRXgk1U}iUer8T1R8vwQ zMNIGxr0`l-z)*51q(cI-F+};CmOu+`E2Pgk3C&PYD3d5;@_7Rqt0caXbx(b_h&JK| zcb%>OP^m9%6h?84zR-z|Vn98ESAI2CzssYh&gB$cbu9+6;OaQ4vpVP?16p223P6$s zOfe;aKJ_9*7j@Ol&%4>H!wJ+VkRT;T@u5bN8Ul^bhrfm_GY2|?u=54VQGWFCtTiM_ zAe|^Rw7waFCZj&8&@fGdLJep(v7u#MRI9 zt&i`%r*J<#(=eLS>eV+@1vLjConl!mM8LS^h>KV33cOedjv7l%VP<`<;F!u;2vxo3 zLm_ZuO}I}VKoLTGZXX>&KY>D$BI2tx6_3D~8#KeZh9~1Knkb~^L5jW20}$hLMT9U~ zLlo(hNl_yeu;Czk!6zNoMHS?)TmWng4~m03^$RP=ai5J1K`e|55JuKWLwLBLbos~I_rcCgN`J5uny`5p>ZF+3Jz53zdAsaAhJLl zCZ2dotaL=6)ExTE96D=1Gy!o=!ik(&*e+D@dV?2On=mB?S&D3|cFjHXg`jFQTT~p# zZ5HR%APyQ%X)0{ScAc;g3|2)eoS=x1#RRCcKEpLiO~Dn^s0umIAxYMRUhczV;$%AD|58EK#v(ku~aP_7buW@gu^{CTL6yOjdV{qN8(UGuSZo7DRAtcPF!i-H7z-BM6}5huUcWGL zpY9Sk$m2@jPB)-TQWTx96Jg>DfdtqTt6>u5P;>?zi69-Zm-T6cdpQstLFZPm)I~#% z$~w%j5;y8JKC3=|r=%cT|3^Svz?nz$e4krGcNL82(3yJ0W?<3=O( z3h~w^7IBO%zZZx=sXA(aBfY3!a?u&<8j5sLu;_uqbV0EVKK8dBC|gG|Y?_|XB?uvE zG=*U5WPK~FiPk32VpDu0x1vH`{Th`{)Q_tO?|*0z+Da`c$VvbJ002ovPDHLkV1haH B=@9?` literal 0 HcmV?d00001 diff --git a/fabric/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java b/fabric/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java index 1422ac19c..709e72465 100644 --- a/fabric/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java +++ b/fabric/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java @@ -1,6 +1,7 @@ package dev.engine_room.flywheel.backend.compile; import dev.engine_room.flywheel.api.Flywheel; +import dev.engine_room.flywheel.backend.NoiseTextures; import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceManager; @@ -16,6 +17,7 @@ public final class FlwProgramsReloader implements SimpleSynchronousResourceReloa @Override public void onResourceManagerReload(ResourceManager manager) { FlwPrograms.reload(manager); + NoiseTextures.reload(manager); } @Override diff --git a/forge/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java b/forge/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java index ada440f24..e3350f108 100644 --- a/forge/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java +++ b/forge/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java @@ -1,5 +1,6 @@ package dev.engine_room.flywheel.backend.compile; +import dev.engine_room.flywheel.backend.NoiseTextures; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceManagerReloadListener; @@ -12,5 +13,6 @@ public final class FlwProgramsReloader implements ResourceManagerReloadListener @Override public void onResourceManagerReload(ResourceManager manager) { FlwPrograms.reload(manager); + NoiseTextures.reload(manager); } } From fe55693cbb488e05e88b20018a0a5eb96c8f26b9 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 22 Feb 2025 18:43:30 -0800 Subject: [PATCH 11/23] Cleanups and depth - Centralized wavelet code and roughly document - Add a full screen pass to render the depth at which transmittance falls to zero --- .../backend/compile/IndirectPrograms.java | 6 + .../engine/indirect/IndirectDrawManager.java | 4 +- .../engine/indirect/OitFramebuffer.java | 48 ++++- .../flywheel/flywheel/internal/common.frag | 122 +----------- .../flywheel/flywheel/internal/depth.glsl | 9 + .../internal/indirect/oit_composite.frag | 65 +----- .../flywheel/internal/indirect/oit_depth.frag | 56 ++++++ .../flywheel/flywheel/internal/wavelet.glsl | 186 ++++++++++++++++++ 8 files changed, 307 insertions(+), 189 deletions(-) create mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/depth.glsl create mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_depth.frag create mode 100644 common/src/backend/resources/assets/flywheel/flywheel/internal/wavelet.glsl diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java index cc985b278..d69c8e2e0 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java @@ -34,6 +34,7 @@ public class IndirectPrograms extends AtomicReferenceCounted { private static final ResourceLocation FULLSCREEN = Flywheel.rl("internal/indirect/fullscreen.vert"); private static final ResourceLocation OIT_COMPOSITE = Flywheel.rl("internal/indirect/oit_composite.frag"); + private static final ResourceLocation OIT_DEPTH = Flywheel.rl("internal/indirect/oit_depth.frag"); private static final Compile> CULL = new Compile<>(); private static final Compile UTIL = new Compile<>(); @@ -138,6 +139,7 @@ public class IndirectPrograms extends AtomicReferenceCounted { .link(UTIL.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.FRAGMENT) .nameMapper(rl -> "fullscreen/" + ResourceUtil.toDebugFileNameNoExtension(rl)) .withResource(s -> s)) + .postLink((key, program) -> Uniforms.setUniformBlockBindings(program)) .harness("fullscreen", sources); } @@ -192,6 +194,10 @@ public class IndirectPrograms extends AtomicReferenceCounted { return fullscreen.get(OIT_COMPOSITE); } + public GlProgram getOitDepthProgram() { + return fullscreen.get(OIT_DEPTH); + } + @Override protected void _delete() { pipeline.delete(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index 09b5b3201..4587cbdad 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -158,9 +158,9 @@ public class IndirectDrawManager extends DrawManager> { group.submitTransparent(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS); } - // wboitFrameBuffer.adjustBackgroundForTotalTransmittance(); + wboitFrameBuffer.renderDepth(); - // vertexArray.bindForDraw(); + vertexArray.bindForDraw(); wboitFrameBuffer.shade(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java index 23260f106..281e3e22b 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java @@ -14,13 +14,13 @@ import net.minecraft.client.Minecraft; public class OitFramebuffer { - public final int fbo; private final IndirectPrograms programs; private final int vao; - public int depthBounds; - public int coefficients; - public int accumulate; + public int fbo = -1; + public int depthBounds = -1; + public int coefficients = -1; + public int accumulate = -1; private int lastWidth = -1; private int lastHeight = -1; @@ -39,6 +39,7 @@ public class OitFramebuffer { // No depth writes, but we'll still use the depth test RenderSystem.depthMask(false); + RenderSystem.colorMask(true, true, true, true); RenderSystem.enableBlend(); RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); RenderSystem.blendEquation(GL46.GL_MAX); @@ -57,6 +58,7 @@ public class OitFramebuffer { public void renderTransmittance() { // No depth writes, but we'll still use the depth test RenderSystem.depthMask(false); + RenderSystem.colorMask(true, true, true, true); RenderSystem.enableBlend(); RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); RenderSystem.blendEquation(GL46.GL_FUNC_ADD); @@ -85,6 +87,7 @@ public class OitFramebuffer { public void shade() { // No depth writes, but we'll still use the depth test RenderSystem.depthMask(false); + RenderSystem.colorMask(true, true, true, true); RenderSystem.enableBlend(); RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); RenderSystem.blendEquation(GL46.GL_FUNC_ADD); @@ -106,9 +109,34 @@ public class OitFramebuffer { GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); } + public void renderDepth() { + // No depth writes, but we'll still use the depth test + RenderSystem.depthMask(true); + RenderSystem.colorMask(false, false, false, false); + RenderSystem.disableBlend(); + + Samplers.COEFFICIENTS.makeActive(); + GlStateManager._bindTexture(0); + GL46.glBindTextureUnit(0, coefficients); + + Samplers.DEPTH_RANGE.makeActive(); + GlStateManager._bindTexture(depthBounds); + + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{}); + + programs.getOitDepthProgram() + .bind(); + + // Empty VAO, the actual full screen triangle is generated in the vertex shader + GlStateManager._glBindVertexArray(vao); + + GL46.glDrawArrays(GL46.GL_TRIANGLES, 0, 3); + } + public void composite() { // No depth writes, but we'll still use the depth test RenderSystem.depthMask(false); + RenderSystem.colorMask(true, true, true, true); RenderSystem.enableBlend(); RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.DestFactor.SRC_ALPHA); RenderSystem.blendEquation(GL46.GL_FUNC_ADD); @@ -141,9 +169,15 @@ public class OitFramebuffer { } private void deleteTextures() { - GL46.glDeleteTextures(depthBounds); - GL46.glDeleteTextures(coefficients); - GL46.glDeleteTextures(accumulate); + if (depthBounds != -1) { + GL46.glDeleteTextures(depthBounds); + } + if (coefficients != -1) { + GL46.glDeleteTextures(coefficients); + } + if (accumulate != -1) { + GL46.glDeleteTextures(accumulate); + } } private void createTextures(int width, int height) { diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag index fe476a9c5..aee865829 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag @@ -1,6 +1,8 @@ #include "flywheel:internal/packed_material.glsl" #include "flywheel:internal/diffuse.glsl" #include "flywheel:internal/colorizer.glsl" +#include "flywheel:internal/wavelet.glsl" +#include "flywheel:internal/depth.glsl" // optimize discard usage #if defined(GL_ARB_conservative_depth) && defined(_FLW_USE_DISCARD) @@ -19,10 +21,6 @@ flat in uvec2 _flw_ids; #ifdef _FLW_OIT -#define TRANSPARENCY_WAVELET_RANK 3 -#define TRANSPARENCY_WAVELET_COEFFICIENT_COUNT 16 -#define REMOVE_SIGNAL true - layout (binding = 7) uniform sampler2D _flw_depthRange; layout (binding = 8) uniform sampler2DArray _flw_coefficients; @@ -44,11 +42,6 @@ float tented_blue_noise(float normalizedDepth) { return b * tent; } -float linearize_depth(float d, float zNear, float zFar) { - float z_n = 2.0 * d - 1.0; - return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); -} - float linear_depth() { return linearize_depth(gl_FragCoord.z, _flw_cullData.znear, _flw_cullData.zfar); } @@ -69,126 +62,19 @@ layout (location = 0) out vec2 _flw_depthRange_out; #endif - #ifdef _FLW_COLLECT_COEFFS - layout (location = 0) out vec4 _flw_coeffs0; layout (location = 1) out vec4 _flw_coeffs1; layout (location = 2) out vec4 _flw_coeffs2; layout (location = 3) out vec4 _flw_coeffs3; -void add_to_index(inout vec4[4] coefficients, uint index, float addend) { - coefficients[index >> 2][index & 3u] = addend; -} - -void add_event_to_wavelets(inout vec4[4] coefficients, float signal, float depth) -{ - depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; - - int index = clamp(int(floor(depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); - index += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; - - for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) - { - int power = TRANSPARENCY_WAVELET_RANK - i; - int new_index = (index - 1) >> 1; - float k = float((new_index + 1) & ((1 << power) - 1)); - - int wavelet_sign = ((index & 1) << 1) - 1; - float wavelet_phase = ((index + 1) & 1) * exp2(-power); - float addend = fma(fma(-exp2(-power), k, depth), wavelet_sign, wavelet_phase) * exp2(power * 0.5) * signal; - add_to_index(coefficients, new_index, addend); - - index = new_index; - } - - float addend = fma(signal, -depth, signal); - add_to_index(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1, addend); -} - -void add_transmittance_event_to_wavelets(inout vec4[4] coefficients, float transmittance, float depth) -{ - float absorbance = -log(max(transmittance, 0.00001));// transforming the signal from multiplicative transmittance to additive absorbance - add_event_to_wavelets(coefficients, absorbance, depth); -} - #endif #ifdef _FLW_EVALUATE layout (location = 0) out vec4 _flw_accumulate; - -float get_coefficients(in sampler2DArray coefficients, uint index) { - return texelFetch(coefficients, ivec3(gl_FragCoord.xy, index >> 2), 0)[index & 3u]; -} - -float evaluate_wavelets(in sampler2DArray coefficients, float depth, float signal) -{ - float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); - if (scale_coefficient == 0) - { - return 0; - } - - depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; - - if (REMOVE_SIGNAL) - { - float scale_coefficient_addend = fma(signal, -depth, signal); - scale_coefficient -= scale_coefficient_addend; - } - - float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; - int index_b = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); - bool sample_a = index_b >= 1; - int index_a = sample_a ? (index_b - 1) : index_b; - - index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; - index_a += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; - - float b = scale_coefficient; - float a = sample_a ? scale_coefficient : 0; - - for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) - { - int power = TRANSPARENCY_WAVELET_RANK - i; - - int new_index_b = (index_b - 1) >> 1; - int wavelet_sign_b = ((index_b & 1) << 1) - 1; - float coeff_b = get_coefficients(coefficients, new_index_b); - if (REMOVE_SIGNAL) - { - float wavelet_phase_b = ((index_b + 1) & 1) * exp2(-power); - float k = float((new_index_b + 1) & ((1 << power) - 1)); - float addend = fma(fma(-exp2(-power), k, depth), wavelet_sign_b, wavelet_phase_b) * exp2(power * 0.5) * signal; - coeff_b -= addend; - } - b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b; - index_b = new_index_b; - - if (sample_a) - { - int new_index_a = (index_a - 1) >> 1; - int wavelet_sign_a = ((index_a & 1) << 1) - 1; - float coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a);// No addend here on purpose, the original signal didn't contribute to this coefficient - a -= exp2(float(power) * 0.5) * coeff_a * wavelet_sign_a; - index_a = new_index_a; - } - } - - float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth); - - return mix(a, b, t); -} - -float evaluate_transmittance_wavelets(in sampler2DArray coefficients, float depth, float signal) -{ - float absorbance = evaluate_wavelets(coefficients, depth, signal); - return clamp(exp(-absorbance), 0., 1.);// undoing the transformation from absorbance back to transmittance -} - #endif #else @@ -298,7 +184,7 @@ void _flw_main() { result[2] = vec4(0.); result[3] = vec4(0.); - add_transmittance_event_to_wavelets(result, 1. - color.a, depth()); + add_transmittance(result, 1. - color.a, depth()); _flw_coeffs0 = result[0]; _flw_coeffs1 = result[1]; @@ -309,7 +195,7 @@ void _flw_main() { #ifdef _FLW_EVALUATE - float transmittance = evaluate_transmittance_wavelets(_flw_coefficients, depth(), 1. - color.a); + float transmittance = signal_corrected_transmittance(_flw_coefficients, depth(), 1. - color.a); _flw_accumulate = vec4(color.rgb * color.a, color.a) * transmittance; diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/depth.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/depth.glsl new file mode 100644 index 000000000..7a63c4fba --- /dev/null +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/depth.glsl @@ -0,0 +1,9 @@ +float linearize_depth(float d, float zNear, float zFar) { + float z_n = 2.0 * d - 1.0; + return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); +} + +float delinearize_depth(float linearDepth, float zNear, float zFar) { + float z_n = (2.0 * zNear * zFar / linearDepth) - (zFar + zNear); + return 0.5 * (z_n / (zNear - zFar) + 1.0); +} diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag index b09ed5acd..733e2e5ae 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag @@ -1,69 +1,10 @@ +#include "flywheel:internal/wavelet.glsl" + layout (location = 0) out vec4 frag; layout (binding = 0) uniform sampler2DArray _flw_coefficients; layout (binding = 1) uniform sampler2D _flw_accumulate; -#define TRANSPARENCY_WAVELET_RANK 3 -#define TRANSPARENCY_WAVELET_COEFFICIENT_COUNT 16 - -float get_coefficients(in sampler2DArray coefficients, uint index) { - return texelFetch(coefficients, ivec3(gl_FragCoord.xy, index >> 2), 0)[index & 3u]; -} - -float evaluate_wavelets(in sampler2DArray coefficients, float depth) -{ - float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); - if (scale_coefficient == 0) - { - return 0; - } - - depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; - - float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; - int index_b = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); - bool sample_a = index_b >= 1; - int index_a = sample_a ? (index_b - 1) : index_b; - - index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; - index_a += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; - - float b = scale_coefficient; - float a = sample_a ? scale_coefficient : 0; - - for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) - { - int power = TRANSPARENCY_WAVELET_RANK - i; - - int new_index_b = (index_b - 1) >> 1; - int wavelet_sign_b = ((index_b & 1) << 1) - 1; - float coeff_b = get_coefficients(coefficients, new_index_b); - b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b; - index_b = new_index_b; - - if (sample_a) - { - int new_index_a = (index_a - 1) >> 1; - int wavelet_sign_a = ((index_a & 1) << 1) - 1; - float coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a); - a -= exp2(float(power) * 0.5) * coeff_a * wavelet_sign_a; - index_a = new_index_a; - } - } - - float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth); - - return mix(a, b, t); -} - -float evaluate_transmittance_wavelets(in sampler2DArray coefficients, float depth) -{ - float absorbance = evaluate_wavelets(coefficients, depth); - return clamp(exp(-absorbance), 0., 1.);// undoing the transformation from absorbance back to transmittance -} - -const float infinity = 1. / 0.; - void main() { vec4 texel = texelFetch(_flw_accumulate, ivec2(gl_FragCoord.xy), 0); @@ -71,7 +12,7 @@ void main() { discard; } - float total_transmittance = evaluate_transmittance_wavelets(_flw_coefficients, infinity); + float total_transmittance = total_transmittance(_flw_coefficients); frag = vec4(texel.rgb / texel.a, total_transmittance); } diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_depth.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_depth.frag new file mode 100644 index 000000000..1f127b445 --- /dev/null +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_depth.frag @@ -0,0 +1,56 @@ +#include "flywheel:internal/uniforms/frame.glsl" +#include "flywheel:internal/wavelet.glsl" +#include "flywheel:internal/depth.glsl" + +layout (binding = 7) uniform sampler2D _flw_depthRange; + +layout (binding = 8) uniform sampler2DArray _flw_coefficients; + +float eye_depth_from_normalized_transparency_depth(float tDepth) { + vec2 depthRange = texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).rg; + + float delta = depthRange.x + depthRange.y; + + return tDepth * delta - depthRange.x; +} + +void main() { + float threshold = 0.0001; + + // + // If transmittance an infinite depth is above the threshold, it doesn't ever become + // zero, so we can bail out. + // + float transmittance_at_far_depth = total_transmittance(_flw_coefficients); + if (transmittance_at_far_depth > threshold) { + discard; + } + + float normalized_depth_at_zero_transmittance = 1.0; + float sample_depth = 0.5; + float delta = 0.25; + + // + // Quick & Dirty way to binary search through the transmittance function + // looking for a value that's below the threshold. + // + int steps = 6; + for (int i = 0; i < steps; ++i) { + float transmittance = transmittance(_flw_coefficients, sample_depth); + if (transmittance <= threshold) { + normalized_depth_at_zero_transmittance = sample_depth; + sample_depth -= delta; + } else { + sample_depth += delta; + } + delta *= 0.5; + } + + // + // Searching inside the transparency depth bounds, so have to transform that to + // a world-space linear-depth and that into a device depth we can output into + // the currently bound depth buffer. + // + float eyeDepth = eye_depth_from_normalized_transparency_depth(normalized_depth_at_zero_transmittance); + gl_FragDepth = delinearize_depth(eyeDepth, _flw_cullData.znear, _flw_cullData.zfar); +} diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/wavelet.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/wavelet.glsl new file mode 100644 index 000000000..6691e3d16 --- /dev/null +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/wavelet.glsl @@ -0,0 +1,186 @@ +#define TRANSPARENCY_WAVELET_RANK 3 +#define TRANSPARENCY_WAVELET_COEFFICIENT_COUNT 16 + + +// ------------------------------------------------------------------------- +// WRITING +// ------------------------------------------------------------------------- + +void add_to_index(inout vec4[4] coefficients, uint index, float addend) { + coefficients[index >> 2][index & 3u] = addend; +} + +void add_absorbance(inout vec4[4] coefficients, float signal, float depth) { + depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; + + int index = clamp(int(floor(depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + index += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + + for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) { + int power = TRANSPARENCY_WAVELET_RANK - i; + int new_index = (index - 1) >> 1; + float k = float((new_index + 1) & ((1 << power) - 1)); + + int wavelet_sign = ((index & 1) << 1) - 1; + float wavelet_phase = ((index + 1) & 1) * exp2(-power); + float addend = fma(fma(-exp2(-power), k, depth), wavelet_sign, wavelet_phase) * exp2(power * 0.5) * signal; + add_to_index(coefficients, new_index, addend); + + index = new_index; + } + + float addend = fma(signal, -depth, signal); + add_to_index(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1, addend); +} + +void add_transmittance(inout vec4[4] coefficients, float transmittance, float depth) { + float absorbance = -log(max(transmittance, 0.00001));// transforming the signal from multiplicative transmittance to additive absorbance + add_absorbance(coefficients, absorbance, depth); +} + +// ------------------------------------------------------------------------- +// READING +// ------------------------------------------------------------------------- + +// TODO: maybe we could reduce the number of texel fetches below? +float get_coefficients(in sampler2DArray coefficients, uint index) { + return texelFetch(coefficients, ivec3(gl_FragCoord.xy, index >> 2), 0)[index & 3u]; +} + +/// Compute the total absorbance, as if at infinite depth. +float total_absorbance(in sampler2DArray coefficients) { + float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + if (scale_coefficient == 0) { + return 0; + } + + int index_b = TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + + index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + + float b = scale_coefficient; + + for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) { + int power = TRANSPARENCY_WAVELET_RANK - i; + + int new_index_b = (index_b - 1) >> 1; + int wavelet_sign_b = ((index_b & 1) << 1) - 1; + float coeff_b = get_coefficients(coefficients, new_index_b); + b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b; + index_b = new_index_b; + } + + return b; +} + +/// Compute the absorbance at a given normalized depth. +float absorbance(in sampler2DArray coefficients, float depth) { + float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + if (scale_coefficient == 0) { + return 0; + } + + depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; + + float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; + int index_b = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + bool sample_a = index_b >= 1; + int index_a = sample_a ? (index_b - 1) : index_b; + + index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + index_a += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + + float b = scale_coefficient; + float a = sample_a ? scale_coefficient : 0; + + for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) { + int power = TRANSPARENCY_WAVELET_RANK - i; + + int new_index_b = (index_b - 1) >> 1; + int wavelet_sign_b = ((index_b & 1) << 1) - 1; + float coeff_b = get_coefficients(coefficients, new_index_b); + b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b; + index_b = new_index_b; + + if (sample_a) { + int new_index_a = (index_a - 1) >> 1; + int wavelet_sign_a = ((index_a & 1) << 1) - 1; + float coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a); + a -= exp2(float(power) * 0.5) * coeff_a * wavelet_sign_a; + index_a = new_index_a; + } + } + + float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth); + + return mix(a, b, t); +} + +/// Compute the absorbance at a given normalized depth, +/// correcting for self-occlusion by undoing the previously recorded absorbance event. +float signal_corrected_absorbance(in sampler2DArray coefficients, float depth, float signal) { + float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + if (scale_coefficient == 0) { + return 0; + } + + depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; + + float scale_coefficient_addend = fma(signal, -depth, signal); + scale_coefficient -= scale_coefficient_addend; + + float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT; + int index_b = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1); + bool sample_a = index_b >= 1; + int index_a = sample_a ? (index_b - 1) : index_b; + + index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + index_a += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1; + + float b = scale_coefficient; + float a = sample_a ? scale_coefficient : 0; + + for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) { + int power = TRANSPARENCY_WAVELET_RANK - i; + + int new_index_b = (index_b - 1) >> 1; + int wavelet_sign_b = ((index_b & 1) << 1) - 1; + float coeff_b = get_coefficients(coefficients, new_index_b); + + float wavelet_phase_b = ((index_b + 1) & 1) * exp2(-power); + float k = float((new_index_b + 1) & ((1 << power) - 1)); + float addend = fma(fma(-exp2(-power), k, depth), wavelet_sign_b, wavelet_phase_b) * exp2(power * 0.5) * signal; + coeff_b -= addend; + + b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b; + index_b = new_index_b; + + if (sample_a) { + int new_index_a = (index_a - 1) >> 1; + int wavelet_sign_a = ((index_a & 1) << 1) - 1; + float coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a);// No addend here on purpose, the original signal didn't contribute to this coefficient + a -= exp2(float(power) * 0.5) * coeff_a * wavelet_sign_a; + index_a = new_index_a; + } + } + + float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth); + + return mix(a, b, t); +} + +// Helpers below to deal directly in transmittance. + +#define ABSORBANCE_TO_TRANSMITTANCE(a) clamp(exp(-(a)), 0., 1.) + +float total_transmittance(in sampler2DArray coefficients) { + return ABSORBANCE_TO_TRANSMITTANCE(total_absorbance(coefficients)); +} + +float transmittance(in sampler2DArray coefficients, float depth) { + return ABSORBANCE_TO_TRANSMITTANCE(absorbance(coefficients, depth)); +} + +float signal_corrected_transmittance(in sampler2DArray coefficients, float depth, float signal) { + return ABSORBANCE_TO_TRANSMITTANCE(signal_corrected_absorbance(coefficients, depth, signal)); +} From 19c97df115a1b8b0dbc490c1d5fc375d11f7b0ef Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 22 Feb 2025 21:15:37 -0800 Subject: [PATCH 12/23] A win for window sizes - Fix transparent objects disappearing when the window is resized - Clean up OitFramebuffer some more, add a quick description of each render pass - Move oit noise factor to frame uniforms --- .../flywheel/backend/NoiseTextures.java | 17 ++- .../engine/indirect/IndirectDrawManager.java | 17 +-- .../engine/indirect/OitFramebuffer.java | 113 +++++++++++------- .../backend/engine/uniform/FrameUniforms.java | 3 + .../flywheel/flywheel/internal/common.frag | 5 +- .../flywheel/internal/uniforms/frame.glsl | 2 + .../flywheel/noise/{blue/0.png => blue.png} | Bin 7 files changed, 98 insertions(+), 59 deletions(-) rename common/src/backend/resources/assets/flywheel/textures/flywheel/noise/{blue/0.png => blue.png} (100%) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/NoiseTextures.java b/common/src/backend/java/dev/engine_room/flywheel/backend/NoiseTextures.java index 157a64ded..1fcbf99d3 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/NoiseTextures.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/NoiseTextures.java @@ -3,23 +3,23 @@ package dev.engine_room.flywheel.backend; import java.io.IOException; import org.jetbrains.annotations.UnknownNullability; +import org.lwjgl.opengl.GL32; import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.blaze3d.systems.RenderSystem; import dev.engine_room.flywheel.api.Flywheel; +import dev.engine_room.flywheel.backend.gl.GlTextureUnit; import net.minecraft.client.renderer.texture.DynamicTexture; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceManager; public class NoiseTextures { - - public static final ResourceLocation NOISE_TEXTURE = Flywheel.rl("textures/flywheel/noise/blue/0.png"); - public static final int NOISE_LAYERS = 16; + public static final ResourceLocation NOISE_TEXTURE = Flywheel.rl("textures/flywheel/noise/blue.png"); @UnknownNullability public static DynamicTexture BLUE_NOISE; - public static void reload(ResourceManager manager) { if (BLUE_NOISE != null) { BLUE_NOISE.close(); @@ -36,6 +36,15 @@ public class NoiseTextures { var image = NativeImage.read(NativeImage.Format.LUMINANCE, is); BLUE_NOISE = new DynamicTexture(image); + + GlTextureUnit.T0.makeActive(); + BLUE_NOISE.bind(); + + NoiseTextures.BLUE_NOISE.setFilter(true, false); + RenderSystem.texParameter(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_S, GL32.GL_REPEAT); + RenderSystem.texParameter(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_T, GL32.GL_REPEAT); + + RenderSystem.bindTexture(0); } catch (IOException e) { } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index 4587cbdad..ea77372da 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -49,7 +49,7 @@ public class IndirectDrawManager extends DrawManager> { private final DepthPyramid depthPyramid; - private final OitFramebuffer wboitFrameBuffer; + private final OitFramebuffer oitFramebuffer; public IndirectDrawManager(IndirectPrograms programs) { this.programs = programs; @@ -66,7 +66,7 @@ public class IndirectDrawManager extends DrawManager> { depthPyramid = new DepthPyramid(programs); - wboitFrameBuffer = new OitFramebuffer(programs); + oitFramebuffer = new OitFramebuffer(programs); } @Override @@ -146,29 +146,32 @@ public class IndirectDrawManager extends DrawManager> { group.submitSolid(); } - wboitFrameBuffer.depthRange(); + oitFramebuffer.prepare(); + + oitFramebuffer.depthRange(); for (var group : cullingGroups.values()) { group.submitTransparent(PipelineCompiler.OitMode.DEPTH_RANGE); } - wboitFrameBuffer.renderTransmittance(); + oitFramebuffer.renderTransmittance(); for (var group : cullingGroups.values()) { group.submitTransparent(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS); } - wboitFrameBuffer.renderDepth(); + oitFramebuffer.renderDepth(); + // Need to bind this again because we just drew a full screen quad for OIT. vertexArray.bindForDraw(); - wboitFrameBuffer.shade(); + oitFramebuffer.shade(); for (var group : cullingGroups.values()) { group.submitTransparent(PipelineCompiler.OitMode.EVALUATE); } - wboitFrameBuffer.composite(); + oitFramebuffer.composite(); MaterialRenderState.reset(); TextureBinder.resetLightAndOverlay(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java index 281e3e22b..6ebf3e30c 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java @@ -13,7 +13,6 @@ import dev.engine_room.flywheel.backend.gl.GlTextureUnit; import net.minecraft.client.Minecraft; public class OitFramebuffer { - private final IndirectPrograms programs; private final int vao; @@ -27,16 +26,25 @@ public class OitFramebuffer { public OitFramebuffer(IndirectPrograms programs) { this.programs = programs; - fbo = GL46.glCreateFramebuffers(); vao = GL46.glCreateVertexArrays(); } - public void depthRange() { + /** + * Set up the framebuffer. + */ + public void prepare() { var mainRenderTarget = Minecraft.getInstance() .getMainRenderTarget(); - createTextures(mainRenderTarget.width, mainRenderTarget.height); + maybeResizeFBO(mainRenderTarget.width, mainRenderTarget.height); + GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, mainRenderTarget.getDepthTextureId(), 0); + } + + /** + * Render out the min and max depth per fragment. + */ + public void depthRange() { // No depth writes, but we'll still use the depth test RenderSystem.depthMask(false); RenderSystem.colorMask(true, true, true, true); @@ -44,8 +52,6 @@ public class OitFramebuffer { RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); RenderSystem.blendEquation(GL46.GL_MAX); - GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, mainRenderTarget.getDepthTextureId(), 0); - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT0}); var far = Minecraft.getInstance().gameRenderer.getDepthFar(); @@ -55,6 +61,9 @@ public class OitFramebuffer { GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); } + /** + * Generate the coefficients to the transmittance function. + */ public void renderTransmittance() { // No depth writes, but we'll still use the depth test RenderSystem.depthMask(false); @@ -69,11 +78,6 @@ public class OitFramebuffer { Samplers.NOISE.makeActive(); NoiseTextures.BLUE_NOISE.bind(); - NoiseTextures.BLUE_NOISE.setFilter(true, false); - GL46.glTextureParameteri(NoiseTextures.BLUE_NOISE.getId(), GL32.GL_TEXTURE_WRAP_S, GL32.GL_REPEAT); - GL46.glTextureParameteri(NoiseTextures.BLUE_NOISE.getId(), GL32.GL_TEXTURE_WRAP_T, GL32.GL_REPEAT); - - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT1, GL46.GL_COLOR_ATTACHMENT2, GL46.GL_COLOR_ATTACHMENT3, GL46.GL_COLOR_ATTACHMENT4}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); @@ -84,6 +88,34 @@ public class OitFramebuffer { GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); } + /** + * If any fragment has its transmittance fall off to zero, search the transmittance + * function to determine at what depth that occurs and write out to the depth buffer. + */ + public void renderDepth() { + // No depth writes, but we'll still use the depth test + RenderSystem.depthMask(true); + RenderSystem.colorMask(false, false, false, false); + RenderSystem.disableBlend(); + + Samplers.COEFFICIENTS.makeActive(); + RenderSystem.bindTexture(0); + GL46.glBindTextureUnit(0, coefficients); + + Samplers.DEPTH_RANGE.makeActive(); + RenderSystem.bindTexture(depthBounds); + + GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{}); + + programs.getOitDepthProgram() + .bind(); + + drawFullscreenQuad(); + } + + /** + * Sample the transmittance function and accumulate. + */ public void shade() { // No depth writes, but we'll still use the depth test RenderSystem.depthMask(false); @@ -109,30 +141,9 @@ public class OitFramebuffer { GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); } - public void renderDepth() { - // No depth writes, but we'll still use the depth test - RenderSystem.depthMask(true); - RenderSystem.colorMask(false, false, false, false); - RenderSystem.disableBlend(); - - Samplers.COEFFICIENTS.makeActive(); - GlStateManager._bindTexture(0); - GL46.glBindTextureUnit(0, coefficients); - - Samplers.DEPTH_RANGE.makeActive(); - GlStateManager._bindTexture(depthBounds); - - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{}); - - programs.getOitDepthProgram() - .bind(); - - // Empty VAO, the actual full screen triangle is generated in the vertex shader - GlStateManager._glBindVertexArray(vao); - - GL46.glDrawArrays(GL46.GL_TRIANGLES, 0, 3); - } - + /** + * Composite the accumulated luminance onto the main framebuffer. + */ public void composite() { // No depth writes, but we'll still use the depth test RenderSystem.depthMask(false); @@ -147,27 +158,30 @@ public class OitFramebuffer { mainRenderTarget.bindWrite(false); GlTextureUnit.T0.makeActive(); - GlStateManager._bindTexture(0); + RenderSystem.bindTexture(0); GL46.glBindTextureUnit(0, coefficients); GlTextureUnit.T1.makeActive(); - GlStateManager._bindTexture(accumulate); + RenderSystem.bindTexture(accumulate); programs.getOitCompositeProgram() .bind(); - // Empty VAO, the actual full screen triangle is generated in the vertex shader - GlStateManager._glBindVertexArray(vao); - - GL46.glDrawArrays(GL46.GL_TRIANGLES, 0, 3); + drawFullscreenQuad(); } public void delete() { deleteTextures(); - GL46.glDeleteFramebuffers(fbo); GL46.glDeleteVertexArrays(vao); } + private void drawFullscreenQuad() { + // Empty VAO, the actual full screen triangle is generated in the vertex shader + GlStateManager._glBindVertexArray(vao); + + GL46.glDrawArrays(GL46.GL_TRIANGLES, 0, 3); + } + private void deleteTextures() { if (depthBounds != -1) { GL46.glDeleteTextures(depthBounds); @@ -178,9 +192,19 @@ public class OitFramebuffer { if (accumulate != -1) { GL46.glDeleteTextures(accumulate); } + if (fbo != -1) { + GL46.glDeleteFramebuffers(fbo); + } + + // We sometimes get the same texture ID back when creating new textures, + // so bind zero to clear the GlStateManager + Samplers.COEFFICIENTS.makeActive(); + RenderSystem.bindTexture(0); + Samplers.DEPTH_RANGE.makeActive(); + RenderSystem.bindTexture(0); } - private void createTextures(int width, int height) { + private void maybeResizeFBO(int width, int height) { if (lastWidth == width && lastHeight == height) { return; } @@ -190,13 +214,14 @@ public class OitFramebuffer { deleteTextures(); + fbo = GL46.glCreateFramebuffers(); + depthBounds = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); coefficients = GL46.glCreateTextures(GL46.GL_TEXTURE_2D_ARRAY); accumulate = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); GL46.glTextureStorage2D(depthBounds, 1, GL32.GL_RG32F, width, height); GL46.glTextureStorage3D(coefficients, 1, GL32.GL_RGBA16F, width, height, 4); - GL46.glTextureStorage2D(accumulate, 1, GL32.GL_RGBA16F, width, height); // for (int tex : new int[]{zerothMoment, moments, composite}) { diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java index 07e8a9e5c..ce2fd4c6f 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java @@ -116,6 +116,9 @@ public final class FrameUniforms extends UniformWriter { ptr = writeInt(ptr, debugMode); + // OIT noise factor + ptr = writeFloat(ptr, 0.07f); + firstWrite = false; BUFFER.markDirty(); } diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag index aee865829..2693798a0 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag @@ -27,9 +27,6 @@ layout (binding = 8) uniform sampler2DArray _flw_coefficients; layout (binding = 9) uniform sampler2D _flw_blueNoise; - -uniform float _flw_blueNoiseFactor = 0.08; - float tented_blue_noise(float normalizedDepth) { float tentIn = abs(normalizedDepth * 2. - 1); @@ -53,7 +50,7 @@ float depth() { float delta = depthRange.x + depthRange.y; float depth = (linearDepth + depthRange.x) / delta; - return depth - tented_blue_noise(depth) * _flw_blueNoiseFactor; + return depth - tented_blue_noise(depth) * _flw_oitNoise; } #ifdef _FLW_DEPTH_RANGE diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/uniforms/frame.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/uniforms/frame.glsl index f96f502fc..90c5f3f21 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/uniforms/frame.glsl +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/uniforms/frame.glsl @@ -62,6 +62,8 @@ layout(std140) uniform _FlwFrameUniforms { uint flw_cameraInBlock; uint _flw_debugMode; + + float _flw_oitNoise; }; #define flw_renderOrigin (_flw_renderOrigin.xyz) diff --git a/common/src/backend/resources/assets/flywheel/textures/flywheel/noise/blue/0.png b/common/src/backend/resources/assets/flywheel/textures/flywheel/noise/blue.png similarity index 100% rename from common/src/backend/resources/assets/flywheel/textures/flywheel/noise/blue/0.png rename to common/src/backend/resources/assets/flywheel/textures/flywheel/noise/blue.png From 452d912e7bf991c71d279460707d265d408a24e0 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 22 Feb 2025 23:02:23 -0800 Subject: [PATCH 13/23] Fabulously transparent - Write out depth in the composite pass - When fabulous is enabled, write to the item entity target - Flip the total transmittance back to alpha when compositing so it can be consumed by the blit shader --- .../engine/indirect/IndirectDrawManager.java | 4 +- .../engine/indirect/OitFramebuffer.java | 105 ++++++++++-------- .../backend/engine/uniform/FrameUniforms.java | 3 +- .../internal/indirect/oit_composite.frag | 13 ++- 4 files changed, 72 insertions(+), 53 deletions(-) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index ea77372da..42b967ba2 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -160,7 +160,7 @@ public class IndirectDrawManager extends DrawManager> { group.submitTransparent(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS); } - oitFramebuffer.renderDepth(); + oitFramebuffer.renderDepthFromTransmittance(); // Need to bind this again because we just drew a full screen quad for OIT. vertexArray.bindForDraw(); @@ -198,6 +198,8 @@ public class IndirectDrawManager extends DrawManager> { lightBuffers.delete(); matrixBuffer.delete(); + + oitFramebuffer.delete(); } public void renderCrumbling(List crumblingBlocks) { diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java index 6ebf3e30c..40916c3b0 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java @@ -3,6 +3,7 @@ package dev.engine_room.flywheel.backend.engine.indirect; import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL46; +import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; @@ -33,19 +34,40 @@ public class OitFramebuffer { * Set up the framebuffer. */ public void prepare() { - var mainRenderTarget = Minecraft.getInstance() - .getMainRenderTarget(); + RenderTarget renderTarget; - maybeResizeFBO(mainRenderTarget.width, mainRenderTarget.height); + if (Minecraft.useShaderTransparency()) { + renderTarget = Minecraft.getInstance().levelRenderer.getItemEntityTarget(); - GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, mainRenderTarget.getDepthTextureId(), 0); + renderTarget.copyDepthFrom(Minecraft.getInstance() + .getMainRenderTarget()); + } else { + renderTarget = Minecraft.getInstance() + .getMainRenderTarget(); + } + + maybeResizeFBO(renderTarget.width, renderTarget.height); + + GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, renderTarget.getDepthTextureId(), 0); + + Samplers.COEFFICIENTS.makeActive(); + RenderSystem.bindTexture(0); + GL46.glBindTextureUnit(Samplers.COEFFICIENTS.number, coefficients); + + Samplers.DEPTH_RANGE.makeActive(); + RenderSystem.bindTexture(depthBounds); + + Samplers.NOISE.makeActive(); + NoiseTextures.BLUE_NOISE.bind(); + + GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); } /** * Render out the min and max depth per fragment. */ public void depthRange() { - // No depth writes, but we'll still use the depth test + // No depth writes, but we'll still use the depth test. RenderSystem.depthMask(false); RenderSystem.colorMask(true, true, true, true); RenderSystem.enableBlend(); @@ -57,8 +79,6 @@ public class OitFramebuffer { var far = Minecraft.getInstance().gameRenderer.getDepthFar(); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{-far, -far, 0, 0}); - - GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); } /** @@ -72,38 +92,24 @@ public class OitFramebuffer { RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); RenderSystem.blendEquation(GL46.GL_FUNC_ADD); - Samplers.DEPTH_RANGE.makeActive(); - GlStateManager._bindTexture(depthBounds); - - Samplers.NOISE.makeActive(); - NoiseTextures.BLUE_NOISE.bind(); - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT1, GL46.GL_COLOR_ATTACHMENT2, GL46.GL_COLOR_ATTACHMENT3, GL46.GL_COLOR_ATTACHMENT4}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 1, new float[]{0, 0, 0, 0}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 2, new float[]{0, 0, 0, 0}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 3, new float[]{0, 0, 0, 0}); - - GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); } /** * If any fragment has its transmittance fall off to zero, search the transmittance * function to determine at what depth that occurs and write out to the depth buffer. */ - public void renderDepth() { - // No depth writes, but we'll still use the depth test + public void renderDepthFromTransmittance() { + // Only write to depth, not color. RenderSystem.depthMask(true); RenderSystem.colorMask(false, false, false, false); RenderSystem.disableBlend(); - - Samplers.COEFFICIENTS.makeActive(); - RenderSystem.bindTexture(0); - GL46.glBindTextureUnit(0, coefficients); - - Samplers.DEPTH_RANGE.makeActive(); - RenderSystem.bindTexture(depthBounds); + RenderSystem.depthFunc(GL32.GL_ALWAYS); GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{}); @@ -124,50 +130,53 @@ public class OitFramebuffer { RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); RenderSystem.blendEquation(GL46.GL_FUNC_ADD); - Samplers.DEPTH_RANGE.makeActive(); - GlStateManager._bindTexture(depthBounds); - - Samplers.COEFFICIENTS.makeActive(); - GlStateManager._bindTexture(0); - GL46.glBindTextureUnit(Samplers.COEFFICIENTS.number, coefficients); - - Samplers.NOISE.makeActive(); - NoiseTextures.BLUE_NOISE.bind(); - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT5}); GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); - - GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); } /** * Composite the accumulated luminance onto the main framebuffer. */ public void composite() { - // No depth writes, but we'll still use the depth test - RenderSystem.depthMask(false); + if (Minecraft.useShaderTransparency()) { + Minecraft.getInstance().levelRenderer.getItemEntityTarget() + .bindWrite(false); + } else { + Minecraft.getInstance() + .getMainRenderTarget() + .bindWrite(false); + } + + // The composite shader writes out the closest depth to gl_FragDepth. + // depthMask = true: OIT stuff renders on top of other transparent stuff. + // depthMask = false: other transparent stuff renders on top of OIT stuff. + // If Neo gets wavelet OIT we can use their hooks to be correct with everything. + RenderSystem.depthMask(true); RenderSystem.colorMask(true, true, true, true); RenderSystem.enableBlend(); - RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.DestFactor.SRC_ALPHA); + + // We rely on the blend func to achieve: + // final color = (1 - transmittance_total) * sum(color_f * alpha_f * transmittance_f) / sum(alpha_f * transmittance_f) + // + color_dst * transmittance_total + // + // Though note that the alpha value we emit in the fragment shader is actually (1. - transmittance_total). + // The extra inversion step is so we can have a sane alpha value written out for the fabulous blit shader to consume. + RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); RenderSystem.blendEquation(GL46.GL_FUNC_ADD); - - var mainRenderTarget = Minecraft.getInstance() - .getMainRenderTarget(); - - mainRenderTarget.bindWrite(false); + RenderSystem.depthFunc(GL32.GL_ALWAYS); GlTextureUnit.T0.makeActive(); - RenderSystem.bindTexture(0); - GL46.glBindTextureUnit(0, coefficients); - - GlTextureUnit.T1.makeActive(); RenderSystem.bindTexture(accumulate); programs.getOitCompositeProgram() .bind(); drawFullscreenQuad(); + + Minecraft.getInstance() + .getMainRenderTarget() + .bindWrite(false); } public void delete() { diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java index ce2fd4c6f..a7395630d 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java @@ -13,6 +13,7 @@ import dev.engine_room.flywheel.backend.mixin.LevelRendererAccessor; import net.minecraft.Util; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; import net.minecraft.world.level.Level; @@ -198,7 +199,7 @@ public final class FrameUniforms extends UniformWriter { int pyramidHeight = DepthPyramid.mip0Size(mainRenderTarget.height); int pyramidDepth = DepthPyramid.getImageMipLevels(pyramidWidth, pyramidHeight); - ptr = writeFloat(ptr, 0.05F); // zNear + ptr = writeFloat(ptr, GameRenderer.PROJECTION_Z_NEAR); // zNear ptr = writeFloat(ptr, mc.gameRenderer.getDepthFar()); // zFar ptr = writeFloat(ptr, PROJECTION.m00()); // P00 ptr = writeFloat(ptr, PROJECTION.m11()); // P11 diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag index 733e2e5ae..2aa577e84 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag @@ -1,9 +1,12 @@ #include "flywheel:internal/wavelet.glsl" +#include "flywheel:internal/depth.glsl" +#include "flywheel:internal/uniforms/frame.glsl" layout (location = 0) out vec4 frag; -layout (binding = 0) uniform sampler2DArray _flw_coefficients; -layout (binding = 1) uniform sampler2D _flw_accumulate; +layout (binding = 0) uniform sampler2D _flw_accumulate; +layout (binding = 7) uniform sampler2D _flw_depthRange; +layout (binding = 8) uniform sampler2DArray _flw_coefficients; void main() { vec4 texel = texelFetch(_flw_accumulate, ivec2(gl_FragCoord.xy), 0); @@ -14,5 +17,9 @@ void main() { float total_transmittance = total_transmittance(_flw_coefficients); - frag = vec4(texel.rgb / texel.a, total_transmittance); + frag = vec4(texel.rgb / texel.a, 1. - total_transmittance); + + float minDepth = -texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).r; + + gl_FragDepth = delinearize_depth(minDepth, _flw_cullData.znear, _flw_cullData.zfar); } From 7ec8a870bddbde3b9e5e15cb0348e0cef080fa51 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 23 Feb 2025 13:53:22 -0800 Subject: [PATCH 14/23] The choice is yours - Add ORDER_INDEPENDENT transparency enum - Document all transparency modes - Use ORDER_INDEPENDENT for the default translucent chunk materials --- .../flywheel/api/material/Transparency.java | 71 +++++++++++++++++++ .../backend/engine/MaterialRenderState.java | 2 +- .../flywheel/lib/material/Materials.java | 8 +-- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/common/src/api/java/dev/engine_room/flywheel/api/material/Transparency.java b/common/src/api/java/dev/engine_room/flywheel/api/material/Transparency.java index 55ec1a4a5..06de4550f 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/material/Transparency.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/material/Transparency.java @@ -1,10 +1,81 @@ package dev.engine_room.flywheel.api.material; public enum Transparency { + /** + * No blending. Used for solid and cutout geometry. + */ OPAQUE, + + /** + * Additive blending. + * + *

Each fragment blends color and alpha with the following equation: + *

+	 * {@code
+	 * out = src + dst
+	 * }
+	 * 
+ */ ADDITIVE, + + /** + * Lightning transparency. + * + *

Each fragment blends color and alpha with the following equation: + *

+	 * {@code
+	 * out = src * alpha_src + dst
+	 * }
+	 * 
+ */ LIGHTNING, + + /** + * Glint transparency. Used for the enchantment effect. + * + *

Each fragment blends with the following equations: + *

+	 * {@code
+	 * color_out = color_src^2 + color_dst
+	 * alpha_out = alpha_dst
+	 * }
+	 * 
+ */ GLINT, + + /** + * Crumbling transparency. Used for the block breaking overlay. + * + *

Each fragment blends with the following equations: + *

+	 * {@code
+	 * color_out = 2 * color_src * color_dst
+	 * alpha_out = alpha_src
+	 * }
+	 * 
+ */ CRUMBLING, + + /** + * Translucent transparency. + * + *

Each fragment blends with the following equations: + *

+	 * {@code
+	 * color_out = color_src * alpha_src + color_dst * (1 - alpha_src)
+	 * alpha_out = alpha_src + alpha_dst * (1 - alpha_src)
+	 * }
+	 * 
+ */ TRANSLUCENT, + + /** + * If supported by the backend, this mode will use OIT that approximates {@code TRANSLUCENT} transparency. + * + *

If a backend does not support OIT, it must treat this the same as {@code TRANSLUCENT}. + * + *

It is recommended to use this option when possible, though for cases where blend modes are used as an + * overlay against solid geometry the order dependent modes are preferred. + */ + ORDER_INDEPENDENT, } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java index 43206153f..5de40779e 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java @@ -135,7 +135,7 @@ public final class MaterialRenderState { RenderSystem.enableBlend(); RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_COLOR, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); } - case TRANSLUCENT -> { + case TRANSLUCENT, ORDER_INDEPENDENT -> { RenderSystem.enableBlend(); RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); } diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/material/Materials.java b/common/src/lib/java/dev/engine_room/flywheel/lib/material/Materials.java index 3d72653cd..51229b0ce 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/material/Materials.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/material/Materials.java @@ -32,20 +32,20 @@ public final class Materials { .build(); public static final Material TRANSLUCENT_BLOCK = SimpleMaterial.builder() - .transparency(Transparency.TRANSLUCENT) + .transparency(Transparency.ORDER_INDEPENDENT) .build(); public static final Material TRANSLUCENT_UNSHADED_BLOCK = SimpleMaterial.builder() - .transparency(Transparency.TRANSLUCENT) + .transparency(Transparency.ORDER_INDEPENDENT) .diffuse(false) .build(); public static final Material TRIPWIRE_BLOCK = SimpleMaterial.builder() .cutout(CutoutShaders.ONE_TENTH) - .transparency(Transparency.TRANSLUCENT) + .transparency(Transparency.ORDER_INDEPENDENT) .build(); public static final Material TRIPWIRE_UNSHADED_BLOCK = SimpleMaterial.builder() .cutout(CutoutShaders.ONE_TENTH) - .transparency(Transparency.TRANSLUCENT) + .transparency(Transparency.ORDER_INDEPENDENT) .diffuse(false) .build(); From 997583365085cc2f65efdbfce0648b1d9ee39a64 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 23 Feb 2025 14:44:28 -0800 Subject: [PATCH 15/23] Intelligent priorities - Rank indirect very low on intel drivers --- .../engine_room/flywheel/backend/Backends.java | 12 +++++++++++- .../flywheel/lib/backend/SimpleBackend.java | 16 ++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java b/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java index c6de7eb47..41700b9ff 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java @@ -7,6 +7,7 @@ import dev.engine_room.flywheel.backend.compile.InstancingPrograms; import dev.engine_room.flywheel.backend.engine.EngineImpl; import dev.engine_room.flywheel.backend.engine.indirect.IndirectDrawManager; import dev.engine_room.flywheel.backend.engine.instancing.InstancedDrawManager; +import dev.engine_room.flywheel.backend.gl.Driver; import dev.engine_room.flywheel.backend.gl.GlCompat; import dev.engine_room.flywheel.lib.backend.SimpleBackend; import dev.engine_room.flywheel.lib.util.ShadersModHelper; @@ -26,7 +27,16 @@ public final class Backends { */ public static final Backend INDIRECT = SimpleBackend.builder() .engineFactory(level -> new EngineImpl(level, new IndirectDrawManager(IndirectPrograms.get()), 256)) - .priority(1000) + .priority(() -> { + // Read from GlCompat in these provider because loading GlCompat + // at the same time the backends are registered causes GlCapabilities to be null. + if (GlCompat.DRIVER == Driver.INTEL) { + // Intel has very poor performance with indirect rendering, and on top of that has graphics bugs + return 1; + } else { + return 1000; + } + }) .supported(() -> GlCompat.SUPPORTS_INDIRECT && IndirectPrograms.allLoaded() && !ShadersModHelper.isShaderPackInUse()) .register(Flywheel.rl("indirect")); diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java b/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java index bc09227c1..0d6a8c00d 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java @@ -11,10 +11,10 @@ import net.minecraft.world.level.LevelAccessor; public final class SimpleBackend implements Backend { private final Function engineFactory; - private final int priority; + private final PriorityProvider priority; private final BooleanSupplier isSupported; - public SimpleBackend(int priority, Function engineFactory, BooleanSupplier isSupported) { + public SimpleBackend(PriorityProvider priority, Function engineFactory, BooleanSupplier isSupported) { this.priority = priority; this.engineFactory = engineFactory; this.isSupported = isSupported; @@ -31,7 +31,7 @@ public final class SimpleBackend implements Backend { @Override public int priority() { - return priority; + return priority.get(); } @Override @@ -39,9 +39,13 @@ public final class SimpleBackend implements Backend { return isSupported.getAsBoolean(); } + public interface PriorityProvider { + int get(); + } + public static final class Builder { private Function engineFactory; - private int priority = 0; + private PriorityProvider priority = () -> 0; private BooleanSupplier isSupported; public Builder engineFactory(Function engineFactory) { @@ -50,6 +54,10 @@ public final class SimpleBackend implements Backend { } public Builder priority(int priority) { + return priority(() -> priority); + } + + public Builder priority(PriorityProvider priority) { this.priority = priority; return this; } From 026eb905668620930b019249ff35670a92c5457c Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 23 Feb 2025 15:07:46 -0800 Subject: [PATCH 16/23] If you say so - Only do OIT for materials marked ORDER_INDEPENDENT - Clean up some of the indirect frame logic, early out if there's nothing to do --- .../backend/engine/MaterialRenderState.java | 2 +- .../engine/indirect/IndirectCullingGroup.java | 48 +++++-------- .../engine/indirect/IndirectDrawManager.java | 67 ++++++++++++------- 3 files changed, 59 insertions(+), 58 deletions(-) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java index 5f8a2c9b1..ec98d5c09 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/MaterialRenderState.java @@ -146,7 +146,7 @@ public final class MaterialRenderState { RenderSystem.enableBlend(); RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.SRC_COLOR, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); } - case TRANSLUCENT, ORDER_INDEPENDENT -> { + case TRANSLUCENT -> { RenderSystem.enableBlend(); RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java index 886f76830..3e73bfe1c 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -38,7 +38,7 @@ public class IndirectCullingGroup { private final List> instancers = new ArrayList<>(); private final List indirectDraws = new ArrayList<>(); private final List multiDraws = new ArrayList<>(); - private final List transparentDraws = new ArrayList<>(); + private final List oitDraws = new ArrayList<>(); private final IndirectPrograms programs; private final GlProgram cullProgram; @@ -57,7 +57,7 @@ public class IndirectCullingGroup { cullProgram = programs.getCullingProgram(instanceType); } - public void flushInstancers() { + public boolean flushInstancers() { instanceCountThisFrame = 0; int modelIndex = 0; for (var iterator = instancers.iterator(); iterator.hasNext(); ) { @@ -79,13 +79,17 @@ public class IndirectCullingGroup { if (indirectDraws.removeIf(IndirectDraw::deleted)) { needsDrawSort = true; } + + var out = indirectDraws.isEmpty(); + + if (out) { + delete(); + } + + return out; } public void upload(StagingBuffer stagingBuffer) { - if (nothingToDo()) { - return; - } - buffers.updateCounts(instanceCountThisFrame, instancers.size(), indirectDraws.size()); // Upload only instances that have changed. @@ -107,10 +111,6 @@ public class IndirectCullingGroup { } public void dispatchCull() { - if (nothingToDo()) { - return; - } - Uniforms.bindAll(); cullProgram.bind(); @@ -119,21 +119,17 @@ public class IndirectCullingGroup { } public void dispatchApply() { - if (nothingToDo()) { - return; - } - buffers.bindForApply(); glDispatchCompute(GlCompat.getComputeGroupCount(indirectDraws.size()), 1, 1); } - private boolean nothingToDo() { - return indirectDraws.isEmpty() || instanceCountThisFrame == 0; + public boolean hasOitDraws() { + return !oitDraws.isEmpty(); } private void sortDraws() { multiDraws.clear(); - transparentDraws.clear(); + oitDraws.clear(); // sort by visual type, then material indirectDraws.sort(DRAW_COMPARATOR); @@ -143,7 +139,7 @@ public class IndirectCullingGroup { // if the next draw call has a different VisualType or Material, start a new MultiDraw if (i == indirectDraws.size() - 1 || incompatibleDraws(draw1, indirectDraws.get(i + 1))) { var dst = draw1.material() - .transparency() == Transparency.TRANSLUCENT ? transparentDraws : multiDraws; + .transparency() == Transparency.ORDER_INDEPENDENT ? oitDraws : multiDraws; dst.add(new MultiDraw(draw1.material(), draw1.isEmbedded(), start, i + 1)); start = i + 1; } @@ -178,7 +174,7 @@ public class IndirectCullingGroup { } public void submitSolid() { - if (nothingToDo()) { + if (multiDraws.isEmpty()) { return; } @@ -204,7 +200,7 @@ public class IndirectCullingGroup { } public void submitTransparent(PipelineCompiler.OitMode oit) { - if (nothingToDo()) { + if (oitDraws.isEmpty()) { return; } @@ -214,7 +210,7 @@ public class IndirectCullingGroup { GlProgram lastProgram = null; - for (var multiDraw : transparentDraws) { + for (var multiDraw : oitDraws) { var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, oit); if (drawProgram != lastProgram) { lastProgram = drawProgram; @@ -290,16 +286,6 @@ public class IndirectCullingGroup { buffers.delete(); } - public boolean checkEmptyAndDelete() { - var out = indirectDraws.isEmpty(); - - if (out) { - delete(); - } - - return out; - } - private record MultiDraw(Material material, boolean embedded, int start, int end) { private void submit(GlProgram drawProgram) { GlCompat.safeMultiDrawElementsIndirect(drawProgram, GL_TRIANGLES, GL_UNSIGNED_INT, this.start, this.end, IndirectBuffers.DRAW_COMMAND_STRIDE); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index 42b967ba2..262da1c2e 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -85,13 +85,11 @@ public class IndirectDrawManager extends DrawManager> { public void render(LightStorage lightStorage, EnvironmentStorage environmentStorage) { super.render(lightStorage, environmentStorage); - for (var group : cullingGroups.values()) { - group.flushInstancers(); - } - + // Flush instance counts, page mappings, and prune empty groups. cullingGroups.values() - .removeIf(IndirectCullingGroup::checkEmptyAndDelete); + .removeIf(IndirectCullingGroup::flushInstancers); + // Instancers may have been emptied in the above call, now remove them here. instancers.values() .removeIf(instancer -> instancer.instanceCount() == 0); @@ -99,6 +97,12 @@ public class IndirectDrawManager extends DrawManager> { stagingBuffer.reclaim(); + // Genuinely nothing to do, we can just early out. + // Still process the mesh pool and reclaim fenced staging regions though. + if (cullingGroups.isEmpty()) { + return; + } + lightBuffers.flush(stagingBuffer, lightStorage); matrixBuffer.flush(stagingBuffer, environmentStorage); @@ -146,33 +150,44 @@ public class IndirectDrawManager extends DrawManager> { group.submitSolid(); } - oitFramebuffer.prepare(); - - oitFramebuffer.depthRange(); - + // Let's avoid invoking the oit chain if we don't have anything to do + boolean useOit = false; for (var group : cullingGroups.values()) { - group.submitTransparent(PipelineCompiler.OitMode.DEPTH_RANGE); + if (group.hasOitDraws()) { + useOit = true; + break; + } } - oitFramebuffer.renderTransmittance(); + if (useOit) { + oitFramebuffer.prepare(); - for (var group : cullingGroups.values()) { - group.submitTransparent(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS); + oitFramebuffer.depthRange(); + + for (var group : cullingGroups.values()) { + group.submitTransparent(PipelineCompiler.OitMode.DEPTH_RANGE); + } + + oitFramebuffer.renderTransmittance(); + + for (var group : cullingGroups.values()) { + group.submitTransparent(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS); + } + + oitFramebuffer.renderDepthFromTransmittance(); + + // Need to bind this again because we just drew a full screen quad for OIT. + vertexArray.bindForDraw(); + + oitFramebuffer.shade(); + + for (var group : cullingGroups.values()) { + group.submitTransparent(PipelineCompiler.OitMode.EVALUATE); + } + + oitFramebuffer.composite(); } - oitFramebuffer.renderDepthFromTransmittance(); - - // Need to bind this again because we just drew a full screen quad for OIT. - vertexArray.bindForDraw(); - - oitFramebuffer.shade(); - - for (var group : cullingGroups.values()) { - group.submitTransparent(PipelineCompiler.OitMode.EVALUATE); - } - - oitFramebuffer.composite(); - MaterialRenderState.reset(); TextureBinder.resetLightAndOverlay(); } From d2a8d075a7cd3553bf0ff0a365eeee58f656ae5a Mon Sep 17 00:00:00 2001 From: PepperCode1 <44146161+PepperCode1@users.noreply.github.com> Date: Sun, 23 Feb 2025 16:12:50 -0800 Subject: [PATCH 17/23] Assorted adjustments - Add EmptyModel - Move Flywheel.rl to ResourceUtil - Move RenderContext from api to api.backend - Improve LineModelBuilder - Fix exception when calling build twice in a row - Expose ensureCapacity - Return existing EmptyModel.INSTANCE when applicable - Try to shrink memory before finalizing model --- .../engine_room/flywheel/api/Flywheel.java | 9 +-- .../flywheel/api/backend/Engine.java | 1 - .../api/{ => backend}/RenderContext.java | 2 +- .../visualization/VisualizationManager.java | 2 +- .../flywheel/backend/Backends.java | 6 +- .../flywheel/backend/InternalVertex.java | 4 +- .../flywheel/backend/compile/FlwPrograms.java | 3 +- .../backend/compile/IndirectPrograms.java | 13 ++-- .../backend/compile/PipelineCompiler.java | 9 ++- .../flywheel/backend/compile/Pipelines.java | 10 +-- .../BufferTextureInstanceComponent.java | 4 +- .../component/InstanceStructComponent.java | 4 +- .../component/SsboInstanceComponent.java | 4 +- .../StringSubstitutionComponent.java | 4 +- .../component/UberShaderComponent.java | 4 +- .../compile/core/FailedCompilation.java | 4 +- .../flywheel/backend/engine/DrawManager.java | 2 +- .../flywheel/backend/engine/EngineImpl.java | 2 +- .../backend/engine/uniform/FrameUniforms.java | 2 +- .../backend/engine/uniform/LevelUniforms.java | 2 +- .../engine/uniform/PlayerUniforms.java | 2 +- .../backend/engine/uniform/Uniforms.java | 2 +- .../flywheel/lib/backend/SimpleBackend.java | 23 +++---- .../flywheel/lib/instance/InstanceTypes.java | 18 +++--- .../flywheel/lib/material/CutoutShaders.java | 10 +-- .../flywheel/lib/material/FogShaders.java | 8 +-- .../flywheel/lib/material/LightShaders.java | 8 +-- .../lib/material/StandardMaterialShaders.java | 10 +-- .../flywheel/lib/model/EmptyModel.java | 24 +++++++ .../flywheel/lib/model/LineModelBuilder.java | 62 +++++++++++++------ .../flywheel/lib/util/ResourceUtil.java | 4 ++ .../flywheel/impl/BackendManagerImpl.java | 4 +- .../impl/event/RenderContextImpl.java | 2 +- .../VisualizationManagerImpl.java | 2 +- .../flywheel/backend/glsl/TestBase.java | 8 +-- .../backend/compile/FlwProgramsReloader.java | 4 +- .../flywheel/lib/model/baked/MeshEmitter.java | 4 +- .../model/baked/PartialModelEventHandler.java | 4 +- .../flywheel/impl/FlywheelFabric.java | 7 ++- .../flywheel/impl/FlywheelForge.java | 7 ++- 40 files changed, 176 insertions(+), 128 deletions(-) rename common/src/api/java/dev/engine_room/flywheel/api/{ => backend}/RenderContext.java (91%) create mode 100644 common/src/lib/java/dev/engine_room/flywheel/lib/model/EmptyModel.java diff --git a/common/src/api/java/dev/engine_room/flywheel/api/Flywheel.java b/common/src/api/java/dev/engine_room/flywheel/api/Flywheel.java index ef18b8b27..f2f5e6bca 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/Flywheel.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/Flywheel.java @@ -1,14 +1,11 @@ package dev.engine_room.flywheel.api; -import net.minecraft.resources.ResourceLocation; - public final class Flywheel { + /** + * The mod ID and resource namespace of Flywheel. + */ public static final String ID = "flywheel"; private Flywheel() { } - - public static ResourceLocation rl(String path) { - return new ResourceLocation(ID, path); - } } diff --git a/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java b/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java index 6f9d0a93a..b44ee31ff 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/backend/Engine.java @@ -5,7 +5,6 @@ import java.util.List; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Range; -import dev.engine_room.flywheel.api.RenderContext; import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.task.Plan; import dev.engine_room.flywheel.api.visualization.VisualizationContext; diff --git a/common/src/api/java/dev/engine_room/flywheel/api/RenderContext.java b/common/src/api/java/dev/engine_room/flywheel/api/backend/RenderContext.java similarity index 91% rename from common/src/api/java/dev/engine_room/flywheel/api/RenderContext.java rename to common/src/api/java/dev/engine_room/flywheel/api/backend/RenderContext.java index 5fc9f68b5..842f7a266 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/RenderContext.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/backend/RenderContext.java @@ -1,4 +1,4 @@ -package dev.engine_room.flywheel.api; +package dev.engine_room.flywheel.api.backend; import org.joml.Matrix4fc; diff --git a/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java b/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java index e7f831dbc..33437d798 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualizationManager.java @@ -5,7 +5,7 @@ import java.util.SortedSet; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; -import dev.engine_room.flywheel.api.RenderContext; +import dev.engine_room.flywheel.api.backend.RenderContext; import dev.engine_room.flywheel.api.internal.FlwApiLink; import dev.engine_room.flywheel.api.visual.Effect; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java b/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java index 41700b9ff..d796248ae 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java @@ -1,6 +1,5 @@ package dev.engine_room.flywheel.backend; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.backend.Backend; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; import dev.engine_room.flywheel.backend.compile.InstancingPrograms; @@ -10,6 +9,7 @@ import dev.engine_room.flywheel.backend.engine.instancing.InstancedDrawManager; import dev.engine_room.flywheel.backend.gl.Driver; import dev.engine_room.flywheel.backend.gl.GlCompat; import dev.engine_room.flywheel.lib.backend.SimpleBackend; +import dev.engine_room.flywheel.lib.util.ResourceUtil; import dev.engine_room.flywheel.lib.util.ShadersModHelper; public final class Backends { @@ -20,7 +20,7 @@ public final class Backends { .engineFactory(level -> new EngineImpl(level, new InstancedDrawManager(InstancingPrograms.get()), 256)) .priority(500) .supported(() -> GlCompat.SUPPORTS_INSTANCING && InstancingPrograms.allLoaded() && !ShadersModHelper.isShaderPackInUse()) - .register(Flywheel.rl("instancing")); + .register(ResourceUtil.rl("instancing")); /** * Use Compute shaders to cull instances. @@ -38,7 +38,7 @@ public final class Backends { } }) .supported(() -> GlCompat.SUPPORTS_INDIRECT && IndirectPrograms.allLoaded() && !ShadersModHelper.isShaderPackInUse()) - .register(Flywheel.rl("indirect")); + .register(ResourceUtil.rl("indirect")); private Backends() { } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/InternalVertex.java b/common/src/backend/java/dev/engine_room/flywheel/backend/InternalVertex.java index 524e5a0b4..beaab64ce 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/InternalVertex.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/InternalVertex.java @@ -2,11 +2,11 @@ package dev.engine_room.flywheel.backend; import java.util.List; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.layout.FloatRepr; import dev.engine_room.flywheel.api.layout.Layout; import dev.engine_room.flywheel.api.layout.LayoutBuilder; import dev.engine_room.flywheel.backend.gl.array.VertexAttribute; +import dev.engine_room.flywheel.lib.util.ResourceUtil; import dev.engine_room.flywheel.lib.vertex.FullVertexView; import dev.engine_room.flywheel.lib.vertex.VertexView; import net.minecraft.resources.ResourceLocation; @@ -24,7 +24,7 @@ public final class InternalVertex { public static final List ATTRIBUTES = LayoutAttributes.attributes(LAYOUT); public static final int STRIDE = LAYOUT.byteSize(); - public static final ResourceLocation LAYOUT_SHADER = Flywheel.rl("internal/vertex_input.vert"); + public static final ResourceLocation LAYOUT_SHADER = ResourceUtil.rl("internal/vertex_input.vert"); private InternalVertex() { } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwPrograms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwPrograms.java index 2ca25c3bb..2dbc6cfb8 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwPrograms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwPrograms.java @@ -8,13 +8,14 @@ import org.slf4j.LoggerFactory; import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.backend.glsl.ShaderSources; import dev.engine_room.flywheel.backend.glsl.SourceComponent; +import dev.engine_room.flywheel.lib.util.ResourceUtil; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceManager; public final class FlwPrograms { public static final Logger LOGGER = LoggerFactory.getLogger(Flywheel.ID + "/backend/shaders"); - private static final ResourceLocation COMPONENTS_HEADER_FRAG = Flywheel.rl("internal/components_header.frag"); + private static final ResourceLocation COMPONENTS_HEADER_FRAG = ResourceUtil.rl("internal/components_header.frag"); public static ShaderSources SOURCES; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java index 39d211ef9..007bd7bef 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java @@ -6,7 +6,6 @@ import org.jetbrains.annotations.Nullable; import com.google.common.collect.ImmutableList; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.material.Material; import dev.engine_room.flywheel.backend.compile.component.InstanceStructComponent; @@ -25,12 +24,12 @@ import dev.engine_room.flywheel.lib.util.ResourceUtil; import net.minecraft.resources.ResourceLocation; public class IndirectPrograms extends AtomicReferenceCounted { - private static final ResourceLocation CULL_SHADER_API_IMPL = Flywheel.rl("internal/indirect/cull_api_impl.glsl"); - private static final ResourceLocation CULL_SHADER_MAIN = Flywheel.rl("internal/indirect/cull.glsl"); - private static final ResourceLocation APPLY_SHADER_MAIN = Flywheel.rl("internal/indirect/apply.glsl"); - private static final ResourceLocation SCATTER_SHADER_MAIN = Flywheel.rl("internal/indirect/scatter.glsl"); - private static final ResourceLocation DOWNSAMPLE_FIRST = Flywheel.rl("internal/indirect/downsample_first.glsl"); - private static final ResourceLocation DOWNSAMPLE_SECOND = Flywheel.rl("internal/indirect/downsample_second.glsl"); + private static final ResourceLocation CULL_SHADER_API_IMPL = ResourceUtil.rl("internal/indirect/cull_api_impl.glsl"); + private static final ResourceLocation CULL_SHADER_MAIN = ResourceUtil.rl("internal/indirect/cull.glsl"); + private static final ResourceLocation APPLY_SHADER_MAIN = ResourceUtil.rl("internal/indirect/apply.glsl"); + private static final ResourceLocation SCATTER_SHADER_MAIN = ResourceUtil.rl("internal/indirect/scatter.glsl"); + private static final ResourceLocation DOWNSAMPLE_FIRST = ResourceUtil.rl("internal/indirect/downsample_first.glsl"); + private static final ResourceLocation DOWNSAMPLE_SECOND = ResourceUtil.rl("internal/indirect/downsample_second.glsl"); public static final List UTIL_SHADERS = List.of(APPLY_SHADER_MAIN, SCATTER_SHADER_MAIN, DOWNSAMPLE_FIRST, DOWNSAMPLE_SECOND); private static final Compile> CULL = new Compile<>(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java index e752edfd2..6a6950ed6 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.Set; import java.util.WeakHashMap; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.material.LightShader; import dev.engine_room.flywheel.api.material.Material; @@ -40,8 +39,8 @@ public final class PipelineCompiler { private static UberShaderComponent FOG; private static UberShaderComponent CUTOUT; - private static final ResourceLocation API_IMPL_VERT = Flywheel.rl("internal/api_impl.vert"); - private static final ResourceLocation API_IMPL_FRAG = Flywheel.rl("internal/api_impl.frag"); + private static final ResourceLocation API_IMPL_VERT = ResourceUtil.rl("internal/api_impl.vert"); + private static final ResourceLocation API_IMPL_FRAG = ResourceUtil.rl("internal/api_impl.frag"); private final CompilationHarness harness; @@ -184,7 +183,7 @@ public final class PipelineCompiler { } public static void createFogComponent() { - FOG = UberShaderComponent.builder(Flywheel.rl("fog")) + FOG = UberShaderComponent.builder(ResourceUtil.rl("fog")) .materialSources(MaterialShaderIndices.fogSources() .all()) .adapt(FnSignature.create() @@ -197,7 +196,7 @@ public final class PipelineCompiler { } private static void createCutoutComponent() { - CUTOUT = UberShaderComponent.builder(Flywheel.rl("cutout")) + CUTOUT = UberShaderComponent.builder(ResourceUtil.rl("cutout")) .materialSources(MaterialShaderIndices.cutoutSources() .all()) .adapt(FnSignature.create() diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/Pipelines.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/Pipelines.java index 65f122f4b..ff5061b3b 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/Pipelines.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/Pipelines.java @@ -1,15 +1,15 @@ package dev.engine_room.flywheel.backend.compile; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.backend.Samplers; import dev.engine_room.flywheel.backend.compile.component.BufferTextureInstanceComponent; import dev.engine_room.flywheel.backend.compile.component.SsboInstanceComponent; +import dev.engine_room.flywheel.lib.util.ResourceUtil; public final class Pipelines { public static final Pipeline INSTANCING = Pipeline.builder() .compilerMarker("instancing") - .vertexMain(Flywheel.rl("internal/instancing/main.vert")) - .fragmentMain(Flywheel.rl("internal/instancing/main.frag")) + .vertexMain(ResourceUtil.rl("internal/instancing/main.vert")) + .fragmentMain(ResourceUtil.rl("internal/instancing/main.frag")) .assembler(BufferTextureInstanceComponent::new) .onLink(program -> { program.setSamplerBinding("_flw_instances", Samplers.INSTANCE_BUFFER); @@ -20,8 +20,8 @@ public final class Pipelines { public static final Pipeline INDIRECT = Pipeline.builder() .compilerMarker("indirect") - .vertexMain(Flywheel.rl("internal/indirect/main.vert")) - .fragmentMain(Flywheel.rl("internal/indirect/main.frag")) + .vertexMain(ResourceUtil.rl("internal/indirect/main.vert")) + .fragmentMain(ResourceUtil.rl("internal/indirect/main.frag")) .assembler(SsboInstanceComponent::new) .onLink($ -> { }) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/BufferTextureInstanceComponent.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/BufferTextureInstanceComponent.java index 262f051b8..aa421e13f 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/BufferTextureInstanceComponent.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/BufferTextureInstanceComponent.java @@ -2,7 +2,6 @@ package dev.engine_room.flywheel.backend.compile.component; import java.util.ArrayList; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.layout.Layout; import dev.engine_room.flywheel.backend.glsl.generate.FnSignature; @@ -11,6 +10,7 @@ import dev.engine_room.flywheel.backend.glsl.generate.GlslBuilder; import dev.engine_room.flywheel.backend.glsl.generate.GlslExpr; import dev.engine_room.flywheel.backend.glsl.generate.GlslStmt; import dev.engine_room.flywheel.lib.math.MoreMath; +import dev.engine_room.flywheel.lib.util.ResourceUtil; public class BufferTextureInstanceComponent extends InstanceAssemblerComponent { private static final String[] SWIZZLE_SELECTORS = { "x", "y", "z", "w" }; @@ -21,7 +21,7 @@ public class BufferTextureInstanceComponent extends InstanceAssemblerComponent { @Override public String name() { - return Flywheel.rl("buffer_texture_instance_assembler").toString(); + return ResourceUtil.rl("buffer_texture_instance_assembler").toString(); } @Override diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/InstanceStructComponent.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/InstanceStructComponent.java index ec875e095..ac743e578 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/InstanceStructComponent.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/InstanceStructComponent.java @@ -3,12 +3,12 @@ package dev.engine_room.flywheel.backend.compile.component; import java.util.Collection; import java.util.Collections; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.layout.Layout; import dev.engine_room.flywheel.backend.compile.LayoutInterpreter; import dev.engine_room.flywheel.backend.glsl.SourceComponent; import dev.engine_room.flywheel.backend.glsl.generate.GlslBuilder; +import dev.engine_room.flywheel.lib.util.ResourceUtil; public class InstanceStructComponent implements SourceComponent { private static final String STRUCT_NAME = "FlwInstance"; @@ -21,7 +21,7 @@ public class InstanceStructComponent implements SourceComponent { @Override public String name() { - return Flywheel.rl("instance_struct").toString(); + return ResourceUtil.rl("instance_struct").toString(); } @Override diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/SsboInstanceComponent.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/SsboInstanceComponent.java index 2b7df8af8..1151343db 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/SsboInstanceComponent.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/SsboInstanceComponent.java @@ -2,7 +2,6 @@ package dev.engine_room.flywheel.backend.compile.component; import java.util.ArrayList; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.layout.Layout; import dev.engine_room.flywheel.backend.engine.indirect.BufferBindings; @@ -12,6 +11,7 @@ import dev.engine_room.flywheel.backend.glsl.generate.GlslBuilder; import dev.engine_room.flywheel.backend.glsl.generate.GlslExpr; import dev.engine_room.flywheel.backend.glsl.generate.GlslStmt; import dev.engine_room.flywheel.lib.math.MoreMath; +import dev.engine_room.flywheel.lib.util.ResourceUtil; public class SsboInstanceComponent extends InstanceAssemblerComponent { public SsboInstanceComponent(InstanceType type) { @@ -20,7 +20,7 @@ public class SsboInstanceComponent extends InstanceAssemblerComponent { @Override public String name() { - return Flywheel.rl("ssbo_instance_assembler").toString(); + return ResourceUtil.rl("ssbo_instance_assembler").toString(); } @Override diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/StringSubstitutionComponent.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/StringSubstitutionComponent.java index f3091f015..1960c283b 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/StringSubstitutionComponent.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/StringSubstitutionComponent.java @@ -3,8 +3,8 @@ package dev.engine_room.flywheel.backend.compile.component; import java.util.Collection; import java.util.Map; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.backend.glsl.SourceComponent; +import dev.engine_room.flywheel.lib.util.ResourceUtil; public final class StringSubstitutionComponent implements SourceComponent { private final SourceComponent source; @@ -42,7 +42,7 @@ public final class StringSubstitutionComponent implements SourceComponent { @Override public String name() { - return Flywheel.rl("string_substitution").toString() + " / " + source.name(); + return ResourceUtil.rl("string_substitution").toString() + " / " + source.name(); } @Override diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/UberShaderComponent.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/UberShaderComponent.java index ac3d41119..f98aebd10 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/UberShaderComponent.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/component/UberShaderComponent.java @@ -10,7 +10,6 @@ import org.jetbrains.annotations.Nullable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.backend.glsl.ShaderSources; import dev.engine_room.flywheel.backend.glsl.SourceComponent; import dev.engine_room.flywheel.backend.glsl.SourceFile; @@ -19,6 +18,7 @@ import dev.engine_room.flywheel.backend.glsl.generate.GlslBlock; import dev.engine_room.flywheel.backend.glsl.generate.GlslBuilder; import dev.engine_room.flywheel.backend.glsl.generate.GlslExpr; import dev.engine_room.flywheel.backend.glsl.generate.GlslSwitch; +import dev.engine_room.flywheel.lib.util.ResourceUtil; import net.minecraft.resources.ResourceLocation; public class UberShaderComponent implements SourceComponent { @@ -40,7 +40,7 @@ public class UberShaderComponent implements SourceComponent { @Override public String name() { - return Flywheel.rl("uber_shader").toString() + " / " + name; + return ResourceUtil.rl("uber_shader").toString() + " / " + name; } @Override diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/core/FailedCompilation.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/core/FailedCompilation.java index 47553238d..9ff6facd0 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/core/FailedCompilation.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/core/FailedCompilation.java @@ -8,18 +8,18 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.backend.glsl.SourceFile; import dev.engine_room.flywheel.backend.glsl.SourceLines; import dev.engine_room.flywheel.backend.glsl.error.ConsoleColors; import dev.engine_room.flywheel.backend.glsl.error.ErrorBuilder; import dev.engine_room.flywheel.backend.glsl.error.ErrorLevel; import dev.engine_room.flywheel.backend.glsl.span.Span; +import dev.engine_room.flywheel.lib.util.ResourceUtil; import dev.engine_room.flywheel.lib.util.StringUtil; import net.minecraft.resources.ResourceLocation; public class FailedCompilation { - public static final ResourceLocation GENERATED_SOURCE_NAME = Flywheel.rl("generated_source"); + public static final ResourceLocation GENERATED_SOURCE_NAME = ResourceUtil.rl("generated_source"); private static final Pattern PATTERN_ONE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)"); private static final Pattern PATTERN_TWO = Pattern.compile("(\\w+): (\\d+):(\\d+):(?: '(.+?)' :)?(.*)"); private final List files; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java index 996c900a2..0a835f9dc 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java @@ -12,8 +12,8 @@ import org.jetbrains.annotations.Nullable; import com.mojang.datafixers.util.Pair; -import dev.engine_room.flywheel.api.RenderContext; import dev.engine_room.flywheel.api.backend.Engine; +import dev.engine_room.flywheel.api.backend.RenderContext; import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.model.Model; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java index a5c83d33d..f0381eecc 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java @@ -4,8 +4,8 @@ import java.util.List; import com.mojang.blaze3d.systems.RenderSystem; -import dev.engine_room.flywheel.api.RenderContext; import dev.engine_room.flywheel.api.backend.Engine; +import dev.engine_room.flywheel.api.backend.RenderContext; import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.instance.Instancer; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java index 07e8a9e5c..1e9d47c55 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/FrameUniforms.java @@ -6,7 +6,7 @@ import org.joml.Vector2f; import org.joml.Vector3f; import org.lwjgl.system.MemoryUtil; -import dev.engine_room.flywheel.api.RenderContext; +import dev.engine_room.flywheel.api.backend.RenderContext; import dev.engine_room.flywheel.api.visualization.VisualizationManager; import dev.engine_room.flywheel.backend.engine.indirect.DepthPyramid; import dev.engine_room.flywheel.backend.mixin.LevelRendererAccessor; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/LevelUniforms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/LevelUniforms.java index 6dcdf7044..126f08a2a 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/LevelUniforms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/LevelUniforms.java @@ -2,7 +2,7 @@ package dev.engine_room.flywheel.backend.engine.uniform; import org.joml.Vector3f; -import dev.engine_room.flywheel.api.RenderContext; +import dev.engine_room.flywheel.api.backend.RenderContext; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.Level; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/PlayerUniforms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/PlayerUniforms.java index 34a974abe..1c2bc6b34 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/PlayerUniforms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/PlayerUniforms.java @@ -2,7 +2,7 @@ package dev.engine_room.flywheel.backend.engine.uniform; import org.jetbrains.annotations.Nullable; -import dev.engine_room.flywheel.api.RenderContext; +import dev.engine_room.flywheel.api.backend.RenderContext; import dev.engine_room.flywheel.backend.FlwBackendXplat; import dev.engine_room.flywheel.backend.mixin.AbstractClientPlayerAccessor; import net.minecraft.client.Minecraft; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/Uniforms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/Uniforms.java index a0cbf47e5..bcfa1e3ef 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/Uniforms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/uniform/Uniforms.java @@ -1,6 +1,6 @@ package dev.engine_room.flywheel.backend.engine.uniform; -import dev.engine_room.flywheel.api.RenderContext; +import dev.engine_room.flywheel.api.backend.RenderContext; import dev.engine_room.flywheel.backend.gl.shader.GlProgram; public final class Uniforms { diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java b/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java index 0d6a8c00d..f7da878c7 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/backend/SimpleBackend.java @@ -3,6 +3,9 @@ package dev.engine_room.flywheel.lib.backend; import java.util.Objects; import java.util.function.BooleanSupplier; import java.util.function.Function; +import java.util.function.IntSupplier; + +import org.jetbrains.annotations.Nullable; import dev.engine_room.flywheel.api.backend.Backend; import dev.engine_room.flywheel.api.backend.Engine; @@ -11,12 +14,12 @@ import net.minecraft.world.level.LevelAccessor; public final class SimpleBackend implements Backend { private final Function engineFactory; - private final PriorityProvider priority; + private final IntSupplier priority; private final BooleanSupplier isSupported; - public SimpleBackend(PriorityProvider priority, Function engineFactory, BooleanSupplier isSupported) { - this.priority = priority; + public SimpleBackend(Function engineFactory, IntSupplier priority, BooleanSupplier isSupported) { this.engineFactory = engineFactory; + this.priority = priority; this.isSupported = isSupported; } @@ -31,7 +34,7 @@ public final class SimpleBackend implements Backend { @Override public int priority() { - return priority.get(); + return priority.getAsInt(); } @Override @@ -39,13 +42,11 @@ public final class SimpleBackend implements Backend { return isSupported.getAsBoolean(); } - public interface PriorityProvider { - int get(); - } - public static final class Builder { + @Nullable private Function engineFactory; - private PriorityProvider priority = () -> 0; + private IntSupplier priority = () -> 0; + @Nullable private BooleanSupplier isSupported; public Builder engineFactory(Function engineFactory) { @@ -57,7 +58,7 @@ public final class SimpleBackend implements Backend { return priority(() -> priority); } - public Builder priority(PriorityProvider priority) { + public Builder priority(IntSupplier priority) { this.priority = priority; return this; } @@ -71,7 +72,7 @@ public final class SimpleBackend implements Backend { Objects.requireNonNull(engineFactory); Objects.requireNonNull(isSupported); - return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(priority, engineFactory, isSupported)); + return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(engineFactory, priority, isSupported)); } } } diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/instance/InstanceTypes.java b/common/src/lib/java/dev/engine_room/flywheel/lib/instance/InstanceTypes.java index da093b634..9e9152e1a 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/instance/InstanceTypes.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/instance/InstanceTypes.java @@ -2,12 +2,12 @@ package dev.engine_room.flywheel.lib.instance; import org.lwjgl.system.MemoryUtil; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.layout.FloatRepr; import dev.engine_room.flywheel.api.layout.IntegerRepr; import dev.engine_room.flywheel.api.layout.LayoutBuilder; import dev.engine_room.flywheel.lib.util.ExtraMemoryOps; +import dev.engine_room.flywheel.lib.util.ResourceUtil; public final class InstanceTypes { public static final InstanceType TRANSFORMED = SimpleInstanceType.builder(TransformedInstance::new) @@ -26,8 +26,8 @@ public final class InstanceTypes { ExtraMemoryOps.put2x16(ptr + 8, instance.light); ExtraMemoryOps.putMatrix4f(ptr + 12, instance.pose); }) - .vertexShader(Flywheel.rl("instance/transformed.vert")) - .cullShader(Flywheel.rl("instance/cull/transformed.glsl")) + .vertexShader(ResourceUtil.rl("instance/transformed.vert")) + .cullShader(ResourceUtil.rl("instance/cull/transformed.glsl")) .build(); public static final InstanceType POSED = SimpleInstanceType.builder(PosedInstance::new) @@ -48,8 +48,8 @@ public final class InstanceTypes { ExtraMemoryOps.putMatrix4f(ptr + 12, instance.pose); ExtraMemoryOps.putMatrix3f(ptr + 76, instance.normal); }) - .vertexShader(Flywheel.rl("instance/posed.vert")) - .cullShader(Flywheel.rl("instance/cull/posed.glsl")) + .vertexShader(ResourceUtil.rl("instance/posed.vert")) + .cullShader(ResourceUtil.rl("instance/cull/posed.glsl")) .build(); public static final InstanceType ORIENTED = SimpleInstanceType.builder(OrientedInstance::new) @@ -76,8 +76,8 @@ public final class InstanceTypes { MemoryUtil.memPutFloat(ptr + 32, instance.pivotZ); ExtraMemoryOps.putQuaternionf(ptr + 36, instance.rotation); }) - .vertexShader(Flywheel.rl("instance/oriented.vert")) - .cullShader(Flywheel.rl("instance/cull/oriented.glsl")) + .vertexShader(ResourceUtil.rl("instance/oriented.vert")) + .cullShader(ResourceUtil.rl("instance/cull/oriented.glsl")) .build(); public static final InstanceType SHADOW = SimpleInstanceType.builder(ShadowInstance::new) @@ -99,8 +99,8 @@ public final class InstanceTypes { MemoryUtil.memPutFloat(ptr + 28, instance.alpha); MemoryUtil.memPutFloat(ptr + 32, instance.radius); }) - .vertexShader(Flywheel.rl("instance/shadow.vert")) - .cullShader(Flywheel.rl("instance/cull/shadow.glsl")) + .vertexShader(ResourceUtil.rl("instance/shadow.vert")) + .cullShader(ResourceUtil.rl("instance/cull/shadow.glsl")) .build(); private InstanceTypes() { diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/material/CutoutShaders.java b/common/src/lib/java/dev/engine_room/flywheel/lib/material/CutoutShaders.java index 24383863b..0b107c39c 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/material/CutoutShaders.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/material/CutoutShaders.java @@ -1,25 +1,25 @@ package dev.engine_room.flywheel.lib.material; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.material.CutoutShader; +import dev.engine_room.flywheel.lib.util.ResourceUtil; public final class CutoutShaders { /** * Do not discard any fragments based on alpha. */ - public static final CutoutShader OFF = new SimpleCutoutShader(Flywheel.rl("cutout/off.glsl")); + public static final CutoutShader OFF = new SimpleCutoutShader(ResourceUtil.rl("cutout/off.glsl")); /** * Discard fragments with alpha close to or equal to zero. */ - public static final CutoutShader EPSILON = new SimpleCutoutShader(Flywheel.rl("cutout/epsilon.glsl")); + public static final CutoutShader EPSILON = new SimpleCutoutShader(ResourceUtil.rl("cutout/epsilon.glsl")); /** * Discard fragments with alpha less than to 0.1. */ - public static final CutoutShader ONE_TENTH = new SimpleCutoutShader(Flywheel.rl("cutout/one_tenth.glsl")); + public static final CutoutShader ONE_TENTH = new SimpleCutoutShader(ResourceUtil.rl("cutout/one_tenth.glsl")); /** * Discard fragments with alpha less than to 0.5. */ - public static final CutoutShader HALF = new SimpleCutoutShader(Flywheel.rl("cutout/half.glsl")); + public static final CutoutShader HALF = new SimpleCutoutShader(ResourceUtil.rl("cutout/half.glsl")); private CutoutShaders() { } diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/material/FogShaders.java b/common/src/lib/java/dev/engine_room/flywheel/lib/material/FogShaders.java index e6698e2d6..39f903c47 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/material/FogShaders.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/material/FogShaders.java @@ -1,12 +1,12 @@ package dev.engine_room.flywheel.lib.material; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.material.FogShader; +import dev.engine_room.flywheel.lib.util.ResourceUtil; public final class FogShaders { - public static final FogShader NONE = new SimpleFogShader(Flywheel.rl("fog/none.glsl")); - public static final FogShader LINEAR = new SimpleFogShader(Flywheel.rl("fog/linear.glsl")); - public static final FogShader LINEAR_FADE = new SimpleFogShader(Flywheel.rl("fog/linear_fade.glsl")); + public static final FogShader NONE = new SimpleFogShader(ResourceUtil.rl("fog/none.glsl")); + public static final FogShader LINEAR = new SimpleFogShader(ResourceUtil.rl("fog/linear.glsl")); + public static final FogShader LINEAR_FADE = new SimpleFogShader(ResourceUtil.rl("fog/linear_fade.glsl")); private FogShaders() { } diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/material/LightShaders.java b/common/src/lib/java/dev/engine_room/flywheel/lib/material/LightShaders.java index ac9e351cc..f9f373d64 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/material/LightShaders.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/material/LightShaders.java @@ -1,12 +1,12 @@ package dev.engine_room.flywheel.lib.material; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.material.LightShader; +import dev.engine_room.flywheel.lib.util.ResourceUtil; public final class LightShaders { - public static final LightShader SMOOTH_WHEN_EMBEDDED = new SimpleLightShader(Flywheel.rl("light/smooth_when_embedded.glsl")); - public static final LightShader SMOOTH = new SimpleLightShader(Flywheel.rl("light/smooth.glsl")); - public static final LightShader FLAT = new SimpleLightShader(Flywheel.rl("light/flat.glsl")); + public static final LightShader SMOOTH_WHEN_EMBEDDED = new SimpleLightShader(ResourceUtil.rl("light/smooth_when_embedded.glsl")); + public static final LightShader SMOOTH = new SimpleLightShader(ResourceUtil.rl("light/smooth.glsl")); + public static final LightShader FLAT = new SimpleLightShader(ResourceUtil.rl("light/flat.glsl")); private LightShaders() { } diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/material/StandardMaterialShaders.java b/common/src/lib/java/dev/engine_room/flywheel/lib/material/StandardMaterialShaders.java index 5aef506bf..6892adb00 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/material/StandardMaterialShaders.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/material/StandardMaterialShaders.java @@ -1,17 +1,17 @@ package dev.engine_room.flywheel.lib.material; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.material.MaterialShaders; +import dev.engine_room.flywheel.lib.util.ResourceUtil; public final class StandardMaterialShaders { public static final MaterialShaders DEFAULT = new SimpleMaterialShaders( - Flywheel.rl("material/default.vert"), Flywheel.rl("material/default.frag")); + ResourceUtil.rl("material/default.vert"), ResourceUtil.rl("material/default.frag")); - public static final MaterialShaders WIREFRAME = new SimpleMaterialShaders(Flywheel.rl("material/wireframe.vert"), Flywheel.rl("material/wireframe.frag")); + public static final MaterialShaders WIREFRAME = new SimpleMaterialShaders(ResourceUtil.rl("material/wireframe.vert"), ResourceUtil.rl("material/wireframe.frag")); - public static final MaterialShaders LINE = new SimpleMaterialShaders(Flywheel.rl("material/lines.vert"), Flywheel.rl("material/lines.frag")); + public static final MaterialShaders LINE = new SimpleMaterialShaders(ResourceUtil.rl("material/lines.vert"), ResourceUtil.rl("material/lines.frag")); - public static final MaterialShaders GLINT = new SimpleMaterialShaders(Flywheel.rl("material/glint.vert"), Flywheel.rl("material/default.frag")); + public static final MaterialShaders GLINT = new SimpleMaterialShaders(ResourceUtil.rl("material/glint.vert"), ResourceUtil.rl("material/default.frag")); private StandardMaterialShaders() { } diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/model/EmptyModel.java b/common/src/lib/java/dev/engine_room/flywheel/lib/model/EmptyModel.java new file mode 100644 index 000000000..fd38a686f --- /dev/null +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/model/EmptyModel.java @@ -0,0 +1,24 @@ +package dev.engine_room.flywheel.lib.model; + +import java.util.Collections; +import java.util.List; + +import org.joml.Vector4f; +import org.joml.Vector4fc; + +import dev.engine_room.flywheel.api.model.Model; + +public final class EmptyModel implements Model { + private static final Vector4fc BOUNDING_SPHERE = new Vector4f(0, 0, 0, 0); + public static final EmptyModel INSTANCE = new EmptyModel(); + + @Override + public List meshes() { + return Collections.emptyList(); + } + + @Override + public Vector4fc boundingSphere() { + return BOUNDING_SPHERE; + } +} diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/model/LineModelBuilder.java b/common/src/lib/java/dev/engine_room/flywheel/lib/model/LineModelBuilder.java index 0689e8097..97bdb8440 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/model/LineModelBuilder.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/model/LineModelBuilder.java @@ -1,7 +1,6 @@ package dev.engine_room.flywheel.lib.model; import org.jetbrains.annotations.UnknownNullability; -import org.joml.Vector4f; import org.joml.Vector4fc; import org.lwjgl.system.MemoryUtil; @@ -32,14 +31,37 @@ public final class LineModelBuilder { private MemoryBlock data; private int vertexCount = 0; - public LineModelBuilder(int segmentCount) { - vertexView = new FullVertexView(); - data = MemoryBlock.mallocTracked(segmentCount * 4 * vertexView.stride()); - vertexView.ptr(data.ptr()); + public LineModelBuilder() { + } + + public LineModelBuilder(int initialSegmentCount) { + ensureCapacity(initialSegmentCount); + } + + public void ensureCapacity(int segmentCount) { + if (segmentCount < 0) { + throw new IllegalArgumentException("Segment count must be greater than or equal to 0"); + } else if (segmentCount == 0) { + return; + } + + if (data == null) { + vertexView = new FullVertexView(); + data = MemoryBlock.mallocTracked(segmentCount * 4 * vertexView.stride()); + vertexView.ptr(data.ptr()); + vertexCount = 0; + } else { + long requiredCapacity = (vertexCount + segmentCount * 4) * vertexView.stride(); + + if (requiredCapacity > data.size()) { + data = data.realloc(requiredCapacity); + vertexView.ptr(data.ptr()); + } + } } public LineModelBuilder line(float x1, float y1, float z1, float x2, float y2, float z2) { - ensureCapacity(vertexCount + 4); + ensureCapacity(1); // We'll use the normal to figure out the orientation of the line in the vertex shader. float dx = x2 - x1; @@ -79,8 +101,19 @@ public final class LineModelBuilder { } public Model build() { - vertexView.vertexCount(vertexCount); + if (vertexCount == 0) { + return EmptyModel.INSTANCE; + } + + long requiredCapacity = vertexCount * vertexView.stride(); + + if (data.size() > requiredCapacity) { + data = data.realloc(requiredCapacity); + vertexView.ptr(data.ptr()); + } + vertexView.nativeMemoryOwner(data); + vertexView.vertexCount(vertexCount); var boundingSphere = ModelUtil.computeBoundingSphere(vertexView); boundingSphere.w += 0.1f; // make the bounding sphere a little bigger to account for line width @@ -94,17 +127,6 @@ public final class LineModelBuilder { return model; } - private void ensureCapacity(int vertexCount) { - if (data == null) { - vertexView = new FullVertexView(); - data = MemoryBlock.mallocTracked(vertexCount * vertexView.stride()); - vertexView.ptr(data.ptr()); - } else if (vertexCount * vertexView.stride() > data.size()) { - data = data.realloc(vertexCount * vertexView.stride()); - vertexView.ptr(data.ptr()); - } - } - private static class LineMesh implements Mesh { private static final IndexSequence INDEX_SEQUENCE = (ptr, count) -> { int numVertices = 2 * count / 3; @@ -124,9 +146,9 @@ public final class LineModelBuilder { } }; private final VertexList vertexList; - private final Vector4f boundingSphere; + private final Vector4fc boundingSphere; - public LineMesh(VertexList vertexList, Vector4f boundingSphere) { + public LineMesh(VertexList vertexList, Vector4fc boundingSphere) { this.vertexList = vertexList; this.boundingSphere = boundingSphere; } diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/util/ResourceUtil.java b/common/src/lib/java/dev/engine_room/flywheel/lib/util/ResourceUtil.java index 8aca2b9be..b3dfdad25 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/util/ResourceUtil.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/util/ResourceUtil.java @@ -15,6 +15,10 @@ public final class ResourceUtil { private ResourceUtil() { } + public static ResourceLocation rl(String path) { + return new ResourceLocation(Flywheel.ID, path); + } + /** * Same as {@link ResourceLocation#ResourceLocation(String)}, but defaults to Flywheel namespace. */ diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/BackendManagerImpl.java b/common/src/main/java/dev/engine_room/flywheel/impl/BackendManagerImpl.java index f88e5fb74..df4262e5a 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/BackendManagerImpl.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/BackendManagerImpl.java @@ -2,10 +2,10 @@ package dev.engine_room.flywheel.impl; import java.util.ArrayList; -import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.backend.Backend; import dev.engine_room.flywheel.impl.visualization.VisualizationManagerImpl; import dev.engine_room.flywheel.lib.backend.SimpleBackend; +import dev.engine_room.flywheel.lib.util.ResourceUtil; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.resources.ResourceLocation; @@ -15,7 +15,7 @@ public final class BackendManagerImpl { throw new UnsupportedOperationException("Cannot create engine when backend is off."); }) .supported(() -> true) - .register(Flywheel.rl("off")); + .register(ResourceUtil.rl("off")); private static Backend backend = OFF_BACKEND; diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/event/RenderContextImpl.java b/common/src/main/java/dev/engine_room/flywheel/impl/event/RenderContextImpl.java index 1ee538a73..546d1f93a 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/event/RenderContextImpl.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/event/RenderContextImpl.java @@ -5,7 +5,7 @@ import org.joml.Matrix4fc; import com.mojang.blaze3d.vertex.PoseStack; -import dev.engine_room.flywheel.api.RenderContext; +import dev.engine_room.flywheel.api.backend.RenderContext; import net.minecraft.client.Camera; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.LevelRenderer; diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java index 5eeadf043..e5b39b1d6 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/VisualizationManagerImpl.java @@ -9,9 +9,9 @@ import org.jetbrains.annotations.Nullable; import org.joml.FrustumIntersection; import org.joml.Matrix4f; -import dev.engine_room.flywheel.api.RenderContext; import dev.engine_room.flywheel.api.backend.BackendManager; import dev.engine_room.flywheel.api.backend.Engine; +import dev.engine_room.flywheel.api.backend.RenderContext; import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.task.Plan; import dev.engine_room.flywheel.api.visual.DynamicVisual; diff --git a/common/src/test/java/dev/engine_room/flywheel/backend/glsl/TestBase.java b/common/src/test/java/dev/engine_room/flywheel/backend/glsl/TestBase.java index 5c159daf8..1e2860eb1 100644 --- a/common/src/test/java/dev/engine_room/flywheel/backend/glsl/TestBase.java +++ b/common/src/test/java/dev/engine_room/flywheel/backend/glsl/TestBase.java @@ -6,13 +6,13 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.List; -import dev.engine_room.flywheel.api.Flywheel; +import dev.engine_room.flywheel.lib.util.ResourceUtil; import net.minecraft.resources.ResourceLocation; public class TestBase { - public static final ResourceLocation FLW_A = Flywheel.rl("a.glsl"); - public static final ResourceLocation FLW_B = Flywheel.rl("b.glsl"); - public static final ResourceLocation FLW_C = Flywheel.rl("c.glsl"); + public static final ResourceLocation FLW_A = ResourceUtil.rl("a.glsl"); + public static final ResourceLocation FLW_B = ResourceUtil.rl("b.glsl"); + public static final ResourceLocation FLW_C = ResourceUtil.rl("c.glsl"); public static T assertSingletonList(List list) { assertEquals(1, list.size()); diff --git a/fabric/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java b/fabric/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java index 1422ac19c..98f48b4bf 100644 --- a/fabric/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java +++ b/fabric/src/backend/java/dev/engine_room/flywheel/backend/compile/FlwProgramsReloader.java @@ -1,6 +1,6 @@ package dev.engine_room.flywheel.backend.compile; -import dev.engine_room.flywheel.api.Flywheel; +import dev.engine_room.flywheel.lib.util.ResourceUtil; import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceManager; @@ -8,7 +8,7 @@ import net.minecraft.server.packs.resources.ResourceManager; public final class FlwProgramsReloader implements SimpleSynchronousResourceReloadListener { public static final FlwProgramsReloader INSTANCE = new FlwProgramsReloader(); - public static final ResourceLocation ID = Flywheel.rl("programs"); + public static final ResourceLocation ID = ResourceUtil.rl("programs"); private FlwProgramsReloader() { } diff --git a/fabric/src/lib/java/dev/engine_room/flywheel/lib/model/baked/MeshEmitter.java b/fabric/src/lib/java/dev/engine_room/flywheel/lib/model/baked/MeshEmitter.java index 268a6e1da..13bdfc521 100644 --- a/fabric/src/lib/java/dev/engine_room/flywheel/lib/model/baked/MeshEmitter.java +++ b/fabric/src/lib/java/dev/engine_room/flywheel/lib/model/baked/MeshEmitter.java @@ -36,7 +36,7 @@ class MeshEmitter { return bufferBuilder; } - void prepareForGeometry(boolean shade) { + private void prepareForGeometry(boolean shade) { if (!bufferBuilder.building()) { bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); } else if (shade != currentShade) { @@ -47,7 +47,7 @@ class MeshEmitter { currentShade = shade; } - void emit() { + private void emit() { var renderedBuffer = bufferBuilder.endOrDiscardIfEmpty(); if (renderedBuffer != null) { diff --git a/fabric/src/lib/java/dev/engine_room/flywheel/lib/model/baked/PartialModelEventHandler.java b/fabric/src/lib/java/dev/engine_room/flywheel/lib/model/baked/PartialModelEventHandler.java index 5b0bc5203..62fb40ac3 100644 --- a/fabric/src/lib/java/dev/engine_room/flywheel/lib/model/baked/PartialModelEventHandler.java +++ b/fabric/src/lib/java/dev/engine_room/flywheel/lib/model/baked/PartialModelEventHandler.java @@ -4,7 +4,7 @@ import java.util.List; import org.jetbrains.annotations.ApiStatus; -import dev.engine_room.flywheel.api.Flywheel; +import dev.engine_room.flywheel.lib.util.ResourceUtil; import net.fabricmc.fabric.api.resource.ResourceReloadListenerKeys; import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; import net.minecraft.client.Minecraft; @@ -32,7 +32,7 @@ public final class PartialModelEventHandler { public static final class ReloadListener implements SimpleSynchronousResourceReloadListener { public static final ReloadListener INSTANCE = new ReloadListener(); - public static final ResourceLocation ID = Flywheel.rl("partial_models"); + public static final ResourceLocation ID = ResourceUtil.rl("partial_models"); public static final List DEPENDENCIES = List.of(ResourceReloadListenerKeys.MODELS); private ReloadListener() { diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java index f7ba7914b..4aa29fd23 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java @@ -11,6 +11,7 @@ import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler; import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler; import dev.engine_room.flywheel.lib.util.RendererReloadCache; import dev.engine_room.flywheel.lib.util.ResourceReloadHolder; +import dev.engine_room.flywheel.lib.util.ResourceUtil; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; @@ -60,9 +61,9 @@ public final class FlywheelFabric implements ClientModInitializer { EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) -> BackendManagerImpl.onEndClientResourceReload(error.isPresent())); - ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("backend"), BackendArgument.class, BackendArgument.INFO); - ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("debug_mode"), DebugModeArgument.class, DebugModeArgument.INFO); - ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("light_smoothness"), LightSmoothnessArgument.class, LightSmoothnessArgument.INFO); + ArgumentTypeRegistry.registerArgumentType(ResourceUtil.rl("backend"), BackendArgument.class, BackendArgument.INFO); + ArgumentTypeRegistry.registerArgumentType(ResourceUtil.rl("debug_mode"), DebugModeArgument.class, DebugModeArgument.INFO); + ArgumentTypeRegistry.registerArgumentType(ResourceUtil.rl("light_smoothness"), LightSmoothnessArgument.class, LightSmoothnessArgument.INFO); } private static void setupLib() { diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java b/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java index f91023871..b6f5bd75b 100644 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java @@ -14,6 +14,7 @@ import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler; import dev.engine_room.flywheel.lib.util.LevelAttached; import dev.engine_room.flywheel.lib.util.RendererReloadCache; import dev.engine_room.flywheel.lib.util.ResourceReloadHolder; +import dev.engine_room.flywheel.lib.util.ResourceUtil; import net.minecraft.client.Minecraft; import net.minecraft.commands.synchronization.ArgumentTypeInfos; import net.minecraftforge.api.distmarker.Dist; @@ -101,9 +102,9 @@ public final class FlywheelForge { }); modEventBus.addListener((RegisterEvent e) -> { if (e.getRegistryKey().equals(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES)) { - e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, Flywheel.rl("backend"), () -> BackendArgument.INFO); - e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, Flywheel.rl("debug_mode"), () -> DebugModeArgument.INFO); - e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, Flywheel.rl("light_smoothness"), () -> LightSmoothnessArgument.INFO); + e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, ResourceUtil.rl("backend"), () -> BackendArgument.INFO); + e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, ResourceUtil.rl("debug_mode"), () -> DebugModeArgument.INFO); + e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, ResourceUtil.rl("light_smoothness"), () -> LightSmoothnessArgument.INFO); } }); } From 5fc07a751323335347ea65cc164855fa91c355b4 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 23 Feb 2025 16:21:04 -0800 Subject: [PATCH 18/23] Direct or indirect, that is the question - Add non-dsa paths for the OitFramebuffer, behind a gl capabilities check --- .../engine/indirect/IndirectDrawManager.java | 2 +- .../engine/indirect/OitFramebuffer.java | 162 +++++++++++++----- .../flywheel/backend/gl/GlCompat.java | 10 ++ 3 files changed, 126 insertions(+), 48 deletions(-) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index 262da1c2e..1e38fedbd 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -179,7 +179,7 @@ public class IndirectDrawManager extends DrawManager> { // Need to bind this again because we just drew a full screen quad for OIT. vertexArray.bindForDraw(); - oitFramebuffer.shade(); + oitFramebuffer.accumulate(); for (var group : cullingGroups.values()) { group.submitTransparent(PipelineCompiler.OitMode.EVALUATE); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java index 40916c3b0..d184acf02 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java @@ -10,10 +10,17 @@ import com.mojang.blaze3d.systems.RenderSystem; import dev.engine_room.flywheel.backend.NoiseTextures; import dev.engine_room.flywheel.backend.Samplers; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; +import dev.engine_room.flywheel.backend.gl.GlCompat; import dev.engine_room.flywheel.backend.gl.GlTextureUnit; import net.minecraft.client.Minecraft; public class OitFramebuffer { + public static final float[] CLEAR_TO_ZERO = {0, 0, 0, 0}; + public static final int[] DEPTH_RANGE_DRAW_BUFFERS = {GL46.GL_COLOR_ATTACHMENT0}; + public static final int[] RENDER_TRANSMITTANCE_DRAW_BUFFERS = {GL46.GL_COLOR_ATTACHMENT1, GL46.GL_COLOR_ATTACHMENT2, GL46.GL_COLOR_ATTACHMENT3, GL46.GL_COLOR_ATTACHMENT4}; + public static final int[] ACCUMULATE_DRAW_BUFFERS = {GL46.GL_COLOR_ATTACHMENT5}; + public static final int[] DEPTH_ONLY_DRAW_BUFFERS = {}; + private final IndirectPrograms programs; private final int vao; @@ -27,7 +34,11 @@ public class OitFramebuffer { public OitFramebuffer(IndirectPrograms programs) { this.programs = programs; - vao = GL46.glCreateVertexArrays(); + if (GlCompat.SUPPORTS_DSA) { + vao = GL46.glCreateVertexArrays(); + } else { + vao = GL32.glGenVertexArrays(); + } } /** @@ -48,11 +59,10 @@ public class OitFramebuffer { maybeResizeFBO(renderTarget.width, renderTarget.height); - GL46.glNamedFramebufferTexture(fbo, GL46.GL_DEPTH_ATTACHMENT, renderTarget.getDepthTextureId(), 0); - Samplers.COEFFICIENTS.makeActive(); + // Bind zero to render system to make sure we clear their internal state RenderSystem.bindTexture(0); - GL46.glBindTextureUnit(Samplers.COEFFICIENTS.number, coefficients); + GL32.glBindTexture(GL32.GL_TEXTURE_2D_ARRAY, coefficients); Samplers.DEPTH_RANGE.makeActive(); RenderSystem.bindTexture(depthBounds); @@ -60,7 +70,8 @@ public class OitFramebuffer { Samplers.NOISE.makeActive(); NoiseTextures.BLUE_NOISE.bind(); - GlStateManager._glBindFramebuffer(GL46.GL_FRAMEBUFFER, fbo); + GlStateManager._glBindFramebuffer(GL32.GL_FRAMEBUFFER, fbo); + GL32.glFramebufferTexture(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, renderTarget.getDepthTextureId(), 0); } /** @@ -72,13 +83,18 @@ public class OitFramebuffer { RenderSystem.colorMask(true, true, true, true); RenderSystem.enableBlend(); RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); - RenderSystem.blendEquation(GL46.GL_MAX); - - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT0}); + RenderSystem.blendEquation(GL32.GL_MAX); var far = Minecraft.getInstance().gameRenderer.getDepthFar(); - GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{-far, -far, 0, 0}); + if (GlCompat.SUPPORTS_DSA) { + GL46.glNamedFramebufferDrawBuffers(fbo, DEPTH_RANGE_DRAW_BUFFERS); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{-far, -far, 0, 0}); + } else { + GL32.glDrawBuffers(DEPTH_RANGE_DRAW_BUFFERS); + RenderSystem.clearColor(-far, -far, 0, 0); + RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT, false); + } } /** @@ -90,14 +106,20 @@ public class OitFramebuffer { RenderSystem.colorMask(true, true, true, true); RenderSystem.enableBlend(); RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); - RenderSystem.blendEquation(GL46.GL_FUNC_ADD); + RenderSystem.blendEquation(GL32.GL_FUNC_ADD); - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT1, GL46.GL_COLOR_ATTACHMENT2, GL46.GL_COLOR_ATTACHMENT3, GL46.GL_COLOR_ATTACHMENT4}); + if (GlCompat.SUPPORTS_DSA) { + GL46.glNamedFramebufferDrawBuffers(fbo, RENDER_TRANSMITTANCE_DRAW_BUFFERS); - GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); - GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 1, new float[]{0, 0, 0, 0}); - GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 2, new float[]{0, 0, 0, 0}); - GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 3, new float[]{0, 0, 0, 0}); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, CLEAR_TO_ZERO); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 1, CLEAR_TO_ZERO); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 2, CLEAR_TO_ZERO); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 3, CLEAR_TO_ZERO); + } else { + GL32.glDrawBuffers(RENDER_TRANSMITTANCE_DRAW_BUFFERS); + RenderSystem.clearColor(0, 0, 0, 0); + RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT, false); + } } /** @@ -111,7 +133,11 @@ public class OitFramebuffer { RenderSystem.disableBlend(); RenderSystem.depthFunc(GL32.GL_ALWAYS); - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{}); + if (GlCompat.SUPPORTS_DSA) { + GL46.glNamedFramebufferDrawBuffers(fbo, DEPTH_ONLY_DRAW_BUFFERS); + } else { + GL32.glDrawBuffers(DEPTH_ONLY_DRAW_BUFFERS); + } programs.getOitDepthProgram() .bind(); @@ -122,17 +148,23 @@ public class OitFramebuffer { /** * Sample the transmittance function and accumulate. */ - public void shade() { + public void accumulate() { // No depth writes, but we'll still use the depth test RenderSystem.depthMask(false); RenderSystem.colorMask(true, true, true, true); RenderSystem.enableBlend(); RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE); - RenderSystem.blendEquation(GL46.GL_FUNC_ADD); + RenderSystem.blendEquation(GL32.GL_FUNC_ADD); - GL46.glNamedFramebufferDrawBuffers(fbo, new int[]{GL46.GL_COLOR_ATTACHMENT5}); + if (GlCompat.SUPPORTS_DSA) { + GL46.glNamedFramebufferDrawBuffers(fbo, ACCUMULATE_DRAW_BUFFERS); - GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{0, 0, 0, 0}); + GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, CLEAR_TO_ZERO); + } else { + GL32.glDrawBuffers(ACCUMULATE_DRAW_BUFFERS); + RenderSystem.clearColor(0, 0, 0, 0); + RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT, false); + } } /** @@ -163,7 +195,7 @@ public class OitFramebuffer { // Though note that the alpha value we emit in the fragment shader is actually (1. - transmittance_total). // The extra inversion step is so we can have a sane alpha value written out for the fabulous blit shader to consume. RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - RenderSystem.blendEquation(GL46.GL_FUNC_ADD); + RenderSystem.blendEquation(GL32.GL_FUNC_ADD); RenderSystem.depthFunc(GL32.GL_ALWAYS); GlTextureUnit.T0.makeActive(); @@ -181,28 +213,28 @@ public class OitFramebuffer { public void delete() { deleteTextures(); - GL46.glDeleteVertexArrays(vao); + GL32.glDeleteVertexArrays(vao); } private void drawFullscreenQuad() { // Empty VAO, the actual full screen triangle is generated in the vertex shader GlStateManager._glBindVertexArray(vao); - GL46.glDrawArrays(GL46.GL_TRIANGLES, 0, 3); + GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 3); } private void deleteTextures() { if (depthBounds != -1) { - GL46.glDeleteTextures(depthBounds); + GL32.glDeleteTextures(depthBounds); } if (coefficients != -1) { - GL46.glDeleteTextures(coefficients); + GL32.glDeleteTextures(coefficients); } if (accumulate != -1) { - GL46.glDeleteTextures(accumulate); + GL32.glDeleteTextures(accumulate); } if (fbo != -1) { - GL46.glDeleteFramebuffers(fbo); + GL32.glDeleteFramebuffers(fbo); } // We sometimes get the same texture ID back when creating new textures, @@ -223,29 +255,65 @@ public class OitFramebuffer { deleteTextures(); - fbo = GL46.glCreateFramebuffers(); + if (GlCompat.SUPPORTS_DSA) { + fbo = GL46.glCreateFramebuffers(); - depthBounds = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); - coefficients = GL46.glCreateTextures(GL46.GL_TEXTURE_2D_ARRAY); - accumulate = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); + depthBounds = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); + coefficients = GL46.glCreateTextures(GL46.GL_TEXTURE_2D_ARRAY); + accumulate = GL46.glCreateTextures(GL46.GL_TEXTURE_2D); - GL46.glTextureStorage2D(depthBounds, 1, GL32.GL_RG32F, width, height); - GL46.glTextureStorage3D(coefficients, 1, GL32.GL_RGBA16F, width, height, 4); - GL46.glTextureStorage2D(accumulate, 1, GL32.GL_RGBA16F, width, height); + GL46.glTextureStorage2D(depthBounds, 1, GL32.GL_RG32F, width, height); + GL46.glTextureStorage3D(coefficients, 1, GL32.GL_RGBA16F, width, height, 4); + GL46.glTextureStorage2D(accumulate, 1, GL32.GL_RGBA16F, width, height); - // for (int tex : new int[]{zerothMoment, moments, composite}) { - // GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST); - // GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST); - // GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_COMPARE_MODE, GL32.GL_NONE); - // GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE); - // GL46.glTextureParameteri(tex, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE); - // } + GL46.glNamedFramebufferTexture(fbo, GL32.GL_COLOR_ATTACHMENT0, depthBounds, 0); + GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT1, coefficients, 0, 0); + GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT2, coefficients, 0, 1); + GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT3, coefficients, 0, 2); + GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT4, coefficients, 0, 3); + GL46.glNamedFramebufferTexture(fbo, GL32.GL_COLOR_ATTACHMENT5, accumulate, 0); + } else { + fbo = GL46.glGenFramebuffers(); - GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT0, depthBounds, 0); - GL46.glNamedFramebufferTextureLayer(fbo, GL46.GL_COLOR_ATTACHMENT1, coefficients, 0, 0); - GL46.glNamedFramebufferTextureLayer(fbo, GL46.GL_COLOR_ATTACHMENT2, coefficients, 0, 1); - GL46.glNamedFramebufferTextureLayer(fbo, GL46.GL_COLOR_ATTACHMENT3, coefficients, 0, 2); - GL46.glNamedFramebufferTextureLayer(fbo, GL46.GL_COLOR_ATTACHMENT4, coefficients, 0, 3); - GL46.glNamedFramebufferTexture(fbo, GL46.GL_COLOR_ATTACHMENT5, accumulate, 0); + depthBounds = GL32.glGenTextures(); + coefficients = GL32.glGenTextures(); + accumulate = GL32.glGenTextures(); + + GlTextureUnit.T0.makeActive(); + RenderSystem.bindTexture(0); + + GL32.glBindTexture(GL32.GL_TEXTURE_2D, depthBounds); + GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RG32F, width, height, 0, GL46.GL_RGBA, GL46.GL_BYTE, 0); + + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE); + + GL32.glBindTexture(GL32.GL_TEXTURE_2D_ARRAY, coefficients); + GL32.glTexImage3D(GL32.GL_TEXTURE_2D_ARRAY, 0, GL32.GL_RGBA16F, width, height, 4, 0, GL46.GL_RGBA, GL46.GL_BYTE, 0); + + GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE); + + GL32.glBindTexture(GL32.GL_TEXTURE_2D, accumulate); + GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16F, width, height, 0, GL46.GL_RGBA, GL46.GL_BYTE, 0); + + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE); + + GlStateManager._glBindFramebuffer(GL32.GL_FRAMEBUFFER, fbo); + + GL46.glFramebufferTexture(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, depthBounds, 0); + GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT1, coefficients, 0, 0); + GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT2, coefficients, 0, 1); + GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT3, coefficients, 0, 2); + GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT4, coefficients, 0, 3); + GL46.glFramebufferTexture(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT5, accumulate, 0); + } } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java b/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java index b07070bc7..d93c72426 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java @@ -39,6 +39,8 @@ public final class GlCompat { public static final boolean ALLOW_DSA = true; public static final GlslVersion MAX_GLSL_VERSION = maxGlslVersion(); + public static final boolean SUPPORTS_DSA = ALLOW_DSA && isDsaSupported(); + public static final boolean SUPPORTS_INSTANCING = isInstancingSupported(); public static final boolean SUPPORTS_INDIRECT = isIndirectSupported(); @@ -165,6 +167,14 @@ public final class GlCompat { && CAPABILITIES.GL_ARB_vertex_attrib_binding; } + private static boolean isDsaSupported() { + if (CAPABILITIES == null) { + return false; + } + + return CAPABILITIES.GL_ARB_direct_state_access; + } + /** * Try to compile a shader with progressively lower glsl versions. * The first version to compile successfully is returned. From 8aa610648afebf47882d61cf01e5d65df085f3e6 Mon Sep 17 00:00:00 2001 From: PepperCode1 <44146161+PepperCode1@users.noreply.github.com> Date: Sun, 23 Feb 2025 16:30:34 -0800 Subject: [PATCH 19/23] Registration not necessary Do not register custom argument type infos to BuiltInRegistries.COMMAND_ARGUMENT_TYPE as it is a synced registry and Flywheel is client-side only. Doing so can cause errors when joining a dedicated server. --- .../flywheel/impl/FlywheelFabric.java | 12 +++++++----- .../mixin/fabric/ArgumentTypeInfosAccessor.java | 17 +++++++++++++++++ .../resources/flywheel.impl.fabric.mixins.json | 1 + .../flywheel/impl/FlywheelForge.java | 12 ++---------- 4 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/fabric/ArgumentTypeInfosAccessor.java diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java index 4aa29fd23..3a4ce1c91 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java @@ -7,17 +7,16 @@ import dev.engine_room.flywheel.api.event.EndClientResourceReloadCallback; import dev.engine_room.flywheel.api.event.ReloadLevelRendererCallback; import dev.engine_room.flywheel.backend.compile.FlwProgramsReloader; import dev.engine_room.flywheel.backend.engine.uniform.Uniforms; +import dev.engine_room.flywheel.impl.mixin.fabric.ArgumentTypeInfosAccessor; import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler; import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler; import dev.engine_room.flywheel.lib.util.RendererReloadCache; import dev.engine_room.flywheel.lib.util.ResourceReloadHolder; -import dev.engine_room.flywheel.lib.util.ResourceUtil; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin; -import net.fabricmc.fabric.api.command.v2.ArgumentTypeRegistry; import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; @@ -61,9 +60,12 @@ public final class FlywheelFabric implements ClientModInitializer { EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) -> BackendManagerImpl.onEndClientResourceReload(error.isPresent())); - ArgumentTypeRegistry.registerArgumentType(ResourceUtil.rl("backend"), BackendArgument.class, BackendArgument.INFO); - ArgumentTypeRegistry.registerArgumentType(ResourceUtil.rl("debug_mode"), DebugModeArgument.class, DebugModeArgument.INFO); - ArgumentTypeRegistry.registerArgumentType(ResourceUtil.rl("light_smoothness"), LightSmoothnessArgument.class, LightSmoothnessArgument.INFO); + // We can't use ArgumentTypeRegistry from Fabric API here as it also registers to BuiltInRegistries.COMMAND_ARGUMENT_TYPE. + // We can't register anything to BuiltInRegistries.COMMAND_ARGUMENT_TYPE because it is a synced registry but + // Flywheel is a client-side only mod. + ArgumentTypeInfosAccessor.getBY_CLASS().put(BackendArgument.class, BackendArgument.INFO); + ArgumentTypeInfosAccessor.getBY_CLASS().put(DebugModeArgument.class, DebugModeArgument.INFO); + ArgumentTypeInfosAccessor.getBY_CLASS().put(LightSmoothnessArgument.class, LightSmoothnessArgument.INFO); } private static void setupLib() { diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/fabric/ArgumentTypeInfosAccessor.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/fabric/ArgumentTypeInfosAccessor.java new file mode 100644 index 000000000..d471aea33 --- /dev/null +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/fabric/ArgumentTypeInfosAccessor.java @@ -0,0 +1,17 @@ +package dev.engine_room.flywheel.impl.mixin.fabric; + +import java.util.Map; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.commands.synchronization.ArgumentTypeInfo; +import net.minecraft.commands.synchronization.ArgumentTypeInfos; + +@Mixin(ArgumentTypeInfos.class) +public interface ArgumentTypeInfosAccessor { + @Accessor("BY_CLASS") + static Map, ArgumentTypeInfo> getBY_CLASS() { + throw new AssertionError(); + } +} diff --git a/fabric/src/main/resources/flywheel.impl.fabric.mixins.json b/fabric/src/main/resources/flywheel.impl.fabric.mixins.json index ea3900fdd..448a155ec 100644 --- a/fabric/src/main/resources/flywheel.impl.fabric.mixins.json +++ b/fabric/src/main/resources/flywheel.impl.fabric.mixins.json @@ -5,6 +5,7 @@ "compatibilityLevel": "JAVA_17", "refmap": "flywheel.refmap.json", "client": [ + "ArgumentTypeInfosAccessor", "DebugScreenOverlayMixin", "MinecraftMixin", "SystemReportMixin" diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java b/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java index b6f5bd75b..48ed3577e 100644 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java @@ -14,7 +14,6 @@ import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler; import dev.engine_room.flywheel.lib.util.LevelAttached; import dev.engine_room.flywheel.lib.util.RendererReloadCache; import dev.engine_room.flywheel.lib.util.ResourceReloadHolder; -import dev.engine_room.flywheel.lib.util.ResourceUtil; import net.minecraft.client.Minecraft; import net.minecraft.commands.synchronization.ArgumentTypeInfos; import net.minecraftforge.api.distmarker.Dist; @@ -33,8 +32,6 @@ import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import net.minecraftforge.registries.ForgeRegistries; -import net.minecraftforge.registries.RegisterEvent; @Mod(Flywheel.ID) public final class FlywheelForge { @@ -96,17 +93,12 @@ public final class FlywheelForge { modEventBus.addListener((EndClientResourceReloadEvent e) -> BackendManagerImpl.onEndClientResourceReload(e.error().isPresent())); modEventBus.addListener((FMLCommonSetupEvent e) -> { + // We can't register anything to BuiltInRegistries.COMMAND_ARGUMENT_TYPE because it is a synced registry but + // Flywheel is a client-side only mod. ArgumentTypeInfos.registerByClass(BackendArgument.class, BackendArgument.INFO); ArgumentTypeInfos.registerByClass(DebugModeArgument.class, DebugModeArgument.INFO); ArgumentTypeInfos.registerByClass(LightSmoothnessArgument.class, LightSmoothnessArgument.INFO); }); - modEventBus.addListener((RegisterEvent e) -> { - if (e.getRegistryKey().equals(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES)) { - e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, ResourceUtil.rl("backend"), () -> BackendArgument.INFO); - e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, ResourceUtil.rl("debug_mode"), () -> DebugModeArgument.INFO); - e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, ResourceUtil.rl("light_smoothness"), () -> LightSmoothnessArgument.INFO); - } - }); } private static void registerLibEventListeners(IEventBus forgeEventBus, IEventBus modEventBus) { From 71a5eba3b3aa8b2cf12a6176b1c76bdd9cc9f838 Mon Sep 17 00:00:00 2001 From: PepperCode1 <44146161+PepperCode1@users.noreply.github.com> Date: Sun, 23 Feb 2025 17:56:48 -0800 Subject: [PATCH 20/23] The choice is ours - Allow "DEFAULT" to be used in place of a backend ID in commands and configs to dynamically and non-persistently select the best backend --- .../engine_room/flywheel/impl/FlwConfig.java | 2 ++ .../flywheel/impl/FabricFlwConfig.java | 27 ++++++++++++------- .../flywheel/impl/FlwCommands.java | 16 +++++++++++ .../flywheel/impl/FlwCommands.java | 13 +++++++++ .../flywheel/impl/ForgeFlwConfig.java | 18 ++++++++----- 5 files changed, 60 insertions(+), 16 deletions(-) diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/FlwConfig.java b/common/src/main/java/dev/engine_room/flywheel/impl/FlwConfig.java index 760711c0f..7b632adcb 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/FlwConfig.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/FlwConfig.java @@ -4,6 +4,8 @@ import dev.engine_room.flywheel.api.backend.Backend; import dev.engine_room.flywheel.backend.BackendConfig; public interface FlwConfig { + String DEFAULT_BACKEND_STR = "DEFAULT"; + FlwConfig INSTANCE = FlwImplXplat.INSTANCE.getConfig(); Backend backend(); diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/FabricFlwConfig.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/FabricFlwConfig.java index a6f17f39a..9fb97c0d5 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/FabricFlwConfig.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/FabricFlwConfig.java @@ -38,8 +38,8 @@ public class FabricFlwConfig implements FlwConfig { private final File file; - // Don't actually default to off, we'll find the true default in #load public Backend backend = BackendManager.offBackend(); + public boolean useDefaultBackend = true; public boolean limitUpdates = LIMIT_UPDATES_DEFAULT; public int workerThreads = WORKER_THREADS_DEFAULT; @@ -51,6 +51,10 @@ public class FabricFlwConfig implements FlwConfig { @Override public Backend backend() { + if (useDefaultBackend) { + return BackendManager.defaultBackend(); + } + return backend; } @@ -70,10 +74,6 @@ public class FabricFlwConfig implements FlwConfig { } public void load() { - // Grab the default backend here because this object is constructed - // very early in flywheel loading and not all backends may be registered - backend = BackendManager.defaultBackend(); - if (file.exists()) { try (FileReader reader = new FileReader(file)) { fromJson(JsonParser.parseReader(reader)); @@ -96,7 +96,8 @@ public class FabricFlwConfig implements FlwConfig { public void fromJson(JsonElement json) { if (!(json instanceof JsonObject object)) { FlwImpl.CONFIG_LOGGER.warn("Config JSON must be an object"); - backend = BackendManager.defaultBackend(); + backend = BackendManager.offBackend(); + useDefaultBackend = true; limitUpdates = LIMIT_UPDATES_DEFAULT; workerThreads = WORKER_THREADS_DEFAULT; return; @@ -114,8 +115,15 @@ public class FabricFlwConfig implements FlwConfig { if (backendJson instanceof JsonPrimitive primitive && primitive.isString()) { var value = primitive.getAsString(); + if (value.equals(DEFAULT_BACKEND_STR)) { + backend = BackendManager.offBackend(); + useDefaultBackend = true; + return; + } + try { - this.backend = Backend.REGISTRY.getOrThrow(new ResourceLocation(value)); + backend = Backend.REGISTRY.getOrThrow(new ResourceLocation(value)); + useDefaultBackend = false; return; } catch (ResourceLocationException e) { msg = "'backend' value '" + value + "' is not a valid resource location"; @@ -133,7 +141,8 @@ public class FabricFlwConfig implements FlwConfig { if (msg != null) { FlwImpl.CONFIG_LOGGER.warn(msg); } - backend = BackendManager.defaultBackend(); + backend = BackendManager.offBackend(); + useDefaultBackend = true; } private void readLimitUpdates(JsonObject object) { @@ -181,7 +190,7 @@ public class FabricFlwConfig implements FlwConfig { public JsonObject toJson() { JsonObject object = new JsonObject(); - object.addProperty("backend", Backend.REGISTRY.getIdOrThrow(backend).toString()); + object.addProperty("backend", useDefaultBackend ? DEFAULT_BACKEND_STR : Backend.REGISTRY.getIdOrThrow(backend).toString()); object.addProperty("limitUpdates", limitUpdates); object.addProperty("workerThreads", workerThreads); object.add("flw_backends", backendConfig.toJson()); diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java index 1cee65f24..fa4a0ba1c 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java @@ -38,10 +38,26 @@ public final class FlwCommands { context.getSource().sendFeedback(Component.translatable("command.flywheel.backend.get", idStr)); return Command.SINGLE_SUCCESS; }) + .then(ClientCommandManager.literal("DEFAULT") + .executes(context -> { + FabricFlwConfig.INSTANCE.backend = BackendManager.offBackend(); + FabricFlwConfig.INSTANCE.useDefaultBackend = true; + FabricFlwConfig.INSTANCE.save(); + + // Reload renderers so we can report the actual backend. + Minecraft.getInstance().levelRenderer.allChanged(); + + Backend actualBackend = BackendManager.currentBackend(); + String actualIdStr = Backend.REGISTRY.getIdOrThrow(actualBackend) + .toString(); + context.getSource().sendFeedback(Component.translatable("command.flywheel.backend.set", actualIdStr)); + return Command.SINGLE_SUCCESS; + })) .then(ClientCommandManager.argument("id", BackendArgument.INSTANCE) .executes(context -> { Backend requestedBackend = context.getArgument("id", Backend.class); FabricFlwConfig.INSTANCE.backend = requestedBackend; + FabricFlwConfig.INSTANCE.useDefaultBackend = false; FabricFlwConfig.INSTANCE.save(); // Reload renderers so we can report the actual backend. diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java index 4dd8cd5a5..2bc7cbb53 100644 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java @@ -38,6 +38,19 @@ public final class FlwCommands { sendMessage(context.getSource(), Component.translatable("command.flywheel.backend.get", idStr)); return Command.SINGLE_SUCCESS; }) + .then(Commands.literal("DEFAULT") + .executes(context -> { + backendValue.set(FlwConfig.DEFAULT_BACKEND_STR); + + // Reload renderers so we can report the actual backend. + Minecraft.getInstance().levelRenderer.allChanged(); + + Backend actualBackend = BackendManager.currentBackend(); + String actualIdStr = Backend.REGISTRY.getIdOrThrow(actualBackend) + .toString(); + sendMessage(context.getSource(), Component.translatable("command.flywheel.backend.set", actualIdStr)); + return Command.SINGLE_SUCCESS; + })) .then(Commands.argument("id", BackendArgument.INSTANCE) .executes(context -> { Backend requestedBackend = context.getArgument("id", Backend.class); diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/ForgeFlwConfig.java b/forge/src/main/java/dev/engine_room/flywheel/impl/ForgeFlwConfig.java index 9f0cc4760..1207c63ac 100644 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/ForgeFlwConfig.java +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/ForgeFlwConfig.java @@ -29,20 +29,24 @@ public class ForgeFlwConfig implements FlwConfig { public Backend backend() { Backend backend = parseBackend(client.backend.get()); if (backend == null) { + client.backend.set(DEFAULT_BACKEND_STR); backend = BackendManager.defaultBackend(); - client.backend.set(Backend.REGISTRY.getIdOrThrow(backend).toString()); } return backend; } @Nullable - private static Backend parseBackend(String idStr) { + private static Backend parseBackend(String value) { + if (value.equals(DEFAULT_BACKEND_STR)) { + return BackendManager.defaultBackend(); + } + ResourceLocation backendId; try { - backendId = new ResourceLocation(idStr); + backendId = new ResourceLocation(value); } catch (ResourceLocationException e) { - FlwImpl.CONFIG_LOGGER.warn("'backend' value '{}' is not a valid resource location", idStr); + FlwImpl.CONFIG_LOGGER.warn("'backend' value '{}' is not a valid resource location", value); return null; } @@ -82,8 +86,8 @@ public class ForgeFlwConfig implements FlwConfig { public final ForgeBackendConfig backendConfig; private ClientConfig(ForgeConfigSpec.Builder builder) { - backend = builder.comment("Select the backend to use.") - .define("backend", () -> Backend.REGISTRY.getIdOrThrow(BackendManager.defaultBackend()).toString(), o -> o != null && String.class.isAssignableFrom(o.getClass())); + backend = builder.comment("Select the backend to use. Set to \"DEFAULT\" to let Flywheel decide.") + .define("backend", DEFAULT_BACKEND_STR); limitUpdates = builder.comment("Enable or disable instance update limiting with distance.") .define("limitUpdates", true); @@ -103,7 +107,7 @@ public class ForgeFlwConfig implements FlwConfig { public final ForgeConfigSpec.EnumValue lightSmoothness; public ForgeBackendConfig(ForgeConfigSpec.Builder builder) { - lightSmoothness = builder.comment("How smooth flywheel's shader-based lighting should be. May have a large performance impact.") + lightSmoothness = builder.comment("How smooth Flywheel's shader-based lighting should be. May have a large performance impact.") .defineEnum("lightSmoothness", LightSmoothness.SMOOTH); } From 6af08f542634a215cbca571ab419420ee45de1d2 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 23 Feb 2025 18:31:23 -0800 Subject: [PATCH 21/23] Transparency, for instance - Get wavelet oit working on instancing - Fix shaders not compiling on glsl 150 - Add missing extensions for indirect - Fix implicit int -> uint casts - Explicitly bind new sampler locations on the java side - Ensure FMA is always available, even if only defined through a hack - Inline a lot of the instanced draw manager to make it easier to sort draws and separate oit draws - Move oit full screen pass programs to a separate class, shared by instancing and indirect --- .../backend/compile/IndirectPrograms.java | 36 ++--- .../backend/compile/InstancingPrograms.java | 17 ++- .../flywheel/backend/compile/OitPrograms.java | 68 ++++++++++ .../backend/compile/PipelineCompiler.java | 16 +++ .../engine/indirect/IndirectDrawManager.java | 2 +- .../engine/indirect/OitFramebuffer.java | 6 +- .../instancing/InstancedDrawManager.java | 128 +++++++++++++++++- .../instancing/InstancedRenderStage.java | 106 --------------- .../flywheel/backend/gl/GlCompat.java | 3 +- .../flywheel/flywheel/internal/common.frag | 18 +-- .../internal/{indirect => }/fullscreen.vert | 0 .../flywheel/flywheel/internal/light_lut.glsl | 30 ++-- .../{indirect => }/oit_composite.frag | 8 +- .../internal/{indirect => }/oit_depth.frag | 4 +- .../flywheel/flywheel/internal/wavelet.glsl | 9 +- 15 files changed, 266 insertions(+), 185 deletions(-) create mode 100644 common/src/backend/java/dev/engine_room/flywheel/backend/compile/OitPrograms.java delete mode 100644 common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java rename common/src/backend/resources/assets/flywheel/flywheel/internal/{indirect => }/fullscreen.vert (100%) rename common/src/backend/resources/assets/flywheel/flywheel/internal/{indirect => }/oit_composite.frag (73%) rename common/src/backend/resources/assets/flywheel/flywheel/internal/{indirect => }/oit_depth.frag (93%) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java index d69c8e2e0..3dd2979f6 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/IndirectPrograms.java @@ -32,10 +32,6 @@ public class IndirectPrograms extends AtomicReferenceCounted { private static final ResourceLocation DOWNSAMPLE_FIRST = Flywheel.rl("internal/indirect/downsample_first.glsl"); private static final ResourceLocation DOWNSAMPLE_SECOND = Flywheel.rl("internal/indirect/downsample_second.glsl"); - private static final ResourceLocation FULLSCREEN = Flywheel.rl("internal/indirect/fullscreen.vert"); - private static final ResourceLocation OIT_COMPOSITE = Flywheel.rl("internal/indirect/oit_composite.frag"); - private static final ResourceLocation OIT_DEPTH = Flywheel.rl("internal/indirect/oit_depth.frag"); - private static final Compile> CULL = new Compile<>(); private static final Compile UTIL = new Compile<>(); @@ -48,13 +44,13 @@ public class IndirectPrograms extends AtomicReferenceCounted { private final PipelineCompiler pipeline; private final CompilationHarness> culling; private final CompilationHarness utils; - private final CompilationHarness fullscreen; + private final OitPrograms oitPrograms; - private IndirectPrograms(PipelineCompiler pipeline, CompilationHarness> culling, CompilationHarness utils, CompilationHarness fullscreen) { + private IndirectPrograms(PipelineCompiler pipeline, CompilationHarness> culling, CompilationHarness utils, OitPrograms oitPrograms) { this.pipeline = pipeline; this.culling = culling; this.utils = utils; - this.fullscreen = fullscreen; + this.oitPrograms = oitPrograms; } private static List getExtensions(GlslVersion glslVersion) { @@ -64,9 +60,11 @@ public class IndirectPrograms extends AtomicReferenceCounted { } if (glslVersion.compareTo(GlslVersion.V420) < 0) { extensions.add("GL_ARB_shading_language_420pack"); + extensions.add("GL_ARB_shader_image_load_store"); } if (glslVersion.compareTo(GlslVersion.V430) < 0) { extensions.add("GL_ARB_shader_storage_buffer_object"); + extensions.add("GL_ARB_shader_image_size"); } if (glslVersion.compareTo(GlslVersion.V460) < 0) { extensions.add("GL_ARB_shader_draw_parameters"); @@ -93,7 +91,7 @@ public class IndirectPrograms extends AtomicReferenceCounted { var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INDIRECT, vertexComponents, fragmentComponents, EXTENSIONS); var cullingCompiler = createCullingCompiler(sources); var utilCompiler = createUtilCompiler(sources); - var fullscreenCompiler = createFullscreenCompiler(sources); + var fullscreenCompiler = OitPrograms.createFullscreenCompiler(sources); IndirectPrograms newInstance = new IndirectPrograms(pipelineCompiler, cullingCompiler, utilCompiler, fullscreenCompiler); @@ -131,18 +129,6 @@ public class IndirectPrograms extends AtomicReferenceCounted { .harness("utilities", sources); } - private static CompilationHarness createFullscreenCompiler(ShaderSources sources) { - return UTIL.program() - .link(UTIL.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.VERTEX) - .nameMapper($ -> "fullscreen/fullscreen") - .withResource(FULLSCREEN)) - .link(UTIL.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.FRAGMENT) - .nameMapper(rl -> "fullscreen/" + ResourceUtil.toDebugFileNameNoExtension(rl)) - .withResource(s -> s)) - .postLink((key, program) -> Uniforms.setUniformBlockBindings(program)) - .harness("fullscreen", sources); - } - static void setInstance(@Nullable IndirectPrograms newInstance) { if (instance != null) { instance.release(); @@ -190,12 +176,8 @@ public class IndirectPrograms extends AtomicReferenceCounted { return utils.get(DOWNSAMPLE_SECOND); } - public GlProgram getOitCompositeProgram() { - return fullscreen.get(OIT_COMPOSITE); - } - - public GlProgram getOitDepthProgram() { - return fullscreen.get(OIT_DEPTH); + public OitPrograms oitPrograms() { + return oitPrograms; } @Override @@ -203,6 +185,6 @@ public class IndirectPrograms extends AtomicReferenceCounted { pipeline.delete(); culling.delete(); utils.delete(); - fullscreen.delete(); + oitPrograms.delete(); } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java index 392435849..96704fbbc 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/InstancingPrograms.java @@ -23,8 +23,11 @@ public class InstancingPrograms extends AtomicReferenceCounted { private final PipelineCompiler pipeline; - private InstancingPrograms(PipelineCompiler pipeline) { + private final OitPrograms oitPrograms; + + private InstancingPrograms(PipelineCompiler pipeline, OitPrograms oitPrograms) { this.pipeline = pipeline; + this.oitPrograms = oitPrograms; } private static List getExtensions(GlslVersion glslVersion) { @@ -41,7 +44,8 @@ public class InstancingPrograms extends AtomicReferenceCounted { } var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INSTANCING, vertexComponents, fragmentComponents, EXTENSIONS); - InstancingPrograms newInstance = new InstancingPrograms(pipelineCompiler); + var fullscreen = OitPrograms.createFullscreenCompiler(sources); + InstancingPrograms newInstance = new InstancingPrograms(pipelineCompiler, fullscreen); setInstance(newInstance); } @@ -69,12 +73,17 @@ public class InstancingPrograms extends AtomicReferenceCounted { setInstance(null); } - public GlProgram get(InstanceType instanceType, ContextShader contextShader, Material material) { - return pipeline.get(instanceType, contextShader, material, PipelineCompiler.OitMode.OFF); + public GlProgram get(InstanceType instanceType, ContextShader contextShader, Material material, PipelineCompiler.OitMode mode) { + return pipeline.get(instanceType, contextShader, material, mode); + } + + public OitPrograms oitPrograms() { + return oitPrograms; } @Override protected void _delete() { pipeline.delete(); + oitPrograms.delete(); } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/OitPrograms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/OitPrograms.java new file mode 100644 index 000000000..75bc1a0c3 --- /dev/null +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/OitPrograms.java @@ -0,0 +1,68 @@ +package dev.engine_room.flywheel.backend.compile; + +import dev.engine_room.flywheel.api.Flywheel; +import dev.engine_room.flywheel.backend.Samplers; +import dev.engine_room.flywheel.backend.compile.core.CompilationHarness; +import dev.engine_room.flywheel.backend.compile.core.Compile; +import dev.engine_room.flywheel.backend.engine.uniform.Uniforms; +import dev.engine_room.flywheel.backend.gl.GlCompat; +import dev.engine_room.flywheel.backend.gl.GlTextureUnit; +import dev.engine_room.flywheel.backend.gl.shader.GlProgram; +import dev.engine_room.flywheel.backend.gl.shader.ShaderType; +import dev.engine_room.flywheel.backend.glsl.GlslVersion; +import dev.engine_room.flywheel.backend.glsl.ShaderSources; +import dev.engine_room.flywheel.lib.util.ResourceUtil; +import net.minecraft.resources.ResourceLocation; + +public class OitPrograms { + private static final ResourceLocation FULLSCREEN = Flywheel.rl("internal/fullscreen.vert"); + static final ResourceLocation OIT_COMPOSITE = Flywheel.rl("internal/oit_composite.frag"); + static final ResourceLocation OIT_DEPTH = Flywheel.rl("internal/oit_depth.frag"); + + private static final Compile COMPILE = new Compile<>(); + + private final CompilationHarness harness; + + public OitPrograms(CompilationHarness harness) { + this.harness = harness; + } + + public static OitPrograms createFullscreenCompiler(ShaderSources sources) { + var harness = COMPILE.program() + .link(COMPILE.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.VERTEX) + .nameMapper($ -> "fullscreen/fullscreen") + .withResource(FULLSCREEN)) + .link(COMPILE.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.FRAGMENT) + .nameMapper(rl -> "fullscreen/" + ResourceUtil.toDebugFileNameNoExtension(rl)) + .onCompile((rl, compilation) -> { + if (GlCompat.MAX_GLSL_VERSION.compareTo(GlslVersion.V400) < 0) { + // Need to define FMA for the wavelet calculations + compilation.define("fma(a, b, c) ((a) * (b) + (c))"); + } + }) + .withResource(s -> s)) + .postLink((key, program) -> { + program.bind(); + Uniforms.setUniformBlockBindings(program); + program.setSamplerBinding("_flw_accumulate", GlTextureUnit.T0); + program.setSamplerBinding("_flw_depthRange", Samplers.DEPTH_RANGE); + program.setSamplerBinding("_flw_coefficients", Samplers.COEFFICIENTS); + + GlProgram.unbind(); + }) + .harness("fullscreen", sources); + return new OitPrograms(harness); + } + + public GlProgram getOitCompositeProgram() { + return harness.get(OitPrograms.OIT_COMPOSITE); + } + + public GlProgram getOitDepthProgram() { + return harness.get(OitPrograms.OIT_DEPTH); + } + + public void delete() { + harness.delete(); + } +} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java index 388563f49..3bd507bde 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java @@ -24,6 +24,7 @@ import dev.engine_room.flywheel.backend.engine.uniform.Uniforms; import dev.engine_room.flywheel.backend.gl.GlCompat; import dev.engine_room.flywheel.backend.gl.shader.GlProgram; import dev.engine_room.flywheel.backend.gl.shader.ShaderType; +import dev.engine_room.flywheel.backend.glsl.GlslVersion; import dev.engine_room.flywheel.backend.glsl.ShaderSources; import dev.engine_room.flywheel.backend.glsl.SourceComponent; import dev.engine_room.flywheel.backend.glsl.generate.FnSignature; @@ -96,6 +97,12 @@ public final class PipelineCompiler { return "pipeline/" + pipeline.compilerMarker() + "/" + instance + "/" + material + "_" + context + debug; }) .requireExtensions(extensions) + .onCompile((rl, compilation) -> { + if (GlCompat.MAX_GLSL_VERSION.compareTo(GlslVersion.V400) < 0 && !extensions.contains("GL_ARB_gpu_shader5")) { + // Only define fma if it wouldn't be declared by gpu shader 5 + compilation.define("fma(a, b, c) ((a) * (b) + (c))"); + } + }) .onCompile((key, comp) -> key.contextShader() .onCompile(comp)) .onCompile((key, comp) -> BackendConfig.INSTANCE.lightSmoothness() @@ -133,6 +140,12 @@ public final class PipelineCompiler { }) .requireExtensions(extensions) .enableExtension("GL_ARB_conservative_depth") + .onCompile((rl, compilation) -> { + if (GlCompat.MAX_GLSL_VERSION.compareTo(GlslVersion.V400) < 0 && !extensions.contains("GL_ARB_gpu_shader5")) { + // Only define fma if it wouldn't be declared by gpu shader 5 + compilation.define("fma(a, b, c) ((a) * (b) + (c))"); + } + }) .onCompile((key, comp) -> key.contextShader() .onCompile(comp)) .onCompile((key, comp) -> BackendConfig.INSTANCE.lightSmoothness() @@ -178,6 +191,9 @@ public final class PipelineCompiler { program.setSamplerBinding("flw_diffuseTex", Samplers.DIFFUSE); program.setSamplerBinding("flw_overlayTex", Samplers.OVERLAY); program.setSamplerBinding("flw_lightTex", Samplers.LIGHT); + program.setSamplerBinding("_flw_depthRange", Samplers.DEPTH_RANGE); + program.setSamplerBinding("_flw_coefficients", Samplers.COEFFICIENTS); + program.setSamplerBinding("_flw_blueNoise", Samplers.NOISE); pipeline.onLink() .accept(program); key.contextShader() diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index 1e38fedbd..68472b971 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -66,7 +66,7 @@ public class IndirectDrawManager extends DrawManager> { depthPyramid = new DepthPyramid(programs); - oitFramebuffer = new OitFramebuffer(programs); + oitFramebuffer = new OitFramebuffer(programs.oitPrograms()); } @Override diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java index d184acf02..693fe9cef 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/OitFramebuffer.java @@ -9,7 +9,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import dev.engine_room.flywheel.backend.NoiseTextures; import dev.engine_room.flywheel.backend.Samplers; -import dev.engine_room.flywheel.backend.compile.IndirectPrograms; +import dev.engine_room.flywheel.backend.compile.OitPrograms; import dev.engine_room.flywheel.backend.gl.GlCompat; import dev.engine_room.flywheel.backend.gl.GlTextureUnit; import net.minecraft.client.Minecraft; @@ -21,7 +21,7 @@ public class OitFramebuffer { public static final int[] ACCUMULATE_DRAW_BUFFERS = {GL46.GL_COLOR_ATTACHMENT5}; public static final int[] DEPTH_ONLY_DRAW_BUFFERS = {}; - private final IndirectPrograms programs; + private final OitPrograms programs; private final int vao; public int fbo = -1; @@ -32,7 +32,7 @@ public class OitFramebuffer { private int lastWidth = -1; private int lastHeight = -1; - public OitFramebuffer(IndirectPrograms programs) { + public OitFramebuffer(OitPrograms programs) { this.programs = programs; if (GlCompat.SUPPORTS_DSA) { vao = GL46.glCreateVertexArrays(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java index e2a97ddf7..0ef90065f 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java @@ -1,13 +1,17 @@ package dev.engine_room.flywheel.backend.engine.instancing; +import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import dev.engine_room.flywheel.api.backend.Engine; import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.material.Material; +import dev.engine_room.flywheel.api.material.Transparency; import dev.engine_room.flywheel.backend.Samplers; import dev.engine_room.flywheel.backend.compile.ContextShader; import dev.engine_room.flywheel.backend.compile.InstancingPrograms; +import dev.engine_room.flywheel.backend.compile.PipelineCompiler; import dev.engine_room.flywheel.backend.engine.AbstractInstancer; import dev.engine_room.flywheel.backend.engine.CommonCrumbling; import dev.engine_room.flywheel.backend.engine.DrawManager; @@ -19,6 +23,7 @@ import dev.engine_room.flywheel.backend.engine.MaterialRenderState; import dev.engine_room.flywheel.backend.engine.MeshPool; import dev.engine_room.flywheel.backend.engine.TextureBinder; import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage; +import dev.engine_room.flywheel.backend.engine.indirect.OitFramebuffer; import dev.engine_room.flywheel.backend.engine.uniform.Uniforms; import dev.engine_room.flywheel.backend.gl.TextureBuffer; import dev.engine_room.flywheel.backend.gl.array.GlVertexArray; @@ -28,7 +33,16 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.resources.model.ModelBakery; public class InstancedDrawManager extends DrawManager> { - private final InstancedRenderStage draws = new InstancedRenderStage(); + private static final Comparator DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::bias) + .thenComparing(InstancedDraw::indexOfMeshInModel) + .thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR); + + private final List allDraws = new ArrayList<>(); + private boolean needSort = false; + + private final List draws = new ArrayList<>(); + private final List oitDraws = new ArrayList<>(); + private final InstancingPrograms programs; /** * A map of vertex types to their mesh pools. @@ -38,6 +52,8 @@ public class InstancedDrawManager extends DrawManager> { private final TextureBuffer instanceTexture; private final InstancedLight light; + private final OitFramebuffer oitFramebuffer; + public InstancedDrawManager(InstancingPrograms programs) { programs.acquire(); this.programs = programs; @@ -48,6 +64,9 @@ public class InstancedDrawManager extends DrawManager> { light = new InstancedLight(); meshPool.bind(vao); + + oitFramebuffer = new OitFramebuffer(programs.oitPrograms()); + } @Override @@ -66,13 +85,31 @@ public class InstancedDrawManager extends DrawManager> { }); // Remove the draw calls for any instancers we deleted. - draws.flush(); + needSort |= allDraws.removeIf(InstancedDraw::deleted); + + if (needSort) { + allDraws.sort(DRAW_COMPARATOR); + + draws.clear(); + oitDraws.clear(); + + for (var draw : allDraws) { + if (draw.material() + .transparency() == Transparency.ORDER_INDEPENDENT) { + oitDraws.add(draw); + } else { + draws.add(draw); + } + } + + needSort = false; + } meshPool.flush(); light.flush(lightStorage); - if (draws.isEmpty()) { + if (allDraws.isEmpty()) { return; } @@ -81,18 +118,92 @@ public class InstancedDrawManager extends DrawManager> { TextureBinder.bindLightAndOverlay(); light.bind(); - draws.draw(instanceTexture, programs); + submitDraws(); + + if (!oitDraws.isEmpty()) { + oitFramebuffer.prepare(); + + oitFramebuffer.depthRange(); + + submitOitDraws(PipelineCompiler.OitMode.DEPTH_RANGE); + + oitFramebuffer.renderTransmittance(); + + submitOitDraws(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS); + + oitFramebuffer.renderDepthFromTransmittance(); + + // Need to bind this again because we just drew a full screen quad for OIT. + vao.bindForDraw(); + + oitFramebuffer.accumulate(); + + submitOitDraws(PipelineCompiler.OitMode.EVALUATE); + + oitFramebuffer.composite(); + } MaterialRenderState.reset(); TextureBinder.resetLightAndOverlay(); } + private void submitDraws() { + for (var drawCall : draws) { + var material = drawCall.material(); + var groupKey = drawCall.groupKey; + var environment = groupKey.environment(); + + var program = programs.get(groupKey.instanceType(), environment.contextShader(), material, PipelineCompiler.OitMode.OFF); + program.bind(); + + environment.setupDraw(program); + + uploadMaterialUniform(program, material); + + program.setUInt("_flw_vertexOffset", drawCall.mesh() + .baseVertex()); + + MaterialRenderState.setup(material); + + Samplers.INSTANCE_BUFFER.makeActive(); + + drawCall.render(instanceTexture); + } + } + + private void submitOitDraws(PipelineCompiler.OitMode mode) { + for (var drawCall : oitDraws) { + var material = drawCall.material(); + var groupKey = drawCall.groupKey; + var environment = groupKey.environment(); + + var program = programs.get(groupKey.instanceType(), environment.contextShader(), material, mode); + program.bind(); + + environment.setupDraw(program); + + uploadMaterialUniform(program, material); + + program.setUInt("_flw_vertexOffset", drawCall.mesh() + .baseVertex()); + + MaterialRenderState.setupOit(material); + + Samplers.INSTANCE_BUFFER.makeActive(); + + drawCall.render(instanceTexture); + } + } + @Override public void delete() { instancers.values() .forEach(InstancedInstancer::delete); - draws.delete(); + allDraws.forEach(InstancedDraw::delete); + allDraws.clear(); + draws.clear(); + oitDraws.clear(); meshPool.delete(); instanceTexture.delete(); @@ -101,6 +212,8 @@ public class InstancedDrawManager extends DrawManager> { light.delete(); + oitFramebuffer.delete(); + super.delete(); } @@ -122,7 +235,8 @@ public class InstancedDrawManager extends DrawManager> { GroupKey groupKey = new GroupKey<>(key.type(), key.environment()); InstancedDraw instancedDraw = new InstancedDraw(instancer, mesh, groupKey, entry.material(), key.bias(), i); - draws.put(groupKey, instancedDraw); + allDraws.add(instancedDraw); + needSort = true; instancer.addDrawCall(instancedDraw); } } @@ -165,7 +279,7 @@ public class InstancedDrawManager extends DrawManager> { for (InstancedDraw draw : instancer.draws()) { CommonCrumbling.applyCrumblingProperties(crumblingMaterial, draw.material()); - var program = programs.get(shader.instanceType(), ContextShader.CRUMBLING, crumblingMaterial); + var program = programs.get(shader.instanceType(), ContextShader.CRUMBLING, crumblingMaterial, PipelineCompiler.OitMode.OFF); program.bind(); program.setInt("_flw_baseInstance", index); uploadMaterialUniform(program, crumblingMaterial); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java deleted file mode 100644 index 38614226c..000000000 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java +++ /dev/null @@ -1,106 +0,0 @@ -package dev.engine_room.flywheel.backend.engine.instancing; - -import static dev.engine_room.flywheel.backend.engine.instancing.InstancedDrawManager.uploadMaterialUniform; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import dev.engine_room.flywheel.backend.Samplers; -import dev.engine_room.flywheel.backend.compile.InstancingPrograms; -import dev.engine_room.flywheel.backend.engine.GroupKey; -import dev.engine_room.flywheel.backend.engine.MaterialRenderState; -import dev.engine_room.flywheel.backend.gl.TextureBuffer; - -public class InstancedRenderStage { - private static final Comparator DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::bias) - .thenComparing(InstancedDraw::indexOfMeshInModel) - .thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR); - - private final Map, DrawGroup> groups = new HashMap<>(); - - public InstancedRenderStage() { - } - - public void delete() { - groups.values() - .forEach(DrawGroup::delete); - groups.clear(); - } - - public void put(GroupKey groupKey, InstancedDraw instancedDraw) { - groups.computeIfAbsent(groupKey, $ -> new DrawGroup()) - .put(instancedDraw); - } - - public boolean isEmpty() { - return groups.isEmpty(); - } - - public void flush() { - groups.values() - .forEach(DrawGroup::flush); - - groups.values() - .removeIf(DrawGroup::isEmpty); - } - - public void draw(TextureBuffer instanceTexture, InstancingPrograms programs) { - for (var entry : groups.entrySet()) { - var shader = entry.getKey(); - var drawCalls = entry.getValue(); - - var environment = shader.environment(); - - for (var drawCall : drawCalls.draws) { - var material = drawCall.material(); - - var program = programs.get(shader.instanceType(), environment.contextShader(), material); - program.bind(); - - environment.setupDraw(program); - - uploadMaterialUniform(program, material); - - program.setUInt("_flw_vertexOffset", drawCall.mesh() - .baseVertex()); - - MaterialRenderState.setup(material); - - Samplers.INSTANCE_BUFFER.makeActive(); - - drawCall.render(instanceTexture); - } - } - } - - public static class DrawGroup { - private final List draws = new ArrayList<>(); - private boolean needSort = false; - - public void put(InstancedDraw instancedDraw) { - draws.add(instancedDraw); - needSort = true; - } - - public void delete() { - draws.forEach(InstancedDraw::delete); - draws.clear(); - } - - public void flush() { - needSort |= draws.removeIf(InstancedDraw::deleted); - - if (needSort) { - draws.sort(DRAW_COMPARATOR); - needSort = false; - } - } - - public boolean isEmpty() { - return draws.isEmpty(); - } - } -} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java b/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java index d93c72426..742b8770c 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/gl/GlCompat.java @@ -163,8 +163,7 @@ public final class GlCompat { && CAPABILITIES.GL_ARB_multi_draw_indirect && CAPABILITIES.GL_ARB_shader_draw_parameters && CAPABILITIES.GL_ARB_shader_storage_buffer_object - && CAPABILITIES.GL_ARB_shading_language_420pack - && CAPABILITIES.GL_ARB_vertex_attrib_binding; + && CAPABILITIES.GL_ARB_shading_language_420pack && CAPABILITIES.GL_ARB_vertex_attrib_binding && CAPABILITIES.GL_ARB_shader_image_load_store && CAPABILITIES.GL_ARB_shader_image_size; } private static boolean isDsaSupported() { diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag index 2693798a0..6f2c04ce2 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.frag @@ -21,11 +21,11 @@ flat in uvec2 _flw_ids; #ifdef _FLW_OIT -layout (binding = 7) uniform sampler2D _flw_depthRange; +uniform sampler2D _flw_depthRange; -layout (binding = 8) uniform sampler2DArray _flw_coefficients; +uniform sampler2DArray _flw_coefficients; -layout (binding = 9) uniform sampler2D _flw_blueNoise; +uniform sampler2D _flw_blueNoise; float tented_blue_noise(float normalizedDepth) { @@ -55,22 +55,22 @@ float depth() { #ifdef _FLW_DEPTH_RANGE -layout (location = 0) out vec2 _flw_depthRange_out; +out vec2 _flw_depthRange_out; #endif #ifdef _FLW_COLLECT_COEFFS -layout (location = 0) out vec4 _flw_coeffs0; -layout (location = 1) out vec4 _flw_coeffs1; -layout (location = 2) out vec4 _flw_coeffs2; -layout (location = 3) out vec4 _flw_coeffs3; +out vec4 _flw_coeffs0; +out vec4 _flw_coeffs1; +out vec4 _flw_coeffs2; +out vec4 _flw_coeffs3; #endif #ifdef _FLW_EVALUATE -layout (location = 0) out vec4 _flw_accumulate; +out vec4 _flw_accumulate; #endif diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/fullscreen.vert b/common/src/backend/resources/assets/flywheel/flywheel/internal/fullscreen.vert similarity index 100% rename from common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/fullscreen.vert rename to common/src/backend/resources/assets/flywheel/flywheel/internal/fullscreen.vert diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/light_lut.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/light_lut.glsl index e9ec12039..3c2a7e796 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/light_lut.glsl +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/light_lut.glsl @@ -1,12 +1,12 @@ -const uint _FLW_BLOCKS_PER_SECTION = 18 * 18 * 18; +const uint _FLW_BLOCKS_PER_SECTION = 18u * 18u * 18u; const uint _FLW_LIGHT_SIZE_BYTES = _FLW_BLOCKS_PER_SECTION; -const uint _FLW_SOLID_SIZE_BYTES = ((_FLW_BLOCKS_PER_SECTION + 31) / 32) * 4; +const uint _FLW_SOLID_SIZE_BYTES = ((_FLW_BLOCKS_PER_SECTION + 31u) / 32u) * 4u; const uint _FLW_LIGHT_START_BYTES = _FLW_SOLID_SIZE_BYTES; const uint _FLW_LIGHT_SECTION_SIZE_BYTES = _FLW_SOLID_SIZE_BYTES + _FLW_LIGHT_SIZE_BYTES; -const uint _FLW_SOLID_START_INTS = 0; -const uint _FLW_LIGHT_START_INTS = _FLW_SOLID_SIZE_BYTES / 4; -const uint _FLW_LIGHT_SECTION_SIZE_INTS = _FLW_LIGHT_SECTION_SIZE_BYTES / 4; +const uint _FLW_SOLID_START_INTS = 0u; +const uint _FLW_LIGHT_START_INTS = _FLW_SOLID_SIZE_BYTES / 4u; +const uint _FLW_LIGHT_SECTION_SIZE_INTS = _FLW_LIGHT_SECTION_SIZE_BYTES / 4u; const uint _FLW_COMPLETELY_SOLID = 0x7FFFFFFu; const float _FLW_EPSILON = 1e-5; @@ -29,39 +29,39 @@ bool _flw_nextLut(uint base, int coord, out uint next) { // The base coordinate. int start = int(_flw_indexLut(base)); // The width of the coordinate span. - uint size = _flw_indexLut(base + 1); + uint size = _flw_indexLut(base + 1u); // Index of the coordinate in the span. int i = coord - start; - if (i < 0 || i >= size) { + if (i < 0 || i >= int(size)) { // We missed. return true; } - next = _flw_indexLut(base + 2 + i); + next = _flw_indexLut(base + 2u + uint(i)); return false; } bool _flw_chunkCoordToSectionIndex(ivec3 sectionPos, out uint index) { uint first; - if (_flw_nextLut(0, sectionPos.y, first) || first == 0) { + if (_flw_nextLut(0u, sectionPos.y, first) || first == 0u) { return true; } uint second; - if (_flw_nextLut(first, sectionPos.x, second) || second == 0) { + if (_flw_nextLut(first, sectionPos.x, second) || second == 0u) { return true; } uint sectionIndex; - if (_flw_nextLut(second, sectionPos.z, sectionIndex) || sectionIndex == 0) { + if (_flw_nextLut(second, sectionPos.z, sectionIndex) || sectionIndex == 0u) { return true; } // The index is written as 1-based so we can properly detect missing sections. - index = sectionIndex - 1; + index = sectionIndex - 1u; return false; } @@ -87,7 +87,7 @@ bool _flw_isSolid(uint sectionOffset, uvec3 blockInSectionPos) { uint word = _flw_indexLight(sectionOffset + _FLW_SOLID_START_INTS + uintOffset); - return (word & (1u << bitInWordOffset)) != 0; + return (word & (1u << bitInWordOffset)) != 0u; } bool flw_lightFetch(ivec3 blockPos, out vec2 lightCoord) { @@ -98,7 +98,7 @@ bool flw_lightFetch(ivec3 blockPos, out vec2 lightCoord) { // The offset of the section in the light buffer. uint sectionOffset = lightSectionIndex * _FLW_LIGHT_SECTION_SIZE_INTS; - uvec3 blockInSectionPos = (blockPos & 0xF) + 1; + uvec3 blockInSectionPos = uvec3((blockPos & 0xF) + 1); lightCoord = vec2(_flw_lightAt(sectionOffset, blockInSectionPos)) * _FLW_LIGHT_NORMALIZER; return true; @@ -106,7 +106,7 @@ bool flw_lightFetch(ivec3 blockPos, out vec2 lightCoord) { uint _flw_fetchSolid3x3x3(uint sectionOffset, ivec3 blockInSectionPos) { - uint ret = 0; + uint ret = 0u; // The formatter does NOT like these macros // @formatter:off diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/oit_composite.frag similarity index 73% rename from common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag rename to common/src/backend/resources/assets/flywheel/flywheel/internal/oit_composite.frag index 2aa577e84..f63f2dc97 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_composite.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/oit_composite.frag @@ -2,11 +2,11 @@ #include "flywheel:internal/depth.glsl" #include "flywheel:internal/uniforms/frame.glsl" -layout (location = 0) out vec4 frag; +out vec4 frag; -layout (binding = 0) uniform sampler2D _flw_accumulate; -layout (binding = 7) uniform sampler2D _flw_depthRange; -layout (binding = 8) uniform sampler2DArray _flw_coefficients; +uniform sampler2D _flw_accumulate; +uniform sampler2D _flw_depthRange; +uniform sampler2DArray _flw_coefficients; void main() { vec4 texel = texelFetch(_flw_accumulate, ivec2(gl_FragCoord.xy), 0); diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_depth.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/oit_depth.frag similarity index 93% rename from common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_depth.frag rename to common/src/backend/resources/assets/flywheel/flywheel/internal/oit_depth.frag index 1f127b445..c5b782d64 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/oit_depth.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/oit_depth.frag @@ -2,9 +2,9 @@ #include "flywheel:internal/wavelet.glsl" #include "flywheel:internal/depth.glsl" -layout (binding = 7) uniform sampler2D _flw_depthRange; +uniform sampler2D _flw_depthRange; -layout (binding = 8) uniform sampler2DArray _flw_coefficients; +uniform sampler2DArray _flw_coefficients; float eye_depth_from_normalized_transparency_depth(float tDepth) { vec2 depthRange = texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).rg; diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/wavelet.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/wavelet.glsl index 6691e3d16..8bfaeb620 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/wavelet.glsl +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/wavelet.glsl @@ -1,13 +1,12 @@ #define TRANSPARENCY_WAVELET_RANK 3 #define TRANSPARENCY_WAVELET_COEFFICIENT_COUNT 16 - // ------------------------------------------------------------------------- // WRITING // ------------------------------------------------------------------------- -void add_to_index(inout vec4[4] coefficients, uint index, float addend) { - coefficients[index >> 2][index & 3u] = addend; +void add_to_index(inout vec4[4] coefficients, int index, float addend) { + coefficients[index >> 2][index & 3] = addend; } void add_absorbance(inout vec4[4] coefficients, float signal, float depth) { @@ -43,8 +42,8 @@ void add_transmittance(inout vec4[4] coefficients, float transmittance, float de // ------------------------------------------------------------------------- // TODO: maybe we could reduce the number of texel fetches below? -float get_coefficients(in sampler2DArray coefficients, uint index) { - return texelFetch(coefficients, ivec3(gl_FragCoord.xy, index >> 2), 0)[index & 3u]; +float get_coefficients(in sampler2DArray coefficients, int index) { + return texelFetch(coefficients, ivec3(gl_FragCoord.xy, index >> 2), 0)[index & 3]; } /// Compute the total absorbance, as if at infinite depth. From 1d20d36e14272c019dd7ddb337e761491d6c6bcb Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Mon, 24 Feb 2025 19:37:43 -0800 Subject: [PATCH 22/23] Scattered issues - Fix gl debug spam when reading from the scatter buffer - Cherry pick optimization from last frame visibility to use the staging buffer itself to upload the scatter list --- .../engine/indirect/StagingBuffer.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/StagingBuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/StagingBuffer.java index 59a8c3e0a..efe107f92 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/StagingBuffer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/StagingBuffer.java @@ -10,6 +10,7 @@ import org.lwjgl.system.MemoryUtil; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; import dev.engine_room.flywheel.backend.gl.GlFence; import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer; +import dev.engine_room.flywheel.backend.gl.buffer.GlBufferUsage; import dev.engine_room.flywheel.lib.memory.FlwMemoryTracker; import dev.engine_room.flywheel.lib.memory.MemoryBlock; import it.unimi.dsi.fastutil.PriorityQueue; @@ -22,6 +23,8 @@ public class StagingBuffer { private static final int STORAGE_FLAGS = GL45C.GL_MAP_PERSISTENT_BIT | GL45C.GL_MAP_WRITE_BIT | GL45C.GL_CLIENT_STORAGE_BIT; private static final int MAP_FLAGS = GL45C.GL_MAP_PERSISTENT_BIT | GL45C.GL_MAP_WRITE_BIT | GL45C.GL_MAP_FLUSH_EXPLICIT_BIT | GL45C.GL_MAP_INVALIDATE_BUFFER_BIT; + private static final int SSBO_ALIGNMENT = GL45.glGetInteger(GL45.GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); + private final int vbo; private final long map; private final long capacity; @@ -30,7 +33,7 @@ public class StagingBuffer { private final OverflowStagingBuffer overflow = new OverflowStagingBuffer(); private final TransferList transfers = new TransferList(); private final PriorityQueue fencedRegions = new ObjectArrayFIFOQueue<>(); - private final GlBuffer scatterBuffer = new GlBuffer(); + private final GlBuffer scatterBuffer = new GlBuffer(GlBufferUsage.STREAM_COPY); private final ScatterList scatterList = new ScatterList(); /** @@ -252,7 +255,6 @@ public class StagingBuffer { .bind(); // These bindings don't change between dstVbos. - GL45.glBindBufferBase(GL45C.GL_SHADER_STORAGE_BUFFER, 0, scatterBuffer.handle()); GL45.glBindBufferBase(GL45C.GL_SHADER_STORAGE_BUFFER, 1, vbo); int dstVbo; @@ -274,7 +276,25 @@ public class StagingBuffer { } private void dispatchScatter(int dstVbo) { - scatterBuffer.upload(scatterList.ptr(), scatterList.usedBytes()); + var scatterSize = scatterList.usedBytes(); + + // If there's enough space in the staging buffer still, lets write the scatter in it directly. + long alignedPos = pos + SSBO_ALIGNMENT - 1 - (pos + SSBO_ALIGNMENT - 1) % SSBO_ALIGNMENT; + + long remaining = capacity - alignedPos; + if (scatterSize <= remaining && scatterSize <= totalAvailable) { + MemoryUtil.memCopy(scatterList.ptr(), map + alignedPos, scatterSize); + GL45.glBindBufferRange(GL45C.GL_SHADER_STORAGE_BUFFER, 0, vbo, alignedPos, scatterSize); + + long alignmentCost = alignedPos - pos; + + usedCapacity += scatterSize + alignmentCost; + totalAvailable -= scatterSize + alignmentCost; + pos += scatterSize + alignmentCost; + } else { + scatterBuffer.upload(scatterList.ptr(), scatterSize); + GL45.glBindBufferBase(GL45C.GL_SHADER_STORAGE_BUFFER, 0, scatterBuffer.handle()); + } GL45.glBindBufferBase(GL45C.GL_SHADER_STORAGE_BUFFER, 2, dstVbo); From dc4d8690899dcac998a651e0ba42db3a88ef268d Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 26 Feb 2025 22:02:25 -0800 Subject: [PATCH 23/23] Absolutely gutted - Remove dead code in source file parsing --- .../backend/glsl/{parse => }/Import.java | 3 +- .../flywheel/backend/glsl/SourceFile.java | 86 +---------- .../backend/glsl/parse/ShaderField.java | 69 --------- .../backend/glsl/parse/ShaderFunction.java | 133 ------------------ .../backend/glsl/parse/ShaderStruct.java | 84 ----------- .../backend/glsl/parse/ShaderVariable.java | 47 ------- .../backend/glsl/parse/StructField.java | 24 ---- .../backend/glsl/TestShaderSourceLoading.java | 2 - 8 files changed, 4 insertions(+), 444 deletions(-) rename common/src/backend/java/dev/engine_room/flywheel/backend/glsl/{parse => }/Import.java (88%) delete mode 100644 common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderField.java delete mode 100644 common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderFunction.java delete mode 100644 common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderStruct.java delete mode 100644 common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderVariable.java delete mode 100644 common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/StructField.java diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/Import.java b/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/Import.java similarity index 88% rename from common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/Import.java rename to common/src/backend/java/dev/engine_room/flywheel/backend/glsl/Import.java index 006338c6f..16a87cca2 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/Import.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/Import.java @@ -1,11 +1,10 @@ -package dev.engine_room.flywheel.backend.glsl.parse; +package dev.engine_room.flywheel.backend.glsl; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.collect.ImmutableList; -import dev.engine_room.flywheel.backend.glsl.SourceLines; import dev.engine_room.flywheel.backend.glsl.span.Span; public record Import(Span self, Span file) { diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/SourceFile.java b/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/SourceFile.java index 31f24b0f1..8a2241d76 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/SourceFile.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/SourceFile.java @@ -10,13 +10,8 @@ import java.util.function.Function; import org.jetbrains.annotations.Nullable; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.mojang.datafixers.util.Pair; -import dev.engine_room.flywheel.backend.glsl.parse.Import; -import dev.engine_room.flywheel.backend.glsl.parse.ShaderField; -import dev.engine_room.flywheel.backend.glsl.parse.ShaderFunction; -import dev.engine_room.flywheel.backend.glsl.parse.ShaderStruct; import dev.engine_room.flywheel.backend.glsl.span.Span; import dev.engine_room.flywheel.backend.glsl.span.StringSpan; import dev.engine_room.flywheel.lib.util.ResourceUtil; @@ -35,39 +30,25 @@ public class SourceFile implements SourceComponent { public final SourceLines source; - /** - * Function lookup by name. - */ - public final ImmutableMap functions; - - /** - * Struct lookup by name. - */ - public final ImmutableMap structs; - /** * Includes ordered as defined in the source. */ public final ImmutableList imports; - public final ImmutableMap fields; public final List included; public final String finalSource; - private SourceFile(ResourceLocation name, SourceLines source, ImmutableMap functions, ImmutableMap structs, ImmutableList imports, ImmutableMap fields, List included, String finalSource) { + private SourceFile(ResourceLocation name, SourceLines source, ImmutableList imports, List included, String finalSource) { this.name = name; this.source = source; - this.functions = functions; - this.structs = structs; this.imports = imports; - this.fields = fields; this.included = included; this.finalSource = finalSource; } public static LoadResult empty(ResourceLocation name) { - return new LoadResult.Success(new SourceFile(name, new SourceLines(name, ""), ImmutableMap.of(), ImmutableMap.of(), ImmutableList.of(), ImmutableMap.of(), ImmutableList.of(), "")); + return new LoadResult.Success(new SourceFile(name, new SourceLines(name, ""), ImmutableList.of(), ImmutableList.of(), "")); } public static LoadResult parse(Function sourceFinder, ResourceLocation name, String stringSource) { @@ -106,12 +87,8 @@ public class SourceFile implements SourceComponent { return new LoadResult.Failure(new LoadError.IncludeError(name, failures)); } - var functions = ShaderFunction.parseFunctions(source); - var structs = ShaderStruct.parseStructs(source); - var fields = ShaderField.parseFields(source); - var finalSource = generateFinalSource(imports, source); - return new LoadResult.Success(new SourceFile(name, source, functions, structs, imports, fields, included, finalSource)); + return new LoadResult.Success(new SourceFile(name, source, imports, included, finalSource)); } @Override @@ -129,13 +106,6 @@ public class SourceFile implements SourceComponent { return name.toString(); } - public Span getLineSpan(int lineNo) { - int begin = source.lineStartIndex(lineNo); - int end = begin + source.lineString(lineNo) - .length(); - return new StringSpan(source, begin, end); - } - public Span getLineSpanNoWhitespace(int line) { int begin = source.lineStartIndex(line); int end = begin + source.lineString(line) @@ -166,56 +136,6 @@ public class SourceFile implements SourceComponent { return new StringSpan(source, begin, end); } - /** - * Search this file and recursively search all imports to find a struct definition matching the given name. - * - * @param name The name of the struct to find. - * @return null if no definition matches the name. - */ - @Nullable - public ShaderStruct findStruct(String name) { - ShaderStruct struct = structs.get(name); - - if (struct != null) { - return struct; - } - - for (var include : included) { - var external = include.findStruct(name); - - if (external != null) { - return external; - } - } - - return null; - } - - /** - * Search this file and recursively search all imports to find a function definition matching the given name. - * - * @param name The name of the function to find. - * @return null if no definition matches the name. - */ - @Nullable - public ShaderFunction findFunction(String name) { - ShaderFunction function = functions.get(name); - - if (function != null) { - return function; - } - - for (var include : included) { - var external = include.findFunction(name); - - if (external != null) { - return external; - } - } - - return null; - } - @Override public String toString() { return name.toString(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderField.java b/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderField.java deleted file mode 100644 index c36bf4f7f..000000000 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderField.java +++ /dev/null @@ -1,69 +0,0 @@ -package dev.engine_room.flywheel.backend.glsl.parse; - -import java.util.regex.Pattern; - -import org.jetbrains.annotations.Nullable; - -import com.google.common.collect.ImmutableMap; - -import dev.engine_room.flywheel.backend.glsl.SourceLines; -import dev.engine_room.flywheel.backend.glsl.span.Span; - -public class ShaderField { - public static final Pattern PATTERN = Pattern.compile("layout\\s*\\(location\\s*=\\s*(\\d+)\\)\\s+(in|out)\\s+([\\w\\d]+)\\s+" + "([\\w\\d]+)"); - - public final Span self; - public final Span location; - public final Span qualifierSpan; - @Nullable - public final Qualifier qualifier; - public final Span type; - public final Span name; - - public ShaderField(Span self, Span location, Span qualifier, Span type, Span name) { - this.self = self; - this.location = location; - this.qualifierSpan = qualifier; - this.qualifier = Qualifier.fromSpan(qualifier); - this.type = type; - this.name = name; - } - - /** - * Scan the source for function definitions and "parse" them into objects that contain properties of the function. - */ - public static ImmutableMap parseFields(SourceLines source) { - // Matcher matcher = PATTERN.matcher(source); - // - // ImmutableMap.Builder fields = ImmutableMap.builder(); - // while (matcher.find()) { - // Span self = Span.fromMatcher(source, matcher); - // Span location = Span.fromMatcher(source, matcher, 1); - // Span decoration = Span.fromMatcher(source, matcher, 2); - // Span type = Span.fromMatcher(source, matcher, 3); - // Span name = Span.fromMatcher(source, matcher, 4); - // - // fields.put(location.get(), new ShaderField(self, location, decoration, type, name)); - // } - - return ImmutableMap.builder() - .build(); - } - - public enum Qualifier { - IN, - OUT, - FLAT, - ; - - @Nullable - public static Qualifier fromSpan(Span span) { - return switch (span.toString()) { - case "in" -> IN; - case "out" -> OUT; - case "flat" -> FLAT; - default -> null; - }; - } - } -} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderFunction.java b/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderFunction.java deleted file mode 100644 index 4ddec0e01..000000000 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderFunction.java +++ /dev/null @@ -1,133 +0,0 @@ -package dev.engine_room.flywheel.backend.glsl.parse; - -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; - -import dev.engine_room.flywheel.backend.glsl.SourceLines; -import dev.engine_room.flywheel.backend.glsl.span.ErrorSpan; -import dev.engine_room.flywheel.backend.glsl.span.Span; -import dev.engine_room.flywheel.backend.glsl.span.StringSpan; - -public class ShaderFunction { - // https://regexr.com/60n3d - public static final Pattern PATTERN = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{"); - public static final Pattern ARGUMENT_PATTERN = Pattern.compile("(?:(inout|in|out) )?(\\w+)\\s+(\\w+)"); - public static final Pattern ASSIGNMENT_PATTERN = Pattern.compile("(\\w+)\\s*="); - - public final Span self; - public final Span type; - public final Span name; - public final Span args; - public final Span body; - - public final ImmutableList parameters; - - public ShaderFunction(Span self, Span type, Span name, Span args, Span body) { - this.self = self; - this.type = type; - this.name = name; - this.args = args; - this.body = body; - - this.parameters = parseArguments(); - } - - /** - * Scan the source for function definitions and "parse" them into objects that contain properties of the function. - */ - public static ImmutableMap parseFunctions(SourceLines source) { - Matcher matcher = PATTERN.matcher(source); - - Map functions = new HashMap<>(); - - while (matcher.find()) { - Span type = Span.fromMatcher(source, matcher, 1); - Span name = Span.fromMatcher(source, matcher, 2); - Span args = Span.fromMatcher(source, matcher, 3); - - int blockStart = matcher.end(); - int blockEnd = findEndOfBlock(source, blockStart); - - Span self; - Span body; - if (blockEnd > blockStart) { - self = new StringSpan(source, matcher.start(), blockEnd + 1); - body = new StringSpan(source, blockStart, blockEnd); - } else { - self = new ErrorSpan(source, matcher.start(), matcher.end()); - body = new ErrorSpan(source, blockStart); - } - - ShaderFunction function = new ShaderFunction(self, type, name, args, body); - - functions.put(name.get(), function); - } - - return ImmutableMap.copyOf(functions); - } - - /** - * Given the position of an opening brace, scans through the source for a paired closing brace. - */ - private static int findEndOfBlock(CharSequence source, int start) { - int blockDepth = 0; - for (int i = start + 1; i < source.length(); i++) { - char ch = source.charAt(i); - - if (ch == '{') { - blockDepth++; - } else if (ch == '}') { - blockDepth--; - } - - if (blockDepth < 0) { - return i; - } - } - - return -1; - } - - public String call(String... args) { - return name + "(" + String.join(", ", args) + ")"; - } - - public Span getParameterType(int index) { - return parameters.get(index).type; - } - - protected ImmutableList parseArguments() { - if (args.isErr() || args.isEmpty()) return ImmutableList.of(); - - Matcher arguments = ARGUMENT_PATTERN.matcher(args.get()); - - ImmutableList.Builder builder = ImmutableList.builder(); - - while (arguments.find()) { - Span self = Span.fromMatcher(args, arguments); - Span qualifier = Span.fromMatcher(args, arguments, 1); - Span type = Span.fromMatcher(args, arguments, 2); - Span name = Span.fromMatcher(args, arguments, 3); - - builder.add(new ShaderVariable(self, qualifier, type, name)); - } - - return builder.build(); - } - - @Override - public String toString() { - String p = parameters.stream() - .map(variable -> variable.type) - .map(Span::get) - .collect(Collectors.joining(", ")); - - return type + " " + name + "(" + p + ")"; - } -} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderStruct.java b/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderStruct.java deleted file mode 100644 index 3b4a7fe7a..000000000 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderStruct.java +++ /dev/null @@ -1,84 +0,0 @@ -package dev.engine_room.flywheel.backend.glsl.parse; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; - -import dev.engine_room.flywheel.backend.glsl.SourceLines; -import dev.engine_room.flywheel.backend.glsl.span.Span; - -public class ShaderStruct { - // https://regexr.com/61rpe - public static final Pattern PATTERN = Pattern.compile("struct\\s+([\\w_]*)\\s*\\{(.*?)}\\s*([\\w_]*)?\\s*;\\s", Pattern.DOTALL); - - public final Span self; - public final Span name; - public final Span body; - public final Span variableName; - - public final ImmutableList fields; - public final ImmutableMap fields2Types; - - public ShaderStruct(Span self, Span name, Span body, Span variableName) { - this.self = self; - this.name = name; - this.body = body; - this.variableName = variableName; - - this.fields = parseFields(); - this.fields2Types = createTypeLookup(); - } - - /** - * Scan the source for function definitions and "parse" them into objects that contain properties of the function. - */ - public static ImmutableMap parseStructs(SourceLines source) { - Matcher matcher = PATTERN.matcher(source); - - ImmutableMap.Builder structs = ImmutableMap.builder(); - while (matcher.find()) { - Span self = Span.fromMatcher(source, matcher); - Span name = Span.fromMatcher(source, matcher, 1); - Span body = Span.fromMatcher(source, matcher, 2); - Span variableName = Span.fromMatcher(source, matcher, 3); - - ShaderStruct shaderStruct = new ShaderStruct(self, name, body, variableName); - - structs.put(name.get(), shaderStruct); - } - - return structs.build(); - } - - private ImmutableList parseFields() { - Matcher matcher = StructField.PATTERN.matcher(body); - - ImmutableList.Builder fields = ImmutableList.builder(); - - while (matcher.find()) { - Span field = Span.fromMatcher(body, matcher); - Span type = Span.fromMatcher(body, matcher, 1); - Span name = Span.fromMatcher(body, matcher, 2); - - fields.add(new StructField(field, type, name)); - } - - return fields.build(); - } - - private ImmutableMap createTypeLookup() { - ImmutableMap.Builder lookup = ImmutableMap.builder(); - for (StructField field : fields) { - lookup.put(field.name.get(), field.type); - } - - return lookup.build(); - } - - @Override - public String toString() { - return "struct " + name; - } -} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderVariable.java b/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderVariable.java deleted file mode 100644 index 6eee86538..000000000 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/ShaderVariable.java +++ /dev/null @@ -1,47 +0,0 @@ -package dev.engine_room.flywheel.backend.glsl.parse; - -import org.jetbrains.annotations.Nullable; - -import dev.engine_room.flywheel.backend.glsl.span.Span; - -public class ShaderVariable { - public final Span self; - public final Span qualifierSpan; - @Nullable - public final Qualifier qualifier; - public final Span type; - public final Span name; - - public ShaderVariable(Span self, Span qualifier, Span type, Span name) { - this.self = self; - this.qualifierSpan = qualifier; - this.qualifier = Qualifier.fromSpan(qualifierSpan); - this.type = type; - this.name = name; - } - - @Override - public String toString() { - return type + " " + name; - } - - public enum Qualifier { - NONE, - IN, - OUT, - INOUT; - - @Nullable - public static Qualifier fromSpan(Span s) { - String span = s.toString(); - - return switch (span) { - case "" -> NONE; - case "in" -> IN; - case "inout" -> INOUT; - case "out" -> OUT; - default -> null; - }; - } - } -} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/StructField.java b/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/StructField.java deleted file mode 100644 index 016494607..000000000 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/glsl/parse/StructField.java +++ /dev/null @@ -1,24 +0,0 @@ -package dev.engine_room.flywheel.backend.glsl.parse; - -import java.util.regex.Pattern; - -import dev.engine_room.flywheel.backend.glsl.span.Span; - -public class StructField { - public static final Pattern PATTERN = Pattern.compile("(\\S+)\\s*(\\S+);"); - - public final Span self; - public final Span type; - public final Span name; - - public StructField(Span self, Span type, Span name) { - this.self = self; - this.type = type; - this.name = name; - } - - @Override - public String toString() { - return type + " " + name; - } -} diff --git a/common/src/test/java/dev/engine_room/flywheel/backend/glsl/TestShaderSourceLoading.java b/common/src/test/java/dev/engine_room/flywheel/backend/glsl/TestShaderSourceLoading.java index b19c481b7..57c635756 100644 --- a/common/src/test/java/dev/engine_room/flywheel/backend/glsl/TestShaderSourceLoading.java +++ b/common/src/test/java/dev/engine_room/flywheel/backend/glsl/TestShaderSourceLoading.java @@ -7,8 +7,6 @@ import org.junit.jupiter.api.Test; import com.google.common.collect.ImmutableList; -import dev.engine_room.flywheel.backend.glsl.parse.Import; - public class TestShaderSourceLoading extends TestBase { @Test void testSimpleFind() {