From 2bb9092d4958b5b940a9622a61d69172283642c9 Mon Sep 17 00:00:00 2001 From: PepperCode1 <44146161+PepperCode1@users.noreply.github.com> Date: Sun, 3 Mar 2024 11:16:38 -0800 Subject: [PATCH] The end of issues with big endianness - Fix unpacking of some layout elements (only for instancing) - All byte backed and short backed elements (incorrect assumption that system is little endian) - All signed byte and signed short elements (missing sign extension) - Matrices with byte backed and short backed reprs in some cases (incorrect assumption that matrix rows are 4 byte aligned) - IntegerRepr.INT scalar (invalid implicit cast) - FloatRepr.NORMALIZED_INT scalar (missing cast to signed int) - IntegerRepr.INT vector (invalid implicit cast) - FloatRepr.NORMALIZED_INT vector (missing cast to signed int) - FloatRepr.NORMALIZED_SHORT vector (incorrect divisor) - Add explicit casts from uint to float since some drivers have a bug where uint over float division is invalid - Lower GLSL requirement of instancing from 420 to 330 - Fix lib instance writers assuming little endian when writing overlay and light - Move overlay clamping to vertex input shader and clamp to 15 instead of 10 - Create abstract InstanceAssemblerComponent class - Change return type of SourceComponent.name() to String and improve implementations --- .../flywheel/backend/compile/FlwPrograms.java | 8 +- .../backend/compile/IndirectPrograms.java | 4 +- .../flywheel/backend/compile/Pipelines.java | 8 +- .../BufferTextureInstanceComponent.java | 292 +++++++++++++++ .../compile/component/IndirectComponent.java | 349 ------------------ .../component/InstanceAssemblerComponent.java | 49 +++ .../component/SamplerBufferComponent.java | 322 ---------------- ....java => StringSubstitutionComponent.java} | 13 +- .../component/StructInstanceComponent.java | 308 ++++++++++++++++ .../component/UberShaderComponent.java | 31 +- .../backend/compile/core/Compilation.java | 2 +- .../backend/glsl/SourceComponent.java | 4 +- .../flywheel/backend/glsl/SourceFile.java | 4 +- .../flywheel/backend/glsl/generate/BinOp.java | 6 +- .../backend/glsl/generate/GlslExpr.java | 16 +- .../flywheel/lib/instance/InstanceTypes.java | 12 +- .../flywheel/flywheel/instance/oriented.vert | 3 +- .../flywheel/instance/transformed.vert | 3 +- .../flywheel/flywheel/internal/common.frag | 7 +- .../flywheel/internal/vertex_input.vert | 5 +- 20 files changed, 719 insertions(+), 727 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/compile/component/BufferTextureInstanceComponent.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/compile/component/IndirectComponent.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceAssemblerComponent.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/compile/component/SamplerBufferComponent.java rename src/main/java/com/jozufozu/flywheel/backend/compile/component/{StringSubstitutionSourceComponent.java => StringSubstitutionComponent.java} (69%) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/compile/component/StructInstanceComponent.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java b/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java index 9dbe4c5a3..de8e5b145 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java @@ -67,7 +67,7 @@ public final class FlwPrograms { @Nullable private static UberShaderComponent createVertexMaterialComponent(SourceLoader loadChecker) { - return UberShaderComponent.builder(Flywheel.rl("uber_material_vertex")) + return UberShaderComponent.builder(Flywheel.rl("material_vertex")) .materialSources(ShaderIndices.materialVertex() .all()) .adapt(FnSignature.ofVoid("flw_materialVertex")) @@ -77,7 +77,7 @@ public final class FlwPrograms { @Nullable private static UberShaderComponent createFragmentMaterialComponent(SourceLoader loadChecker) { - return UberShaderComponent.builder(Flywheel.rl("uber_material_fragment")) + return UberShaderComponent.builder(Flywheel.rl("material_fragment")) .materialSources(ShaderIndices.materialFragment() .all()) .adapt(FnSignature.ofVoid("flw_materialFragment")) @@ -87,7 +87,7 @@ public final class FlwPrograms { @Nullable private static UberShaderComponent createFogComponent(SourceLoader loadChecker) { - return UberShaderComponent.builder(Flywheel.rl("uber_fog")) + return UberShaderComponent.builder(Flywheel.rl("fog")) .materialSources(ShaderIndices.fog() .all()) .adapt(FnSignature.create() @@ -101,7 +101,7 @@ public final class FlwPrograms { @Nullable private static UberShaderComponent createCutoutComponent(SourceLoader loadChecker) { - return UberShaderComponent.builder(Flywheel.rl("uber_cutout")) + return UberShaderComponent.builder(Flywheel.rl("cutout")) .materialSources(ShaderIndices.cutout() .all()) .adapt(FnSignature.create() diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java b/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java index 40e6218c2..921c57e40 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java @@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable; import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.instance.InstanceType; -import com.jozufozu.flywheel.backend.compile.component.IndirectComponent; +import com.jozufozu.flywheel.backend.compile.component.StructInstanceComponent; import com.jozufozu.flywheel.backend.compile.core.CompilationHarness; import com.jozufozu.flywheel.backend.compile.core.Compile; import com.jozufozu.flywheel.backend.gl.GlCompat; @@ -78,7 +78,7 @@ public class IndirectPrograms extends AtomicReferenceCounted { .nameMapper(instanceType -> "culling/" + ResourceUtil.toDebugFileNameNoExtension(instanceType.cullShader())) .define("_FLW_SUBGROUP_SIZE", GlCompat.SUBGROUP_SIZE) .withResource(CULL_SHADER_HEADER) - .withComponent(IndirectComponent::create) + .withComponent(StructInstanceComponent::create) .withResource(InstanceType::cullShader) .withResource(CULL_SHADER_MAIN)) .postLink((key, program) -> program.setUniformBlockBinding("_FlwFrameUniforms", 0)) diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/Pipelines.java b/src/main/java/com/jozufozu/flywheel/backend/compile/Pipelines.java index ff4718722..aba476ec9 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/Pipelines.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/Pipelines.java @@ -2,8 +2,8 @@ package com.jozufozu.flywheel.backend.compile; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.Samplers; -import com.jozufozu.flywheel.backend.compile.component.IndirectComponent; -import com.jozufozu.flywheel.backend.compile.component.SamplerBufferComponent; +import com.jozufozu.flywheel.backend.compile.component.BufferTextureInstanceComponent; +import com.jozufozu.flywheel.backend.compile.component.StructInstanceComponent; import com.jozufozu.flywheel.backend.glsl.GlslVersion; public final class Pipelines { @@ -14,7 +14,7 @@ public final class Pipelines { .fragmentMain(Flywheel.rl("internal/instancing/main.frag")) .vertexApiImpl(Flywheel.rl("internal/instancing/api_impl.vert")) .fragmentApiImpl(Flywheel.rl("internal/instancing/api_impl.frag")) - .assembler(SamplerBufferComponent::create) + .assembler(BufferTextureInstanceComponent::create) .onLink(program -> program.setSamplerBinding("_flw_instances", Samplers.INSTANCE_BUFFER)) .build(); public static final Pipeline INDIRECT = Pipeline.builder() @@ -24,7 +24,7 @@ public final class Pipelines { .fragmentMain(Flywheel.rl("internal/indirect/main.frag")) .vertexApiImpl(Flywheel.rl("internal/indirect/api_impl.vert")) .fragmentApiImpl(Flywheel.rl("internal/indirect/api_impl.frag")) - .assembler(IndirectComponent::create) + .assembler(StructInstanceComponent::create) .onLink($ -> { }) .build(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/BufferTextureInstanceComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/BufferTextureInstanceComponent.java new file mode 100644 index 000000000..14f6f073d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/BufferTextureInstanceComponent.java @@ -0,0 +1,292 @@ +package com.jozufozu.flywheel.backend.compile.component; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.function.Function; + +import com.jozufozu.flywheel.Flywheel; +import com.jozufozu.flywheel.api.instance.InstanceType; +import com.jozufozu.flywheel.api.layout.FloatRepr; +import com.jozufozu.flywheel.api.layout.IntegerRepr; +import com.jozufozu.flywheel.api.layout.Layout; +import com.jozufozu.flywheel.api.layout.MatrixElementType; +import com.jozufozu.flywheel.api.layout.ScalarElementType; +import com.jozufozu.flywheel.api.layout.UnsignedIntegerRepr; +import com.jozufozu.flywheel.api.layout.ValueRepr; +import com.jozufozu.flywheel.api.layout.VectorElementType; +import com.jozufozu.flywheel.backend.compile.Pipeline; +import com.jozufozu.flywheel.backend.glsl.generate.FnSignature; +import com.jozufozu.flywheel.backend.glsl.generate.GlslBlock; +import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder; +import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr; +import com.jozufozu.flywheel.backend.glsl.generate.GlslStmt; +import com.jozufozu.flywheel.lib.math.MoreMath; + +public class BufferTextureInstanceComponent extends InstanceAssemblerComponent { + private static final String UNPACK_ARG = "index"; + + private static final String[] SWIZZLE_SELECTORS = { "x", "y", "z", "w" }; + + // Each function receives a uint expression as the input. + // For byte unpacking, the lowest 8 bits contain the value. For short unpacking, the lowest 16 bits contain the value. + // In both cases, all other bits are 0. + private static final EnumMap> INT_UNPACKING_FUNCS = new EnumMap<>(IntegerRepr.class); + private static final EnumMap> UINT_UNPACKING_FUNCS = new EnumMap<>(UnsignedIntegerRepr.class); + private static final EnumMap> FLOAT_UNPACKING_FUNCS = new EnumMap<>(FloatRepr.class); + + static { + INT_UNPACKING_FUNCS.put(IntegerRepr.BYTE, e -> signExtendByte(e).cast("int")); + INT_UNPACKING_FUNCS.put(IntegerRepr.SHORT, e -> signExtendShort(e).cast("int")); + INT_UNPACKING_FUNCS.put(IntegerRepr.INT, e -> e.cast("int")); + + UINT_UNPACKING_FUNCS.put(UnsignedIntegerRepr.UNSIGNED_BYTE, Function.identity()); + UINT_UNPACKING_FUNCS.put(UnsignedIntegerRepr.UNSIGNED_SHORT, Function.identity()); + UINT_UNPACKING_FUNCS.put(UnsignedIntegerRepr.UNSIGNED_INT, Function.identity()); + + FLOAT_UNPACKING_FUNCS.put(FloatRepr.BYTE, e -> signExtendByte(e).cast("int").cast("float")); + FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_BYTE, e -> signExtendByte(e).cast("int").cast("float").div(127f).clamp(-1, 1)); + FLOAT_UNPACKING_FUNCS.put(FloatRepr.UNSIGNED_BYTE, e -> e.cast("float")); + FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_UNSIGNED_BYTE, e -> e.cast("float").div(255f)); + + FLOAT_UNPACKING_FUNCS.put(FloatRepr.SHORT, e -> signExtendShort(e).cast("int").cast("float")); + FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_SHORT, e -> signExtendShort(e).cast("int").cast("float").div(32767f).clamp(-1, 1)); + FLOAT_UNPACKING_FUNCS.put(FloatRepr.UNSIGNED_SHORT, e -> e.cast("float")); + FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_UNSIGNED_SHORT, e -> e.cast("float").div(65535f)); + + FLOAT_UNPACKING_FUNCS.put(FloatRepr.INT, e -> e.cast("int").cast("float")); + FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_INT, e -> e.cast("int").cast("float").div(2147483647f).clamp(-1, 1)); + FLOAT_UNPACKING_FUNCS.put(FloatRepr.UNSIGNED_INT, e -> e.cast("float")); + FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_UNSIGNED_INT, e -> e.cast("float").div(4294967295f)); + + FLOAT_UNPACKING_FUNCS.put(FloatRepr.FLOAT, e -> e.callFunction("uintBitsToFloat")); // FIXME: GLSL 330+ + } + + public BufferTextureInstanceComponent(InstanceType type) { + super(type); + } + + public static BufferTextureInstanceComponent create(InstanceType type) { + return new BufferTextureInstanceComponent(type); + } + + public static BufferTextureInstanceComponent create(Pipeline.InstanceAssemblerContext ctx) { + return create(ctx.instanceType()); + } + + // https://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend + // Assumes bits higher than sign bit are zero + private static GlslExpr signExtendByte(GlslExpr e) { + return e.xor(0x80).sub(0x80); + } + + private static GlslExpr signExtendShort(GlslExpr e) { + return e.xor(0x8000).sub(0x8000); + } + + @Override + public String name() { + return Flywheel.rl("buffer_texture_instance_assembler").toString(); + } + + @Override + protected void generateUnpacking(GlslBuilder builder) { + var block = new GlslBlock(); + + // TODO: don't require writing to be 16 byte aligned + var texels = MoreMath.ceilingDiv(layout.byteSize(), 16); + + block.add(GlslStmt.raw("int base = " + UNPACK_ARG + " * " + texels + ";")); + + for (int i = 0; i < texels; i++) { + // Fetch all the texels for the given instance ahead of time to simplify the unpacking generators. + block.add(GlslStmt.raw("uvec4 u" + i + " = texelFetch(_flw_instances, base + " + i + ");")); + } + + var unpackArgs = new ArrayList(); + int uintOffset = 0; + for (Layout.Element element : layout.elements()) { + unpackArgs.add(unpackElement(element, uintOffset)); + // Element byte size is always a multiple of 4 + uintOffset += element.type().byteSize() / 4; + } + + block.ret(GlslExpr.call(STRUCT_NAME, unpackArgs)); + + builder._addRaw("uniform usamplerBuffer _flw_instances;"); + builder.blankLine(); + builder.function() + .signature(FnSignature.create() + .returnType(STRUCT_NAME) + .name(UNPACK_FN_NAME) + .arg("int", UNPACK_ARG) + .build()) + .body(block); + } + + private static GlslExpr unpackElement(Layout.Element element, int uintOffset) { + var type = element.type(); + + if (type instanceof ScalarElementType scalar) { + return unpackScalar(scalar, uintOffset); + } else if (type instanceof VectorElementType vector) { + return unpackVector(vector, uintOffset); + } else if (type instanceof MatrixElementType matrix) { + return unpackMatrix(matrix, uintOffset); + } + + throw new IllegalArgumentException("Unknown type " + type); + } + + private static GlslExpr unpackScalar(ScalarElementType type, int uintOffset) { + var repr = type.repr(); + Function unpackingFunc; + + if (repr instanceof IntegerRepr intRepr) { + unpackingFunc = INT_UNPACKING_FUNCS.get(intRepr); + } else if (repr instanceof UnsignedIntegerRepr uintRepr) { + unpackingFunc = UINT_UNPACKING_FUNCS.get(uintRepr); + } else if (repr instanceof FloatRepr floatRepr) { + unpackingFunc = FLOAT_UNPACKING_FUNCS.get(floatRepr); + } else { + throw new IllegalArgumentException("Unknown repr " + repr); + } + + if (isByteBacked(repr)) { + return unpackByteBackedScalar(uintOffset, unpackingFunc); + } else if (isShortBacked(repr)) { + return unpackShortBackedScalar(uintOffset, unpackingFunc); + } else { + return unpackIntBackedScalar(uintOffset, unpackingFunc); + } + } + + private static GlslExpr unpackVector(VectorElementType type, int uintOffset) { + var repr = type.repr(); + int size = type.size(); + Function unpackingFunc; + String outType; + + if (repr instanceof IntegerRepr intRepr) { + unpackingFunc = INT_UNPACKING_FUNCS.get(intRepr); + outType = "ivec" + size; + } else if (repr instanceof UnsignedIntegerRepr uintRepr) { + unpackingFunc = UINT_UNPACKING_FUNCS.get(uintRepr); + outType = "uvec" + size; + } else if (repr instanceof FloatRepr floatRepr) { + unpackingFunc = FLOAT_UNPACKING_FUNCS.get(floatRepr); + outType = "vec" + size; + } else { + throw new IllegalArgumentException("Unknown repr " + repr); + } + + if (isByteBacked(repr)) { + return unpackByteBackedVector(outType, size, uintOffset, unpackingFunc); + } else if (isShortBacked(repr)) { + return unpackShortBackedVector(outType, size, uintOffset, unpackingFunc); + } else { + return unpackIntBackedVector(outType, size, uintOffset, unpackingFunc); + } + } + + private static GlslExpr unpackMatrix(MatrixElementType type, int uintOffset) { + var repr = type.repr(); + int rows = type.rows(); + int columns = type.columns(); + Function unpackingFunc = FLOAT_UNPACKING_FUNCS.get(repr); + String outType = "mat" + columns + "x" + rows; + int size = rows * columns; + + if (isByteBacked(repr)) { + return unpackByteBackedVector(outType, size, uintOffset, unpackingFunc); + } else if (isShortBacked(repr)) { + return unpackShortBackedVector(outType, size, uintOffset, unpackingFunc); + } else { + return unpackIntBackedVector(outType, size, uintOffset, unpackingFunc); + } + } + + private static boolean isByteBacked(ValueRepr repr) { + return repr.byteSize() == Byte.BYTES; + } + + private static boolean isShortBacked(ValueRepr repr) { + return repr.byteSize() == Short.BYTES; + } + + private static GlslExpr unpackByteBackedScalar(int uintOffset, Function perElement) { + GlslExpr e; + if (BIG_ENDIAN) { + e = access(uintOffset) + .rsh(24) + .and(0xFF); + } else { + e = access(uintOffset) + .and(0xFF); + } + return perElement.apply(e); + } + + private static GlslExpr unpackShortBackedScalar(int uintOffset, Function perElement) { + GlslExpr e; + if (BIG_ENDIAN) { + e = access(uintOffset) + .rsh(16) + .and(0xFFFF); + } else { + e = access(uintOffset) + .and(0xFFFF); + } + return perElement.apply(e); + } + + private static GlslExpr unpackIntBackedScalar(int uintOffset, Function perElement) { + return perElement.apply(access(uintOffset)); + } + + private static GlslExpr unpackByteBackedVector(String outType, int size, int uintOffset, Function perElement) { + List args = new ArrayList<>(); + for (int i = 0; i < size; i++) { + // Vectors cannot contain more than 4 elements, but matrix unpacking treats the matrix as a long vector, which for mat4x4 would be the equivalent of a vec16. + int bitPos = (i % 4) * 8; + if (BIG_ENDIAN) { + bitPos = 24 - bitPos; + } + int wordOffset = i / 4; + var element = access(uintOffset + wordOffset) + .rsh(bitPos) + .and(0xFF); + args.add(perElement.apply(element)); + } + return GlslExpr.call(outType, args); + } + + private static GlslExpr unpackShortBackedVector(String outType, int size, int uintOffset, Function perElement) { + List args = new ArrayList<>(); + for (int i = 0; i < size; i++) { + int bitPos = (i % 2) * 16; + if (BIG_ENDIAN) { + bitPos = 16 - bitPos; + } + int wordOffset = i / 2; + var element = access(uintOffset + wordOffset) + .rsh(bitPos) + .and(0xFFFF); + args.add(perElement.apply(element)); + } + return GlslExpr.call(outType, args); + } + + private static GlslExpr unpackIntBackedVector(String outType, int size, int uintOffset, Function perElement) { + List args = new ArrayList<>(); + for (int i = 0; i < size; i++) { + args.add(perElement.apply(access(uintOffset + i))); + } + return GlslExpr.call(outType, args); + } + + private static GlslExpr access(int uintOffset) { + return GlslExpr.variable("u" + (uintOffset >> 2)) + .swizzle(SWIZZLE_SELECTORS[uintOffset & 3]); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/IndirectComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/IndirectComponent.java deleted file mode 100644 index 78d8f88a1..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/IndirectComponent.java +++ /dev/null @@ -1,349 +0,0 @@ -package com.jozufozu.flywheel.backend.compile.component; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.function.Function; - -import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.api.instance.InstanceType; -import com.jozufozu.flywheel.api.layout.FloatRepr; -import com.jozufozu.flywheel.api.layout.IntegerRepr; -import com.jozufozu.flywheel.api.layout.Layout; -import com.jozufozu.flywheel.api.layout.MatrixElementType; -import com.jozufozu.flywheel.api.layout.ScalarElementType; -import com.jozufozu.flywheel.api.layout.UnsignedIntegerRepr; -import com.jozufozu.flywheel.api.layout.VectorElementType; -import com.jozufozu.flywheel.backend.compile.LayoutInterpreter; -import com.jozufozu.flywheel.backend.compile.Pipeline; -import com.jozufozu.flywheel.backend.glsl.SourceComponent; -import com.jozufozu.flywheel.backend.glsl.generate.FnSignature; -import com.jozufozu.flywheel.backend.glsl.generate.GlslBlock; -import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder; -import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr; -import com.jozufozu.flywheel.backend.glsl.generate.GlslStruct; - -import net.minecraft.resources.ResourceLocation; - -public class IndirectComponent implements SourceComponent { - private static final String UNPACK_ARG = "p"; - private static final GlslExpr.Variable UNPACKING_VARIABLE = GlslExpr.variable(UNPACK_ARG); - private static final String STRUCT_NAME = "FlwInstance"; - private static final String PACKED_STRUCT_NAME = "FlwPackedInstance"; - private static final String UNPACK_FN_NAME = "_flw_unpackInstance"; - - private final Layout layout; - - public IndirectComponent(InstanceType type) { - this.layout = type.layout(); - } - - public static IndirectComponent create(Pipeline.InstanceAssemblerContext ctx) { - return create(ctx.instanceType()); - } - - public static IndirectComponent create(InstanceType instanceType) { - return new IndirectComponent(instanceType); - } - - @Override - public Collection included() { - return Collections.emptyList(); - } - - @Override - public ResourceLocation name() { - return Flywheel.rl("generated_indirect"); - } - - @Override - public String source() { - return generateIndirect(); - } - - public String generateIndirect() { - var builder = new GlslBuilder(); - - generateInstanceStruct(builder); - - builder.blankLine(); - - generateUnpacking(builder); - - builder.blankLine(); - - return builder.build(); - } - - private void generateInstanceStruct(GlslBuilder builder) { - var instance = builder.struct(); - instance.setName(STRUCT_NAME); - for (var element : layout.elements()) { - instance.addField(LayoutInterpreter.typeName(element.type()), element.name()); - } - } - - private void generateUnpacking(GlslBuilder builder) { - var packed = builder.struct(); - packed.setName(PACKED_STRUCT_NAME); - - var unpackArgs = new ArrayList(); - - for (Layout.Element element : layout.elements()) { - unpackArgs.add(unpackElement(element, packed)); - } - - var block = new GlslBlock(); - block.ret(GlslExpr.call(STRUCT_NAME, unpackArgs)); - - builder.blankLine(); - builder.function() - .signature(FnSignature.create() - .returnType(STRUCT_NAME) - .name(UNPACK_FN_NAME) - .arg(PACKED_STRUCT_NAME, UNPACK_ARG) - .build()) - .body(block); - } - - public static GlslExpr unpackElement(Layout.Element element, GlslStruct packed) { - // FIXME: I don't think we're unpacking signed byte/short values correctly - // FIXME: we definitely don't consider endianness. this all assumes little endian which works on my machine. - var type = element.type(); - var name = element.name(); - - if (type instanceof ScalarElementType scalar) { - return unpackScalar(name, packed, scalar); - } else if (type instanceof VectorElementType vector) { - return unpackVector(name, packed, vector); - } else if (type instanceof MatrixElementType matrix) { - return unpackMatrix(name, packed, matrix); - } - - throw new IllegalArgumentException("Unknown type " + type); - } - - private static GlslExpr unpackScalar(String fieldName, GlslStruct packed, ScalarElementType scalar) { - var repr = scalar.repr(); - - if (repr instanceof IntegerRepr intRepr) { - return unpackIntScalar(fieldName, intRepr, packed); - } else if (repr instanceof UnsignedIntegerRepr unsignedIntegerRepr) { - return unpackUnsignedScalar(fieldName, unsignedIntegerRepr, packed); - } else if (repr instanceof FloatRepr floatRepr) { - return unpackFloatScalar(fieldName, floatRepr, packed); - } - - throw new IllegalArgumentException("Unknown repr " + repr); - } - - private static GlslExpr unpackIntScalar(String fieldName, IntegerRepr intRepr, GlslStruct packed) { - return switch (intRepr) { - case BYTE -> unpackScalar(fieldName, packed, "uint", e -> e.and(0xFF) - .cast("int")); - case SHORT -> unpackScalar(fieldName, packed, "uint", e -> e.and(0xFFFF) - .cast("int")); - case INT -> unpackScalar(fieldName, packed, "int"); - }; - } - - private static GlslExpr unpackUnsignedScalar(String fieldName, UnsignedIntegerRepr repr, GlslStruct packed) { - return switch (repr) { - case UNSIGNED_BYTE -> unpackScalar(fieldName, packed, "uint", e -> e.and(0xFF)); - case UNSIGNED_SHORT -> unpackScalar(fieldName, packed, "uint", e -> e.and(0xFFFF)); - case UNSIGNED_INT -> unpackScalar(fieldName, packed, "uint"); - }; - } - - private static GlslExpr unpackFloatScalar(String fieldName, FloatRepr repr, GlslStruct packed) { - return switch (repr) { - case BYTE -> unpackScalar(fieldName, packed, "uint", e -> e.and(0xFF) - .cast("int") - .cast("float")); - case NORMALIZED_BYTE -> unpackScalar(fieldName, packed, "uint", e -> e.callFunction("unpackSnorm4x8") - .swizzle("x")); - case UNSIGNED_BYTE -> unpackScalar(fieldName, packed, "uint", e -> e.and(0xFF) - .cast("float")); - case NORMALIZED_UNSIGNED_BYTE -> - unpackScalar(fieldName, packed, "uint", e -> e.callFunction("unpackUnorm4x8") - .swizzle("x")); - case SHORT -> unpackScalar(fieldName, packed, "uint", e -> e.and(0xFFFF) - .cast("int") - .cast("float")); - case NORMALIZED_SHORT -> unpackScalar(fieldName, packed, "uint", e -> e.callFunction("unpackSnorm2x16") - .swizzle("x")); - case UNSIGNED_SHORT -> unpackScalar(fieldName, packed, "uint", e -> e.and(0xFFFF) - .cast("float")); - case NORMALIZED_UNSIGNED_SHORT -> - unpackScalar(fieldName, packed, "uint", e -> e.callFunction("unpackUnorm2x16") - .swizzle("x")); - case INT -> unpackScalar(fieldName, packed, "int", e -> e.cast("float")); - case NORMALIZED_INT -> unpackScalar(fieldName, packed, "int", e -> e.div(2147483647f) - .clamp(-1, 1)); - case UNSIGNED_INT -> unpackScalar(fieldName, packed, "uint", e -> e.cast("float")); - case NORMALIZED_UNSIGNED_INT -> unpackScalar(fieldName, packed, "uint", e -> e.div(4294967295f)); - case FLOAT -> unpackScalar(fieldName, packed, "float"); - }; - } - - private static GlslExpr unpackScalar(String fieldName, GlslStruct packed, String packedType) { - return unpackScalar(fieldName, packed, packedType, Function.identity()); - } - - private static GlslExpr unpackScalar(String fieldName, GlslStruct packed, String packedType, Function perElement) { - packed.addField(packedType, fieldName); - return perElement.apply(UNPACKING_VARIABLE.access(fieldName)); - } - - private static GlslExpr unpackVector(String fieldName, GlslStruct packed, VectorElementType vector) { - var repr = vector.repr(); - - int size = vector.size(); - - if (repr instanceof IntegerRepr intRepr) { - return unpackIntVector(fieldName, intRepr, packed, size); - } else if (repr instanceof UnsignedIntegerRepr unsignedIntegerRepr) { - return unpackUnsignedVector(fieldName, unsignedIntegerRepr, packed, size); - } else if (repr instanceof FloatRepr floatRepr) { - return unpackFloatVector(fieldName, floatRepr, packed, size); - } - - throw new IllegalArgumentException("Unknown repr " + repr); - } - - private static GlslExpr unpackIntVector(String fieldName, IntegerRepr repr, GlslStruct packed, int size) { - return switch (repr) { - case BYTE -> unpackByteBacked(fieldName, packed, size, "ivec" + size, e -> e.cast("int")); - case SHORT -> unpackShortBacked(fieldName, packed, size, "ivec" + size, e -> e.cast("int")); - case INT -> unpack(fieldName, packed, size, "int", "ivec" + size); - }; - } - - private static GlslExpr unpackUnsignedVector(String fieldName, UnsignedIntegerRepr unsignedIntegerRepr, GlslStruct packed, int size) { - return switch (unsignedIntegerRepr) { - case UNSIGNED_BYTE -> unpackByteBacked(fieldName, packed, size, "uvec" + size, e -> e.cast("uint")); - case UNSIGNED_SHORT -> unpackShortBacked(fieldName, packed, size, "uvec" + size, e -> e.cast("uint")); - case UNSIGNED_INT -> unpack(fieldName, packed, size, "uint", "uvec" + size); - }; - } - - private static GlslExpr unpackFloatVector(String fieldName, FloatRepr floatRepr, GlslStruct packed, int size) { - return switch (floatRepr) { - case NORMALIZED_BYTE -> unpackByteBuiltin(fieldName, packed, size, "unpackSnorm4x8"); - case NORMALIZED_UNSIGNED_BYTE -> unpackByteBuiltin(fieldName, packed, size, "unpackUnorm4x8"); - case NORMALIZED_SHORT -> unpackShortBuiltin(fieldName, packed, size, "unpackSnorm2x16"); - case NORMALIZED_UNSIGNED_SHORT -> unpackShortBuiltin(fieldName, packed, size, "unpackUnorm2x16"); - case NORMALIZED_INT -> unpack(fieldName, packed, size, "int", "vec" + size, e -> e.div(2147483647f) - .clamp(-1, 1)); - case NORMALIZED_UNSIGNED_INT -> - unpack(fieldName, packed, size, "uint", "vec" + size, e -> e.div(4294967295f)); - case BYTE -> unpackByteBacked(fieldName, packed, size, "vec" + size, e -> e.cast("int") - .cast("float")); - case UNSIGNED_BYTE -> unpackByteBacked(fieldName, packed, size, "vec" + size, e -> e.cast("float")); - case SHORT -> unpackShortBacked(fieldName, packed, size, "vec" + size, e -> e.cast("int") - .cast("float")); - case UNSIGNED_SHORT -> unpackShortBacked(fieldName, packed, size, "vec" + size, e -> e.cast("float")); - case INT -> unpack(fieldName, packed, size, "int", "vec" + size, e -> e.cast("float")); - case UNSIGNED_INT -> unpack(fieldName, packed, size, "float", "vec" + size, e -> e.cast("float")); - case FLOAT -> unpack(fieldName, packed, size, "float", "vec" + size); - }; - } - - private static GlslExpr unpackByteBacked(String fieldName, GlslStruct packed, int size, String outType, Function perElement) { - packed.addField("uint", fieldName); - List args = new ArrayList<>(); - for (int i = 0; i < size; i++) { - int bitPos = i * 8; - var element = UNPACKING_VARIABLE.access(fieldName) - .and(0xFF << bitPos) - .rsh(bitPos); - args.add(perElement.apply(element)); - } - return GlslExpr.call(outType, args); - } - - private static GlslExpr unpackShortBacked(String fieldName, GlslStruct packed, int size, String outType, Function perElement) { - List args = new ArrayList<>(); - for (int i = 0; i < size; i++) { - int unpackField = i / 2; - int bitPos = (i % 2) * 16; - var name = fieldName + "_" + unpackField; - if (bitPos == 0) { - // First time we're seeing this field, add it to the struct. - packed.addField("uint", name); - } - var element = UNPACKING_VARIABLE.access(name) - .and(0xFFFF << bitPos) - .rsh(bitPos); - args.add(perElement.apply(element)); - } - return GlslExpr.call(outType, args); - } - - private static GlslExpr unpack(String fieldName, GlslStruct packed, int size, String backingType, String outType) { - return unpack(fieldName, packed, size, backingType, outType, Function.identity()); - } - - private static GlslExpr unpack(String fieldName, GlslStruct packed, int size, String backingType, String outType, Function perElement) { - List args = new ArrayList<>(); - for (int i = 0; i < size; i++) { - var name = fieldName + "_" + i; - packed.addField(backingType, name); - args.add(UNPACKING_VARIABLE.access(name) - .transform(perElement)); - } - return GlslExpr.call(outType, args); - } - - private static GlslExpr unpackByteBuiltin(String fieldName, GlslStruct packed, int size, String func) { - packed.addField("uint", fieldName); - GlslExpr expr = UNPACKING_VARIABLE.access(fieldName) - .callFunction(func); - return switch (size) { - case 2 -> expr.swizzle("xy"); - case 3 -> expr.swizzle("xyz"); - case 4 -> expr; - default -> throw new IllegalArgumentException("Invalid vector size " + size); - }; - } - - private static GlslExpr unpackShortBuiltin(String fieldName, GlslStruct packed, int size, String func) { - if (size == 2) { - packed.addField("uint", fieldName); - return UNPACKING_VARIABLE.access(fieldName) - .callFunction(func); - } else { - var name0 = fieldName + "_" + 0; - var name1 = fieldName + "_" + 1; - packed.addField("uint", name0); - packed.addField("uint", name1); - GlslExpr xy = UNPACKING_VARIABLE.access(name0) - .callFunction(func); - - GlslExpr zw = UNPACKING_VARIABLE.access(name1) - .callFunction(func); - - if (size == 3) { - return GlslExpr.call("vec3", List.of(xy.swizzle("xy"), zw.swizzle("x"))); - } else { - return GlslExpr.call("vec4", List.of(xy, zw)); - } - } - } - - private static GlslExpr unpackMatrix(String name, GlslStruct packed, MatrixElementType matrix) { - var repr = matrix.repr(); - - int rows = matrix.rows(); - int columns = matrix.columns(); - - List args = new ArrayList<>(); - - for (int i = 0; i < columns; i++) { - args.add(unpackFloatVector(name + "_" + i, repr, packed, rows)); - } - - return GlslExpr.call("mat" + columns + "x" + rows, args); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceAssemblerComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceAssemblerComponent.java new file mode 100644 index 000000000..2a32399ef --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceAssemblerComponent.java @@ -0,0 +1,49 @@ +package com.jozufozu.flywheel.backend.compile.component; + +import java.nio.ByteOrder; +import java.util.Collection; +import java.util.Collections; + +import com.jozufozu.flywheel.api.instance.InstanceType; +import com.jozufozu.flywheel.api.layout.Layout; +import com.jozufozu.flywheel.backend.compile.LayoutInterpreter; +import com.jozufozu.flywheel.backend.glsl.SourceComponent; +import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder; + +public abstract class InstanceAssemblerComponent implements SourceComponent { + protected static final String STRUCT_NAME = "FlwInstance"; + protected static final String UNPACK_FN_NAME = "_flw_unpackInstance"; + + protected static final boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; + + protected final Layout layout; + + public InstanceAssemblerComponent(InstanceType type) { + layout = type.layout(); + } + + @Override + public Collection included() { + return Collections.emptyList(); + } + + @Override + public String source() { + var builder = new GlslBuilder(); + generateInstanceStruct(builder); + builder.blankLine(); + generateUnpacking(builder); + builder.blankLine(); + return builder.build(); + } + + protected void generateInstanceStruct(GlslBuilder builder) { + var instance = builder.struct(); + instance.setName(STRUCT_NAME); + for (var element : layout.elements()) { + instance.addField(LayoutInterpreter.typeName(element.type()), element.name()); + } + } + + protected abstract void generateUnpacking(GlslBuilder builder); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/SamplerBufferComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/SamplerBufferComponent.java deleted file mode 100644 index cd19996ce..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/SamplerBufferComponent.java +++ /dev/null @@ -1,322 +0,0 @@ -package com.jozufozu.flywheel.backend.compile.component; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.function.Function; - -import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.api.instance.InstanceType; -import com.jozufozu.flywheel.api.layout.FloatRepr; -import com.jozufozu.flywheel.api.layout.IntegerRepr; -import com.jozufozu.flywheel.api.layout.Layout; -import com.jozufozu.flywheel.api.layout.MatrixElementType; -import com.jozufozu.flywheel.api.layout.ScalarElementType; -import com.jozufozu.flywheel.api.layout.UnsignedIntegerRepr; -import com.jozufozu.flywheel.api.layout.VectorElementType; -import com.jozufozu.flywheel.backend.compile.LayoutInterpreter; -import com.jozufozu.flywheel.backend.compile.Pipeline; -import com.jozufozu.flywheel.backend.glsl.SourceComponent; -import com.jozufozu.flywheel.backend.glsl.generate.FnSignature; -import com.jozufozu.flywheel.backend.glsl.generate.GlslBlock; -import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder; -import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr; -import com.jozufozu.flywheel.backend.glsl.generate.GlslStmt; -import com.jozufozu.flywheel.lib.math.MoreMath; - -import net.minecraft.resources.ResourceLocation; - -public class SamplerBufferComponent implements SourceComponent { - private static final String STRUCT_NAME = "FlwInstance"; - private static final String UNPACK_FN_NAME = "_flw_unpackInstance"; - private static final String UNPACK_ARG = "index"; - - private final Layout layout; - - public SamplerBufferComponent(InstanceType type) { - this.layout = type.layout(); - } - - public static SamplerBufferComponent create(Pipeline.InstanceAssemblerContext ctx) { - return create(ctx.instanceType()); - } - - public static SamplerBufferComponent create(InstanceType instanceType) { - return new SamplerBufferComponent(instanceType); - } - - @Override - public Collection included() { - return Collections.emptyList(); - } - - @Override - public ResourceLocation name() { - return Flywheel.rl("generated_instancing"); - } - - @Override - public String source() { - return generateIndirect(); - } - - public String generateIndirect() { - var builder = new GlslBuilder(); - - generateInstanceStruct(builder); - - builder.blankLine(); - - builder._addRaw("uniform usamplerBuffer _flw_instances;"); - - builder.blankLine(); - - generateUnpacking(builder); - - builder.blankLine(); - - return builder.build(); - } - - private void generateInstanceStruct(GlslBuilder builder) { - var instance = builder.struct(); - instance.setName(STRUCT_NAME); - for (var element : layout.elements()) { - instance.addField(LayoutInterpreter.typeName(element.type()), element.name()); - } - } - - private void generateUnpacking(GlslBuilder builder) { - var block = new GlslBlock(); - - var texels = MoreMath.ceilingDiv(layout.byteSize(), 16); - - block.add(GlslStmt.raw("int base = " + UNPACK_ARG + " * " + texels + ";")); - - for (int i = 0; i < texels; i++) { - // Fetch all the texels for the given instance ahead of time to simplify the unpacking generators. - block.add(GlslStmt.raw("uvec4 u" + i + " = texelFetch(_flw_instances, base + " + i + ");")); - } - - var unpackArgs = new ArrayList(); - int uintOffset = 0; - for (Layout.Element element : layout.elements()) { - unpackArgs.add(unpackElement(element, uintOffset)); - uintOffset += MoreMath.ceilingDiv(element.type().byteSize(), 4); - } - - block.ret(GlslExpr.call(STRUCT_NAME, unpackArgs)); - - builder.function() - .signature(FnSignature.create() - .returnType(STRUCT_NAME) - .name(UNPACK_FN_NAME) - .arg("int", UNPACK_ARG) - .build()) - .body(block); - } - - public static GlslExpr access(int uintOffset) { - return GlslExpr.variable("u" + (uintOffset >> 2)) - .swizzle(String.valueOf("xyzw".charAt(uintOffset & 3))); - } - - // TODO: deduplicate this with IndirectComponent somehow? - - public static GlslExpr unpackElement(Layout.Element element, int uintOffset) { - // FIXME: I don't think we're unpacking signed byte/short values correctly - // FIXME: we definitely don't consider endianness. this all assumes little endian which works on my machine. - var type = element.type(); - - if (type instanceof ScalarElementType scalar) { - return unpackScalar(uintOffset, scalar); - } else if (type instanceof VectorElementType vector) { - return unpackVector(uintOffset, vector); - } else if (type instanceof MatrixElementType matrix) { - return unpackMatrix(uintOffset, matrix); - } - - throw new IllegalArgumentException("Unknown type " + type); - } - - private static GlslExpr unpackScalar(int uintOffset, ScalarElementType scalar) { - var repr = scalar.repr(); - - if (repr instanceof IntegerRepr intRepr) { - return unpackIntScalar(uintOffset, intRepr); - } else if (repr instanceof UnsignedIntegerRepr unsignedIntegerRepr) { - return unpackUnsignedScalar(uintOffset, unsignedIntegerRepr); - } else if (repr instanceof FloatRepr floatRepr) { - return unpackFloatScalar(uintOffset, floatRepr); - } - - throw new IllegalArgumentException("Unknown repr " + repr); - } - - private static GlslExpr unpackIntScalar(int uintOffset, IntegerRepr intRepr) { - return switch (intRepr) { - case BYTE -> unpackScalar(uintOffset, e -> e.and(0xFF) - .cast("int")); - case SHORT -> unpackScalar(uintOffset, e -> e.and(0xFFFF) - .cast("int")); - case INT -> unpackScalar(uintOffset); - }; - } - - private static GlslExpr unpackUnsignedScalar(int uintOffset, UnsignedIntegerRepr repr) { - return switch (repr) { - case UNSIGNED_BYTE -> unpackScalar(uintOffset, e -> e.and(0xFF)); - case UNSIGNED_SHORT -> unpackScalar(uintOffset, e -> e.and(0xFFFF)); - case UNSIGNED_INT -> unpackScalar(uintOffset); - }; - } - - private static GlslExpr unpackFloatScalar(int uintOffset, FloatRepr repr) { - return switch (repr) { - case BYTE -> unpackScalar(uintOffset, e -> e.and(0xFF) - .cast("int") - .cast("float")); - case NORMALIZED_BYTE -> unpackScalar(uintOffset, e -> e.callFunction("unpackSnorm4x8") - .swizzle("x")); - case UNSIGNED_BYTE -> unpackScalar(uintOffset, e -> e.and(0xFF) - .cast("float")); - case NORMALIZED_UNSIGNED_BYTE -> - unpackScalar(uintOffset, e -> e.callFunction("unpackUnorm4x8") - .swizzle("x")); - case SHORT -> unpackScalar(uintOffset, e -> e.and(0xFFFF) - .cast("int") - .cast("float")); - case NORMALIZED_SHORT -> unpackScalar(uintOffset, e -> e.callFunction("unpackSnorm2x16") - .swizzle("x")); - case UNSIGNED_SHORT -> unpackScalar(uintOffset, e -> e.and(0xFFFF) - .cast("float")); - case NORMALIZED_UNSIGNED_SHORT -> - unpackScalar(uintOffset, e -> e.callFunction("unpackUnorm2x16") - .swizzle("x")); - case INT -> unpackScalar(uintOffset, e -> e.cast("int").cast("float")); - case NORMALIZED_INT -> unpackScalar(uintOffset, e -> e.div(2147483647f) - .clamp(-1, 1)); - case UNSIGNED_INT -> unpackScalar(uintOffset, e -> e.cast("float")); - case NORMALIZED_UNSIGNED_INT -> unpackScalar(uintOffset, e -> e.div(4294967295f)); - case FLOAT -> unpackScalar(uintOffset, e -> e.callFunction("uintBitsToFloat")); - }; - } - - private static GlslExpr unpackScalar(int uintOffset) { - return unpackScalar(uintOffset, Function.identity()); - } - - private static GlslExpr unpackScalar(int uintOffset, Function perElement) { - return perElement.apply(access(uintOffset)); - } - - private static GlslExpr unpackVector(int uintOffset, VectorElementType vector) { - var repr = vector.repr(); - - int size = vector.size(); - - if (repr instanceof IntegerRepr intRepr) { - return unpackIntVector(uintOffset, intRepr, size); - } else if (repr instanceof UnsignedIntegerRepr unsignedIntegerRepr) { - return unpackUnsignedVector(uintOffset, unsignedIntegerRepr, size); - } else if (repr instanceof FloatRepr floatRepr) { - return unpackFloatVector(uintOffset, floatRepr, size); - } - - throw new IllegalArgumentException("Unknown repr " + repr); - } - - private static GlslExpr unpackIntVector(int uintOffset, IntegerRepr repr, int size) { - return switch (repr) { - case BYTE -> unpackByteBacked(uintOffset, size, "ivec" + size, e -> e.cast("int")); - case SHORT -> unpackShortBacked(uintOffset, size, "ivec" + size, e -> e.cast("int")); - case INT -> unpack(uintOffset, size, "ivec" + size); - }; - } - - private static GlslExpr unpackUnsignedVector(int uintOffset, UnsignedIntegerRepr unsignedIntegerRepr, int size) { - return switch (unsignedIntegerRepr) { - case UNSIGNED_BYTE -> unpackByteBacked(uintOffset, size, "uvec" + size, Function.identity()); - case UNSIGNED_SHORT -> unpackShortBacked(uintOffset, size, "uvec" + size, Function.identity()); - case UNSIGNED_INT -> unpack(uintOffset, size, "uvec" + size); - }; - } - - private static GlslExpr unpackFloatVector(int uintOffset, FloatRepr floatRepr, int size) { - return switch (floatRepr) { - case NORMALIZED_BYTE -> unpackByteBacked(uintOffset, size, "vec" + size, e -> e.div(127).clamp(-1, 1)); - case NORMALIZED_UNSIGNED_BYTE -> unpackByteBacked(uintOffset, size, "vec" + size, e -> e.div(255)); - case NORMALIZED_SHORT -> unpackShortBacked(uintOffset, size, "vec" + size, e -> e.div(32727).clamp(-1, 1)); - case NORMALIZED_UNSIGNED_SHORT -> unpackShortBacked(uintOffset, size, "vec" + size, e -> e.div(65535)); - case NORMALIZED_INT -> unpack(uintOffset, size, "vec" + size, e -> e.div(2147483647f) - .clamp(-1, 1)); - case NORMALIZED_UNSIGNED_INT -> - unpack(uintOffset, size, "vec" + size, e -> e.div(4294967295f)); - case BYTE -> unpackByteBacked(uintOffset, size, "vec" + size, e -> e.cast("int") - .cast("float")); - case UNSIGNED_BYTE -> unpackByteBacked(uintOffset, size, "vec" + size, e -> e.cast("float")); - case SHORT -> unpackShortBacked(uintOffset, size, "vec" + size, e -> e.cast("int") - .cast("float")); - case UNSIGNED_SHORT -> unpackShortBacked(uintOffset, size, "vec" + size, e -> e.cast("float")); - case INT -> unpack(uintOffset, size, "vec" + size, e -> e.cast("float")); - case UNSIGNED_INT -> unpack(uintOffset, size, "vec" + size, e -> e.cast("int").cast("float")); - case FLOAT -> unpack(uintOffset, size, "vec" + size, e -> e.callFunction("uintBitsToFloat")); - }; - } - - private static GlslExpr unpackByteBacked(int uintOffset, int size, String outType, Function perElement) { - List args = new ArrayList<>(); - for (int i = 0; i < size; i++) { - int bitPos = i * 8; - var element = access(uintOffset) - .and(0xFF << bitPos) - .rsh(bitPos); - args.add(perElement.apply(element)); - } - return GlslExpr.call(outType, args); - } - - private static GlslExpr unpackShortBacked(int uintOffset, int size, String outType, Function perElement) { - List args = new ArrayList<>(); - for (int i = 0; i < size; i++) { - int bitPos = (i % 2) * 16; - int wordOffset = i / 2; - var element = access(uintOffset + wordOffset) - .and(0xFFFF << bitPos) - .rsh(bitPos); - args.add(perElement.apply(element)); - } - return GlslExpr.call(outType, args); - } - - private static GlslExpr unpack(int uintOffset, int size, String outType) { - return unpack(uintOffset, size, outType, Function.identity()); - } - - private static GlslExpr unpack(int uintOffset, int size, String outType, Function perElement) { - List args = new ArrayList<>(); - for (int i = 0; i < size; i++) { - args.add(access(uintOffset + i) - .transform(perElement)); - } - return GlslExpr.call(outType, args); - } - - private static GlslExpr unpackMatrix(int uintOffset, MatrixElementType matrix) { - var repr = matrix.repr(); - - int rows = matrix.rows(); - int columns = matrix.columns(); - - int columnWordSize = MoreMath.ceilingDiv(rows * repr.byteSize(), 4); - - List args = new ArrayList<>(); - - for (int i = 0; i < columns; i++) { - args.add(unpackFloatVector(uintOffset + i * columnWordSize, repr, rows)); - } - - return GlslExpr.call("mat" + columns + "x" + rows, args); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/StringSubstitutionSourceComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/StringSubstitutionComponent.java similarity index 69% rename from src/main/java/com/jozufozu/flywheel/backend/compile/component/StringSubstitutionSourceComponent.java rename to src/main/java/com/jozufozu/flywheel/backend/compile/component/StringSubstitutionComponent.java index 7d6b4c7a6..071865d80 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/StringSubstitutionSourceComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/StringSubstitutionComponent.java @@ -3,20 +3,19 @@ package com.jozufozu.flywheel.backend.compile.component; import java.util.Collection; import java.util.Map; +import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.glsl.SourceComponent; -import net.minecraft.resources.ResourceLocation; - -public final class StringSubstitutionSourceComponent implements SourceComponent { +public final class StringSubstitutionComponent implements SourceComponent { private final SourceComponent source; private final Map replacements; private final String sourceString; - public StringSubstitutionSourceComponent(SourceComponent source, String find, String replace) { + public StringSubstitutionComponent(SourceComponent source, String find, String replace) { this(source, Map.of(find, replace)); } - public StringSubstitutionSourceComponent(SourceComponent source, Map replacements) { + public StringSubstitutionComponent(SourceComponent source, Map replacements) { this.source = source; this.replacements = replacements; this.sourceString = source.source(); @@ -42,8 +41,8 @@ public final class StringSubstitutionSourceComponent implements SourceComponent } @Override - public ResourceLocation name() { - return source.name().withSuffix("_string_substitution"); + public String name() { + return Flywheel.rl("string_substitution").toString() + " / " + source.name(); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/StructInstanceComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/StructInstanceComponent.java new file mode 100644 index 000000000..b2177978e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/StructInstanceComponent.java @@ -0,0 +1,308 @@ +package com.jozufozu.flywheel.backend.compile.component; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import com.jozufozu.flywheel.Flywheel; +import com.jozufozu.flywheel.api.instance.InstanceType; +import com.jozufozu.flywheel.api.layout.FloatRepr; +import com.jozufozu.flywheel.api.layout.IntegerRepr; +import com.jozufozu.flywheel.api.layout.Layout; +import com.jozufozu.flywheel.api.layout.MatrixElementType; +import com.jozufozu.flywheel.api.layout.ScalarElementType; +import com.jozufozu.flywheel.api.layout.UnsignedIntegerRepr; +import com.jozufozu.flywheel.api.layout.VectorElementType; +import com.jozufozu.flywheel.backend.compile.Pipeline; +import com.jozufozu.flywheel.backend.glsl.generate.FnSignature; +import com.jozufozu.flywheel.backend.glsl.generate.GlslBlock; +import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder; +import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr; +import com.jozufozu.flywheel.backend.glsl.generate.GlslStruct; + +// TODO: Use a uvec4[] instead of a FlwPackedInstance[] in the SSBO to store data and reuse unpacking code from BufferTextureInstanceComponent. +// Unpacking should be moved from BufferTextureInstanceComponent to InstanceAssemblerComponent and this class should be renamed to SsboInstanceComponent. Further abstraction may be possible. +// Currently, some of the unpacking code generated by this class is incorrect. It is correct in BufferTextureInstanceComponent. +public class StructInstanceComponent extends InstanceAssemblerComponent { + private static final String UNPACK_ARG = "p"; + private static final GlslExpr.Variable UNPACKING_VARIABLE = GlslExpr.variable(UNPACK_ARG); + private static final String PACKED_STRUCT_NAME = "FlwPackedInstance"; + + public StructInstanceComponent(InstanceType type) { + super(type); + } + + public static StructInstanceComponent create(InstanceType type) { + return new StructInstanceComponent(type); + } + + public static StructInstanceComponent create(Pipeline.InstanceAssemblerContext ctx) { + return create(ctx.instanceType()); + } + + @Override + public String name() { + return Flywheel.rl("struct_instance_assembler").toString(); + } + + @Override + protected void generateUnpacking(GlslBuilder builder) { + var packed = builder.struct(); + packed.setName(PACKED_STRUCT_NAME); + + var unpackArgs = new ArrayList(); + + for (Layout.Element element : layout.elements()) { + unpackArgs.add(unpackElement(element, packed)); + } + + var block = new GlslBlock(); + block.ret(GlslExpr.call(STRUCT_NAME, unpackArgs)); + + builder.blankLine(); + builder.function() + .signature(FnSignature.create() + .returnType(STRUCT_NAME) + .name(UNPACK_FN_NAME) + .arg(PACKED_STRUCT_NAME, UNPACK_ARG) + .build()) + .body(block); + } + + private static GlslExpr unpackElement(Layout.Element element, GlslStruct packed) { + // FIXME: I don't think we're unpacking signed byte/short values correctly + // FIXME: we definitely don't consider endianness. this all assumes little endian which works on my machine. + var type = element.type(); + var name = element.name(); + + if (type instanceof ScalarElementType scalar) { + return unpackScalar(scalar, name, packed); + } else if (type instanceof VectorElementType vector) { + return unpackVector(vector, name, packed); + } else if (type instanceof MatrixElementType matrix) { + return unpackMatrix(matrix, name, packed); + } + + throw new IllegalArgumentException("Unknown type " + type); + } + + private static GlslExpr unpackScalar(ScalarElementType type, String fieldName, GlslStruct packed) { + var repr = type.repr(); + + if (repr instanceof IntegerRepr intRepr) { + return unpackIntScalar(intRepr, fieldName, packed); + } else if (repr instanceof UnsignedIntegerRepr uintRepr) { + return unpackUintScalar(uintRepr, fieldName, packed); + } else if (repr instanceof FloatRepr floatRepr) { + return unpackFloatScalar(floatRepr, fieldName, packed); + } + + throw new IllegalArgumentException("Unknown repr " + repr); + } + + private static GlslExpr unpackIntScalar(IntegerRepr repr, String fieldName, GlslStruct packed) { + return switch (repr) { + case BYTE -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFF) + .cast("int")); + case SHORT -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFFFF) + .cast("int")); + case INT -> unpackScalar("int", fieldName, packed); + }; + } + + private static GlslExpr unpackUintScalar(UnsignedIntegerRepr repr, String fieldName, GlslStruct packed) { + return switch (repr) { + case UNSIGNED_BYTE -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFF)); + case UNSIGNED_SHORT -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFFFF)); + case UNSIGNED_INT -> unpackScalar("uint", fieldName, packed); + }; + } + + private static GlslExpr unpackFloatScalar(FloatRepr repr, String fieldName, GlslStruct packed) { + return switch (repr) { + case BYTE -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFF) + .cast("int") + .cast("float")); + case NORMALIZED_BYTE -> unpackScalar("uint", fieldName, packed, e -> e.callFunction("unpackSnorm4x8") + .swizzle("x")); + case UNSIGNED_BYTE -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFF) + .cast("float")); + case NORMALIZED_UNSIGNED_BYTE -> + unpackScalar("uint", fieldName, packed, e -> e.callFunction("unpackUnorm4x8") + .swizzle("x")); + case SHORT -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFFFF) + .cast("int") + .cast("float")); + case NORMALIZED_SHORT -> unpackScalar("uint", fieldName, packed, e -> e.callFunction("unpackSnorm2x16") + .swizzle("x")); + case UNSIGNED_SHORT -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFFFF) + .cast("float")); + case NORMALIZED_UNSIGNED_SHORT -> + unpackScalar("uint", fieldName, packed, e -> e.callFunction("unpackUnorm2x16") + .swizzle("x")); + case INT -> unpackScalar("int", fieldName, packed, e -> e.cast("float")); + case NORMALIZED_INT -> unpackScalar("int", fieldName, packed, e -> e.div(2147483647f) + .clamp(-1, 1)); + case UNSIGNED_INT -> unpackScalar("uint", fieldName, packed, e -> e.cast("float")); + case NORMALIZED_UNSIGNED_INT -> unpackScalar("uint", fieldName, packed, e -> e.div(4294967295f)); + case FLOAT -> unpackScalar("float", fieldName, packed); + }; + } + + private static GlslExpr unpackScalar(String packedType, String fieldName, GlslStruct packed) { + return unpackScalar(packedType, fieldName, packed, Function.identity()); + } + + private static GlslExpr unpackScalar(String packedType, String fieldName, GlslStruct packed, Function perElement) { + packed.addField(packedType, fieldName); + return perElement.apply(UNPACKING_VARIABLE.access(fieldName)); + } + + private static GlslExpr unpackVector(VectorElementType type, String fieldName, GlslStruct packed) { + var repr = type.repr(); + int size = type.size(); + + if (repr instanceof IntegerRepr intRepr) { + return unpackIntVector(intRepr, size, fieldName, packed); + } else if (repr instanceof UnsignedIntegerRepr uintRepr) { + return unpackUintVector(uintRepr, size, fieldName, packed); + } else if (repr instanceof FloatRepr floatRepr) { + return unpackFloatVector(floatRepr, size, fieldName, packed); + } + + throw new IllegalArgumentException("Unknown repr " + repr); + } + + private static GlslExpr unpackIntVector(IntegerRepr repr, int size, String fieldName, GlslStruct packed) { + return switch (repr) { + case BYTE -> unpackByteBackedVector("ivec" + size, size, fieldName, packed, e -> e.cast("int")); + case SHORT -> unpackShortBackedVector("ivec" + size, size, fieldName, packed, e -> e.cast("int")); + case INT -> unpackVector("ivec" + size, size, "int", fieldName, packed); + }; + } + + private static GlslExpr unpackUintVector(UnsignedIntegerRepr repr, int size, String fieldName, GlslStruct packed) { + return switch (repr) { + case UNSIGNED_BYTE -> unpackByteBackedVector("uvec" + size, size, fieldName, packed, e -> e.cast("uint")); + case UNSIGNED_SHORT -> unpackShortBackedVector("uvec" + size, size, fieldName, packed, e -> e.cast("uint")); + case UNSIGNED_INT -> unpackVector("uvec" + size, size, "uint", fieldName, packed); + }; + } + + private static GlslExpr unpackFloatVector(FloatRepr repr, int size, String fieldName, GlslStruct packed) { + return switch (repr) { + case BYTE -> unpackByteBackedVector("vec" + size, size, fieldName, packed, e -> e.cast("int") + .cast("float")); + case NORMALIZED_BYTE -> unpackByteBuiltinVector(size, fieldName, packed, "unpackSnorm4x8"); + case UNSIGNED_BYTE -> unpackByteBackedVector("vec" + size, size, fieldName, packed, e -> e.cast("float")); + case NORMALIZED_UNSIGNED_BYTE -> unpackByteBuiltinVector(size, fieldName, packed, "unpackUnorm4x8"); + case SHORT -> unpackShortBackedVector("vec" + size, size, fieldName, packed, e -> e.cast("int") + .cast("float")); + case NORMALIZED_SHORT -> unpackShortBuiltinVector(size, fieldName, packed, "unpackSnorm2x16"); + case UNSIGNED_SHORT -> unpackShortBackedVector("vec" + size, size, fieldName, packed, e -> e.cast("float")); + case NORMALIZED_UNSIGNED_SHORT -> unpackShortBuiltinVector(size, fieldName, packed, "unpackUnorm2x16"); + case INT -> unpackVector("vec" + size, size, "int", fieldName, packed, e -> e.cast("float")); + case NORMALIZED_INT -> unpackVector("vec" + size, size, "int", fieldName, packed, e -> e.div(2147483647f) + .clamp(-1, 1)); + case UNSIGNED_INT -> unpackVector("vec" + size, size, "float", fieldName, packed, e -> e.cast("float")); + case NORMALIZED_UNSIGNED_INT -> + unpackVector("vec" + size, size, "uint", fieldName, packed, e -> e.div(4294967295f)); + case FLOAT -> unpackVector("vec" + size, size, "float", fieldName, packed); + }; + } + + private static GlslExpr unpackByteBackedVector(String outType, int size, String fieldName, GlslStruct packed, Function perElement) { + packed.addField("uint", fieldName); + List args = new ArrayList<>(); + for (int i = 0; i < size; i++) { + int bitPos = i * 8; + var element = UNPACKING_VARIABLE.access(fieldName) + .rsh(bitPos) + .and(0xFF); + args.add(perElement.apply(element)); + } + return GlslExpr.call(outType, args); + } + + private static GlslExpr unpackShortBackedVector(String outType, int size, String fieldName, GlslStruct packed, Function perElement) { + List args = new ArrayList<>(); + for (int i = 0; i < size; i++) { + int unpackField = i / 2; + int bitPos = (i % 2) * 16; + var name = fieldName + "_" + unpackField; + if (bitPos == 0) { + // First time we're seeing this field, add it to the struct. + packed.addField("uint", name); + } + var element = UNPACKING_VARIABLE.access(name) + .rsh(bitPos) + .and(0xFFFF); + args.add(perElement.apply(element)); + } + return GlslExpr.call(outType, args); + } + + private static GlslExpr unpackByteBuiltinVector(int size, String fieldName, GlslStruct packed, String func) { + packed.addField("uint", fieldName); + GlslExpr expr = UNPACKING_VARIABLE.access(fieldName) + .callFunction(func); + return switch (size) { + case 2 -> expr.swizzle("xy"); + case 3 -> expr.swizzle("xyz"); + case 4 -> expr; + default -> throw new IllegalArgumentException("Invalid vector size " + size); + }; + } + + private static GlslExpr unpackShortBuiltinVector(int size, String fieldName, GlslStruct packed, String func) { + if (size == 2) { + packed.addField("uint", fieldName); + return UNPACKING_VARIABLE.access(fieldName) + .callFunction(func); + } else { + var name0 = fieldName + "_" + 0; + var name1 = fieldName + "_" + 1; + packed.addField("uint", name0); + packed.addField("uint", name1); + GlslExpr xy = UNPACKING_VARIABLE.access(name0) + .callFunction(func); + + GlslExpr zw = UNPACKING_VARIABLE.access(name1) + .callFunction(func); + + if (size == 3) { + return GlslExpr.call("vec3", List.of(xy.swizzle("xy"), zw.swizzle("x"))); + } else { + return GlslExpr.call("vec4", List.of(xy, zw)); + } + } + } + + private static GlslExpr unpackVector(String outType, int size, String backingType, String fieldName, GlslStruct packed) { + return unpackVector(outType, size, backingType, fieldName, packed, Function.identity()); + } + + private static GlslExpr unpackVector(String outType, int size, String backingType, String fieldName, GlslStruct packed, Function perElement) { + List args = new ArrayList<>(); + for (int i = 0; i < size; i++) { + var name = fieldName + "_" + i; + packed.addField(backingType, name); + args.add(perElement.apply(UNPACKING_VARIABLE.access(name))); + } + return GlslExpr.call(outType, args); + } + + private static GlslExpr unpackMatrix(MatrixElementType type, String name, GlslStruct packed) { + var repr = type.repr(); + int rows = type.rows(); + int columns = type.columns(); + + List args = new ArrayList<>(); + + for (int i = 0; i < columns; i++) { + args.add(unpackFloatVector(repr, rows, name + "_" + i, packed)); + } + + return GlslExpr.call("mat" + columns + "x" + rows, args); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/UberShaderComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/UberShaderComponent.java index d80a63c67..7f8ecad89 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/UberShaderComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/UberShaderComponent.java @@ -9,6 +9,7 @@ import org.jetbrains.annotations.Nullable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.compile.core.SourceLoader; import com.jozufozu.flywheel.backend.glsl.SourceComponent; import com.jozufozu.flywheel.backend.glsl.SourceFile; @@ -24,9 +25,9 @@ public class UberShaderComponent implements SourceComponent { private final ResourceLocation name; private final GlslExpr switchArg; private final List functionsToAdapt; - private final List adaptedComponents; + private final List adaptedComponents; - private UberShaderComponent(ResourceLocation name, GlslExpr switchArg, List functionsToAdapt, List adaptedComponents) { + private UberShaderComponent(ResourceLocation name, GlslExpr switchArg, List functionsToAdapt, List adaptedComponents) { this.name = name; this.switchArg = switchArg; this.functionsToAdapt = functionsToAdapt; @@ -38,8 +39,8 @@ public class UberShaderComponent implements SourceComponent { } @Override - public ResourceLocation name() { - return name; + public String name() { + return Flywheel.rl("uber_shader").toString() + " / " + name; } @Override @@ -141,7 +142,7 @@ public class UberShaderComponent implements SourceComponent { throw new NullPointerException("Switch argument must be set"); } - var transformed = ImmutableList.builder(); + var transformed = ImmutableList.builder(); boolean errored = false; int index = 0; @@ -150,7 +151,7 @@ public class UberShaderComponent implements SourceComponent { final int finalIndex = index; if (sourceFile != null) { var adapterMap = createAdapterMap(adaptedFunctions, fnName -> "_" + fnName + "_" + finalIndex); - transformed.add(new StringSubstitutionSourceComponent(sourceFile, adapterMap)); + transformed.add(new StringSubstitutionComponent(sourceFile, adapterMap)); } else { errored = true; } @@ -163,17 +164,17 @@ public class UberShaderComponent implements SourceComponent { return new UberShaderComponent(name, switchArg, adaptedFunctions, transformed.build()); } - } - private static ImmutableMap createAdapterMap(List adaptedFunctions, UnaryOperator nameAdapter) { - ImmutableMap.Builder builder = ImmutableMap.builder(); + private static ImmutableMap createAdapterMap(List adaptedFunctions, UnaryOperator nameAdapter) { + ImmutableMap.Builder builder = ImmutableMap.builder(); - for (var adapted : adaptedFunctions) { - var fnName = adapted.signature() - .name(); - builder.put(fnName, nameAdapter.apply(fnName)); + for (var adapted : adaptedFunctions) { + var fnName = adapted.signature() + .name(); + builder.put(fnName, nameAdapter.apply(fnName)); + } + + return builder.build(); } - - return builder.build(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/core/Compilation.java b/src/main/java/com/jozufozu/flywheel/backend/compile/core/Compilation.java index 9bed55fc5..3fea1ffb7 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/core/Compilation.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/core/Compilation.java @@ -95,7 +95,7 @@ public class Compilation { fullSource.append("\n#line 0 ") .append(fileId) .append(" // ") - .append(file.name) + .append(file.name()) .append('\n'); } else { // Add extra newline to keep line numbers consistent diff --git a/src/main/java/com/jozufozu/flywheel/backend/glsl/SourceComponent.java b/src/main/java/com/jozufozu/flywheel/backend/glsl/SourceComponent.java index 55cdbe00d..2219f845f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/glsl/SourceComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/glsl/SourceComponent.java @@ -2,12 +2,10 @@ package com.jozufozu.flywheel.backend.glsl; import java.util.Collection; -import net.minecraft.resources.ResourceLocation; - public interface SourceComponent { Collection included(); String source(); - ResourceLocation name(); + String name(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/glsl/SourceFile.java b/src/main/java/com/jozufozu/flywheel/backend/glsl/SourceFile.java index cda77ffd6..09daa2951 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/glsl/SourceFile.java +++ b/src/main/java/com/jozufozu/flywheel/backend/glsl/SourceFile.java @@ -125,8 +125,8 @@ public class SourceFile implements SourceComponent { } @Override - public ResourceLocation name() { - return name; + public String name() { + return name.toString(); } public Span getLineSpan(int lineNo) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/glsl/generate/BinOp.java b/src/main/java/com/jozufozu/flywheel/backend/glsl/generate/BinOp.java index d3f883454..25abf4c84 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/glsl/generate/BinOp.java +++ b/src/main/java/com/jozufozu/flywheel/backend/glsl/generate/BinOp.java @@ -1,9 +1,11 @@ package com.jozufozu.flywheel.backend.glsl.generate; public enum BinOp { - BITWISE_AND("&"), - RIGHT_SHIFT(">>"), DIVIDE("/"), + SUBTRACT("-"), + RIGHT_SHIFT(">>"), + BITWISE_AND("&"), + BITWISE_XOR("^"), // TODO: add more as we need them ; diff --git a/src/main/java/com/jozufozu/flywheel/backend/glsl/generate/GlslExpr.java b/src/main/java/com/jozufozu/flywheel/backend/glsl/generate/GlslExpr.java index 4182e9e7b..7c2239f4d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/glsl/generate/GlslExpr.java +++ b/src/main/java/com/jozufozu/flywheel/backend/glsl/generate/GlslExpr.java @@ -95,8 +95,12 @@ public interface GlslExpr { return f.apply(this); } - default GlslExpr and(int mask) { - return new Binary(this, uintHexLiteral(mask), BinOp.BITWISE_AND); + default GlslExpr div(float v) { + return new Binary(this, floatLiteral(v), BinOp.DIVIDE); + } + + default GlslExpr sub(int v) { + return new Binary(this, uintLiteral(v), BinOp.SUBTRACT); } default GlslExpr rsh(int by) { @@ -106,8 +110,12 @@ public interface GlslExpr { return new Binary(this, uintLiteral(by), BinOp.RIGHT_SHIFT); } - default GlslExpr div(float v) { - return new Binary(this, floatLiteral(v), BinOp.DIVIDE); + default GlslExpr and(int mask) { + return new Binary(this, uintHexLiteral(mask), BinOp.BITWISE_AND); + } + + default GlslExpr xor(int mask) { + return new Binary(this, uintHexLiteral(mask), BinOp.BITWISE_XOR); } default GlslExpr clamp(float from, float to) { diff --git a/src/main/java/com/jozufozu/flywheel/lib/instance/InstanceTypes.java b/src/main/java/com/jozufozu/flywheel/lib/instance/InstanceTypes.java index df6c92fd0..9b8ae298d 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/instance/InstanceTypes.java +++ b/src/main/java/com/jozufozu/flywheel/lib/instance/InstanceTypes.java @@ -24,8 +24,10 @@ public final class InstanceTypes { MemoryUtil.memPutByte(ptr + 1, instance.g); MemoryUtil.memPutByte(ptr + 2, instance.b); MemoryUtil.memPutByte(ptr + 3, instance.a); - MemoryUtil.memPutInt(ptr + 4, instance.overlay); - MemoryUtil.memPutInt(ptr + 8, (int) instance.blockLight | (int) instance.skyLight << 16); + MemoryUtil.memPutShort(ptr + 4, (short) (instance.overlay & 0xFFFF)); + MemoryUtil.memPutShort(ptr + 6, (short) (instance.overlay >> 16 & 0xFFFF)); + MemoryUtil.memPutShort(ptr + 8, (short) (Byte.toUnsignedInt(instance.blockLight) << 4)); + MemoryUtil.memPutShort(ptr + 10, (short) (Byte.toUnsignedInt(instance.skyLight) << 4)); MatrixMath.writeUnsafe(instance.model, ptr + 12); MatrixMath.writeUnsafe(instance.normal, ptr + 76); }) @@ -47,8 +49,10 @@ public final class InstanceTypes { MemoryUtil.memPutByte(ptr + 1, instance.g); MemoryUtil.memPutByte(ptr + 2, instance.b); MemoryUtil.memPutByte(ptr + 3, instance.a); - MemoryUtil.memPutInt(ptr + 4, instance.overlay); - MemoryUtil.memPutInt(ptr + 8, (int) instance.blockLight | (int) instance.skyLight << 16); + MemoryUtil.memPutShort(ptr + 4, (short) (instance.overlay & 0xFFFF)); + MemoryUtil.memPutShort(ptr + 6, (short) (instance.overlay >> 16 & 0xFFFF)); + MemoryUtil.memPutShort(ptr + 8, (short) (Byte.toUnsignedInt(instance.blockLight) << 4)); + MemoryUtil.memPutShort(ptr + 10, (short) (Byte.toUnsignedInt(instance.skyLight) << 4)); MemoryUtil.memPutFloat(ptr + 12, instance.posX); MemoryUtil.memPutFloat(ptr + 16, instance.posY); MemoryUtil.memPutFloat(ptr + 20, instance.posZ); diff --git a/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert b/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert index 5e79d8f9b..6d113f0a8 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert +++ b/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert @@ -5,5 +5,6 @@ void flw_instanceVertex(in FlwInstance i) { flw_vertexNormal = rotateByQuaternion(flw_vertexNormal, i.rotation); flw_vertexColor *= i.color; flw_vertexOverlay = i.overlay; - flw_vertexLight = i.light / 15.; + // Some drivers have a bug where uint over float division is invalid, so use an explicit cast. + flw_vertexLight = vec2(i.light) / 256.0; } diff --git a/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert b/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert index e56b1b640..918883e84 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert +++ b/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert @@ -3,5 +3,6 @@ void flw_instanceVertex(in FlwInstance i) { flw_vertexNormal = i.normal * flw_vertexNormal; flw_vertexColor *= i.color; flw_vertexOverlay = i.overlay; - flw_vertexLight = i.light / 15.; + // Some drivers have a bug where uint over float division is invalid, so use an explicit cast. + flw_vertexLight = vec2(i.light) / 256.0; } diff --git a/src/main/resources/assets/flywheel/flywheel/internal/common.frag b/src/main/resources/assets/flywheel/flywheel/internal/common.frag index e775ef2a8..98d510611 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/common.frag +++ b/src/main/resources/assets/flywheel/flywheel/internal/common.frag @@ -33,15 +33,12 @@ void _flw_main() { } if (flw_material.useOverlay) { - // Need to clamp the overlay texture coords to sane coordinates because integer vertex attributes explode on - // some drivers for some draw calls. This should only effect instances that don't write to overlay, but - // the internal vertex format is unfortunately subject to these issues. - vec4 overlayColor = texelFetch(flw_overlayTex, clamp(flw_fragOverlay, 0, 10), 0); + vec4 overlayColor = texelFetch(flw_overlayTex, flw_fragOverlay, 0); color.rgb = mix(overlayColor.rgb, color.rgb, overlayColor.a); } if (flw_material.useLight) { - vec4 lightColor = texture(flw_lightTex, (flw_fragLight * 15.0 + 0.5) / 16.0); + vec4 lightColor = texture(flw_lightTex, clamp(flw_fragLight, 0.5 / 16.0, 15.5 / 16.0)); color *= lightColor; } diff --git a/src/main/resources/assets/flywheel/flywheel/internal/vertex_input.vert b/src/main/resources/assets/flywheel/flywheel/internal/vertex_input.vert index e617d95bf..776f12513 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/vertex_input.vert +++ b/src/main/resources/assets/flywheel/flywheel/internal/vertex_input.vert @@ -9,7 +9,10 @@ void _flw_layoutVertex() { flw_vertexPos = vec4(_flw_a_pos, 1.0); flw_vertexColor = _flw_a_color; flw_vertexTexCoord = _flw_a_texCoord; - flw_vertexOverlay = _flw_a_overlay; + // Need to clamp the overlay texture coords to sane coordinates because integer vertex attributes explode on + // some drivers for some draw calls. This should only effect instances that don't write to overlay, but + // the internal vertex format is unfortunately subject to these issues. + flw_vertexOverlay = clamp(_flw_a_overlay, 0, 15); flw_vertexLight = _flw_a_light / 256.0; flw_vertexNormal = _flw_a_normal; }