diff --git a/src/main/java/com/jozufozu/flywheel/api/layout/ArrayElementType.java b/src/main/java/com/jozufozu/flywheel/api/layout/ArrayElementType.java new file mode 100644 index 000000000..fd8c2f06a --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/api/layout/ArrayElementType.java @@ -0,0 +1,12 @@ +package com.jozufozu.flywheel.api.layout; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Range; + +@ApiStatus.NonExtendable +public non-sealed interface ArrayElementType extends ElementType { + ElementType innerType(); + + @Range(from = 1, to = 256) + int length(); +} diff --git a/src/main/java/com/jozufozu/flywheel/api/layout/ElementType.java b/src/main/java/com/jozufozu/flywheel/api/layout/ElementType.java index 333bfddb6..852ef8505 100644 --- a/src/main/java/com/jozufozu/flywheel/api/layout/ElementType.java +++ b/src/main/java/com/jozufozu/flywheel/api/layout/ElementType.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.api.layout; -public sealed interface ElementType permits ScalarElementType, VectorElementType, MatrixElementType { +public sealed interface ElementType permits ScalarElementType, VectorElementType, MatrixElementType, ArrayElementType { int byteSize(); + + int byteAlignment(); } diff --git a/src/main/java/com/jozufozu/flywheel/api/layout/Layout.java b/src/main/java/com/jozufozu/flywheel/api/layout/Layout.java index 10d6b9a10..3551576a9 100644 --- a/src/main/java/com/jozufozu/flywheel/api/layout/Layout.java +++ b/src/main/java/com/jozufozu/flywheel/api/layout/Layout.java @@ -17,12 +17,18 @@ public interface Layout { int byteSize(); + int byteAlignment(); + @ApiStatus.NonExtendable interface Element { String name(); ElementType type(); - int offset(); + int byteOffset(); + + int paddedByteSize(); + + int paddingByteSize(); } } diff --git a/src/main/java/com/jozufozu/flywheel/api/layout/LayoutBuilder.java b/src/main/java/com/jozufozu/flywheel/api/layout/LayoutBuilder.java index c001316ff..77d866199 100644 --- a/src/main/java/com/jozufozu/flywheel/api/layout/LayoutBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/api/layout/LayoutBuilder.java @@ -15,6 +15,14 @@ public interface LayoutBuilder { LayoutBuilder matrix(String name, FloatRepr repr, @Range(from = 2, to = 4) int size); + LayoutBuilder scalarArray(String name, ValueRepr repr, @Range(from = 1, to = 256) int length); + + LayoutBuilder vectorArray(String name, ValueRepr repr, @Range(from = 2, to = 4) int size, @Range(from = 1, to = 256) int length); + + LayoutBuilder matrixArray(String name, FloatRepr repr, @Range(from = 2, to = 4) int rows, @Range(from = 2, to = 4) int columns, @Range(from = 1, to = 256) int length); + + LayoutBuilder matrixArray(String name, FloatRepr repr, @Range(from = 2, to = 4) int size, @Range(from = 1, to = 256) int length); + Layout build(); static LayoutBuilder create() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/InternalVertex.java b/src/main/java/com/jozufozu/flywheel/backend/InternalVertex.java index 74bd5cef0..8b6aa2cb3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/InternalVertex.java +++ b/src/main/java/com/jozufozu/flywheel/backend/InternalVertex.java @@ -7,7 +7,6 @@ import com.jozufozu.flywheel.api.layout.FloatRepr; import com.jozufozu.flywheel.api.layout.Layout; import com.jozufozu.flywheel.api.layout.LayoutBuilder; import com.jozufozu.flywheel.api.vertex.VertexView; -import com.jozufozu.flywheel.backend.engine.LayoutAttributes; import com.jozufozu.flywheel.backend.gl.array.VertexAttribute; import com.jozufozu.flywheel.lib.vertex.FullVertexView; @@ -24,7 +23,6 @@ public final class InternalVertex { .build(); public static final List ATTRIBUTES = LayoutAttributes.attributes(LAYOUT); - public static final int ATTRIBUTE_COUNT = ATTRIBUTES.size(); public static final int STRIDE = LAYOUT.byteSize(); public static final ResourceLocation LAYOUT_SHADER = Flywheel.rl("internal/vertex_input.vert"); diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/LayoutAttributes.java b/src/main/java/com/jozufozu/flywheel/backend/LayoutAttributes.java similarity index 72% rename from src/main/java/com/jozufozu/flywheel/backend/engine/LayoutAttributes.java rename to src/main/java/com/jozufozu/flywheel/backend/LayoutAttributes.java index 052dbbc35..5a66ddf54 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/LayoutAttributes.java +++ b/src/main/java/com/jozufozu/flywheel/backend/LayoutAttributes.java @@ -1,8 +1,10 @@ -package com.jozufozu.flywheel.backend.engine; +package com.jozufozu.flywheel.backend; import java.util.ArrayList; import java.util.List; +import com.jozufozu.flywheel.api.layout.ArrayElementType; +import com.jozufozu.flywheel.api.layout.ElementType; import com.jozufozu.flywheel.api.layout.FloatRepr; import com.jozufozu.flywheel.api.layout.IntegerRepr; import com.jozufozu.flywheel.api.layout.Layout; @@ -25,28 +27,23 @@ public class LayoutAttributes { List out = new ArrayList<>(); for (Layout.Element element : layout.elements()) { - var type = element.type(); - - if (type instanceof ScalarElementType scalar) { - vector(out, scalar.repr(), 1); - } else if (type instanceof VectorElementType vector) { - vector(out, vector.repr(), vector.size()); - } else if (type instanceof MatrixElementType matrix) { - matrix(out, matrix); - } + element(out, element.type()); } return out; } - private static void matrix(List out, MatrixElementType matrix) { - int size = matrix.columns(); - var repr = matrix.repr(); - var glType = toGlType(repr); - boolean normalized = normalized(repr); - - for (int i = 0; i < matrix.rows(); i++) { - out.add(new VertexAttribute.Float(glType, size, normalized)); + private static void element(List out, ElementType type) { + if (type instanceof ScalarElementType scalar) { + vector(out, scalar.repr(), 1); + } else if (type instanceof VectorElementType vector) { + vector(out, vector.repr(), vector.size()); + } else if (type instanceof MatrixElementType matrix) { + matrix(out, matrix); + } else if (type instanceof ArrayElementType array) { + array(out, array); + } else { + throw new IllegalArgumentException("Unknown type " + type); } } @@ -56,7 +53,29 @@ public class LayoutAttributes { } else if (repr instanceof UnsignedIntegerRepr integer) { out.add(new VertexAttribute.Int(toGlType(integer), size)); } else if (repr instanceof FloatRepr floatRepr) { - out.add(new VertexAttribute.Float(toGlType(floatRepr), size, normalized(floatRepr))); + out.add(new VertexAttribute.Float(toGlType(floatRepr), size, isNormalized(floatRepr))); + } else { + throw new IllegalArgumentException("Unknown repr " + repr); + } + } + + private static void matrix(List out, MatrixElementType matrix) { + int size = matrix.columns(); + var repr = matrix.repr(); + var glType = toGlType(repr); + boolean normalized = isNormalized(repr); + + for (int i = 0; i < matrix.rows(); i++) { + out.add(new VertexAttribute.Float(glType, size, normalized)); + } + } + + private static void array(List out, ArrayElementType array) { + ElementType innerType = array.innerType(); + int length = array.length(); + + for (int i = 0; i < length; i++) { + element(out, innerType); } } @@ -88,7 +107,7 @@ public class LayoutAttributes { }; } - private static boolean normalized(FloatRepr repr) { + private static boolean isNormalized(FloatRepr repr) { return switch (repr) { case NORMALIZED_BYTE, NORMALIZED_UNSIGNED_BYTE, NORMALIZED_SHORT, NORMALIZED_UNSIGNED_SHORT, NORMALIZED_INT, NORMALIZED_UNSIGNED_INT -> true; 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 index 6afcc5eec..b2218cbb9 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/BufferTextureInstanceComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/BufferTextureInstanceComponent.java @@ -28,7 +28,7 @@ public class BufferTextureInstanceComponent extends InstanceAssemblerComponent { protected void generateUnpacking(GlslBuilder builder) { var fnBody = new GlslBlock(); - var texels = MoreMath.ceilingDiv(layout.byteSize(), 16); + int texels = MoreMath.ceilingDiv(layout.byteSize(), 16); fnBody.add(GlslStmt.raw("int base = " + UNPACK_ARG + " * " + texels + ";")); @@ -38,11 +38,8 @@ public class BufferTextureInstanceComponent extends InstanceAssemblerComponent { } 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; + unpackArgs.add(unpackElement(element)); } fnBody.ret(GlslExpr.call(STRUCT_NAME, unpackArgs)); 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 index 478db66e2..348146314 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceAssemblerComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceAssemblerComponent.java @@ -9,6 +9,8 @@ import java.util.List; import java.util.function.Function; import com.jozufozu.flywheel.api.instance.InstanceType; +import com.jozufozu.flywheel.api.layout.ArrayElementType; +import com.jozufozu.flywheel.api.layout.ElementType; import com.jozufozu.flywheel.api.layout.FloatRepr; import com.jozufozu.flywheel.api.layout.IntegerRepr; import com.jozufozu.flywheel.api.layout.Layout; @@ -20,6 +22,7 @@ import com.jozufozu.flywheel.api.layout.VectorElementType; import com.jozufozu.flywheel.backend.glsl.SourceComponent; import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder; import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr; +import com.jozufozu.flywheel.impl.layout.LayoutInterpreter; public abstract class InstanceAssemblerComponent implements SourceComponent { protected static final String STRUCT_NAME = "FlwInstance"; @@ -95,164 +98,146 @@ public abstract class InstanceAssemblerComponent implements SourceComponent { protected abstract GlslExpr access(int uintOffset); - protected GlslExpr unpackElement(Layout.Element element, int uintOffset) { - var type = element.type(); + protected GlslExpr unpackElement(Layout.Element element) { + return unpackElement(element.type(), element.byteOffset()); + } + private GlslExpr unpackElement(ElementType type, int byteOffset) { if (type instanceof ScalarElementType scalar) { - return unpackScalar(scalar, uintOffset); + return unpackScalar(scalar, byteOffset); } else if (type instanceof VectorElementType vector) { - return unpackVector(vector, uintOffset); + return unpackVector(vector, byteOffset); } else if (type instanceof MatrixElementType matrix) { - return unpackMatrix(matrix, uintOffset); + return unpackMatrix(matrix, byteOffset); + } else if (type instanceof ArrayElementType array) { + return unpackArray(array, byteOffset); } throw new IllegalArgumentException("Unknown type " + type); } - private GlslExpr unpackScalar(ScalarElementType type, int uintOffset) { + private GlslExpr unpackScalar(ScalarElementType type, int byteOffset) { 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); - } + Function unpackingFunc = getUnpackingFunc(repr); + return unpackScalar(byteOffset, repr.byteSize(), unpackingFunc); } - private GlslExpr unpackVector(VectorElementType type, int uintOffset) { + private GlslExpr unpackVector(VectorElementType type, int byteOffset) { 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); - } + Function unpackingFunc = getUnpackingFunc(repr); + String outType = LayoutInterpreter.vectorTypeName(type); + return unpackVector(outType, size, byteOffset, repr.byteSize(), unpackingFunc); } - private GlslExpr unpackMatrix(MatrixElementType type, int uintOffset) { + private GlslExpr unpackMatrix(MatrixElementType type, int byteOffset) { 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; + String outType = LayoutInterpreter.matrixTypeName(type); int size = rows * columns; + return unpackVector(outType, size, byteOffset, repr.byteSize(), unpackingFunc); + } - if (isByteBacked(repr)) { - return unpackByteBackedVector(outType, size, uintOffset, unpackingFunc); - } else if (isShortBacked(repr)) { - return unpackShortBackedVector(outType, size, uintOffset, unpackingFunc); + private GlslExpr unpackArray(ArrayElementType type, int byteOffset) { + ElementType innerType = type.innerType(); + int innerByteSize = innerType.byteSize(); + int length = type.length(); + String outType = LayoutInterpreter.arrayTypeName(type); + + List args = new ArrayList<>(); + for (int i = 0; i < length; i++) { + args.add(unpackElement(innerType, byteOffset + i * innerByteSize)); + } + return GlslExpr.call(outType, args); + } + + private GlslExpr unpackScalar(int byteOffset, int byteSize, Function unpackingFunc) { + int offset = byteOffset / byteSize; + + if (byteSize == Byte.BYTES) { + return unpackByteBackedScalar(offset, unpackingFunc); + } else if (byteSize == Short.BYTES) { + return unpackShortBackedScalar(offset, unpackingFunc); } else { - return unpackIntBackedVector(outType, size, uintOffset, unpackingFunc); + return unpackIntBackedScalar(offset, unpackingFunc); } } - private boolean isByteBacked(ValueRepr repr) { - return repr.byteSize() == Byte.BYTES; - } - - private boolean isShortBacked(ValueRepr repr) { - return repr.byteSize() == Short.BYTES; - } - - private GlslExpr unpackByteBackedScalar(int uintOffset, Function perElement) { - GlslExpr e; + private GlslExpr unpackByteBackedScalar(int byteOffset, Function unpackingFunc) { + int bitPos = (byteOffset % 4) * 8; if (BIG_ENDIAN) { - e = access(uintOffset) - .rsh(24) - .and(0xFF); - } else { - e = access(uintOffset) - .and(0xFF); + bitPos = 24 - bitPos; } - return perElement.apply(e); + int wordOffset = byteOffset / 4; + GlslExpr prepared = access(wordOffset) + .rsh(bitPos) + .and(0xFF); + return unpackingFunc.apply(prepared); } - private GlslExpr unpackShortBackedScalar(int uintOffset, Function perElement) { - GlslExpr e; + private GlslExpr unpackShortBackedScalar(int shortOffset, Function unpackingFunc) { + int bitPos = (shortOffset % 2) * 16; if (BIG_ENDIAN) { - e = access(uintOffset) - .rsh(16) - .and(0xFFFF); + bitPos = 16 - bitPos; + } + int wordOffset = shortOffset / 2; + GlslExpr prepared = access(wordOffset) + .rsh(bitPos) + .and(0xFFFF); + return unpackingFunc.apply(prepared); + } + + private GlslExpr unpackIntBackedScalar(int intOffset, Function unpackingFunc) { + return unpackingFunc.apply(access(intOffset)); + } + + private GlslExpr unpackVector(String outType, int size, int byteOffset, int byteSize, Function unpackingFunc) { + int offset = byteOffset / byteSize; + + if (byteSize == Byte.BYTES) { + return unpackByteBackedVector(outType, size, offset, unpackingFunc); + } else if (byteSize == Short.BYTES) { + return unpackShortBackedVector(outType, size, offset, unpackingFunc); } else { - e = access(uintOffset) - .and(0xFFFF); + return unpackIntBackedVector(outType, size, offset, unpackingFunc); } - return perElement.apply(e); } - private GlslExpr unpackIntBackedScalar(int uintOffset, Function perElement) { - return perElement.apply(access(uintOffset)); - } - - private GlslExpr unpackByteBackedVector(String outType, int size, int uintOffset, Function perElement) { + private GlslExpr unpackByteBackedVector(String outType, int size, int byteOffset, Function unpackingFunc) { 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)); + args.add(unpackByteBackedScalar(byteOffset + i, unpackingFunc)); } return GlslExpr.call(outType, args); } - private GlslExpr unpackShortBackedVector(String outType, int size, int uintOffset, Function perElement) { + private GlslExpr unpackShortBackedVector(String outType, int size, int shortOffset, Function unpackingFunc) { 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)); + args.add(unpackShortBackedScalar(shortOffset + i, unpackingFunc)); } return GlslExpr.call(outType, args); } - private GlslExpr unpackIntBackedVector(String outType, int size, int uintOffset, Function perElement) { + private GlslExpr unpackIntBackedVector(String outType, int size, int intOffset, Function unpackingFunc) { List args = new ArrayList<>(); for (int i = 0; i < size; i++) { - args.add(perElement.apply(access(uintOffset + i))); + args.add(unpackIntBackedScalar(intOffset + i, unpackingFunc)); } return GlslExpr.call(outType, args); } + + private static Function getUnpackingFunc(ValueRepr repr) { + if (repr instanceof IntegerRepr intRepr) { + return INT_UNPACKING_FUNCS.get(intRepr); + } else if (repr instanceof UnsignedIntegerRepr uintRepr) { + return UINT_UNPACKING_FUNCS.get(uintRepr); + } else if (repr instanceof FloatRepr floatRepr) { + return FLOAT_UNPACKING_FUNCS.get(floatRepr); + } + + throw new IllegalArgumentException("Unknown repr " + repr); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceStructComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceStructComponent.java index 4c3992b16..49986e50f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceStructComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceStructComponent.java @@ -5,17 +5,10 @@ import java.util.Collections; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.instance.InstanceType; -import com.jozufozu.flywheel.api.layout.ElementType; -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.glsl.SourceComponent; import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder; +import com.jozufozu.flywheel.impl.layout.LayoutInterpreter; public class InstanceStructComponent implements SourceComponent { private static final String STRUCT_NAME = "FlwInstance"; @@ -43,48 +36,10 @@ public class InstanceStructComponent implements SourceComponent { var instance = builder.struct(); instance.setName(STRUCT_NAME); for (var element : layout.elements()) { - instance.addField(typeName(element.type()), element.name()); + instance.addField(LayoutInterpreter.typeName(element.type()), element.name()); } builder.blankLine(); return builder.build(); } - - private static String typeName(ElementType type) { - if (type instanceof ScalarElementType scalar) { - return scalarTypeName(scalar.repr()); - } else if (type instanceof VectorElementType vector) { - return vectorTypeName(vector.repr(), vector.size()); - } else if (type instanceof MatrixElementType matrix) { - return matrixTypeName(matrix); - } - - throw new IllegalArgumentException("Unknown type " + type); - } - - private static String scalarTypeName(ValueRepr repr) { - if (repr instanceof IntegerRepr) { - return "int"; - } else if (repr instanceof UnsignedIntegerRepr) { - return "uint"; - } else if (repr instanceof FloatRepr) { - return "float"; - } - throw new IllegalArgumentException("Unknown repr " + repr); - } - - private static String vectorTypeName(ValueRepr repr, int size) { - if (repr instanceof IntegerRepr) { - return "ivec" + size; - } else if (repr instanceof UnsignedIntegerRepr) { - return "uvec" + size; - } else if (repr instanceof FloatRepr) { - return "vec" + size; - } - throw new IllegalArgumentException("Unknown repr " + repr); - } - - private static String matrixTypeName(MatrixElementType matrix) { - return "mat" + matrix.columns() + "x" + matrix.rows(); - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/SsboInstanceComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/SsboInstanceComponent.java index 8c287c302..a09e87ea7 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/SsboInstanceComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/SsboInstanceComponent.java @@ -11,6 +11,7 @@ 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 SsboInstanceComponent extends InstanceAssemblerComponent { public SsboInstanceComponent(InstanceType type) { @@ -26,7 +27,7 @@ public class SsboInstanceComponent extends InstanceAssemblerComponent { protected void generateUnpacking(GlslBuilder builder) { var fnBody = new GlslBlock(); - var uintCount = layout.byteSize() / 4; + int uintCount = MoreMath.ceilingDiv(layout.byteSize(), 4); fnBody.add(GlslStmt.raw("uint base = " + UNPACK_ARG + " * " + uintCount + "u;")); @@ -36,11 +37,8 @@ public class SsboInstanceComponent extends InstanceAssemblerComponent { } 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; + unpackArgs.add(unpackElement(element)); } fnBody.ret(GlslExpr.call(STRUCT_NAME, unpackArgs)); diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java index 2f8969941..90a2e2f47 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -30,6 +30,7 @@ import com.jozufozu.flywheel.backend.engine.uniform.Uniforms; import com.jozufozu.flywheel.backend.gl.Driver; import com.jozufozu.flywheel.backend.gl.GlCompat; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.lib.math.MoreMath; public class IndirectCullingGroup { private static final Comparator DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::stage) @@ -58,8 +59,8 @@ public class IndirectCullingGroup { IndirectCullingGroup(InstanceType instanceType, Environment environment, IndirectPrograms programs) { this.instanceType = instanceType; this.environment = environment; - instanceStride = instanceType.layout() - .byteSize(); + instanceStride = MoreMath.align4(instanceType.layout() + .byteSize()); buffers = new IndirectBuffers(instanceStride); this.programs = programs; diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java index 07109caa3..fcb3efd0c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java @@ -12,6 +12,7 @@ import com.jozufozu.flywheel.api.instance.InstanceWriter; import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.backend.engine.AbstractInstancer; import com.jozufozu.flywheel.backend.engine.embed.Environment; +import com.jozufozu.flywheel.lib.math.MoreMath; public class IndirectInstancer extends AbstractInstancer { private final long instanceStride; @@ -27,8 +28,8 @@ public class IndirectInstancer extends AbstractInstancer public IndirectInstancer(InstanceType type, Environment environment, Model model) { super(type, environment); - instanceStride = type.layout() - .byteSize(); + instanceStride = MoreMath.align4(type.layout() + .byteSize()); writer = this.type.writer(); boundingSphere = model.boundingSphere(); } diff --git a/src/main/java/com/jozufozu/flywheel/impl/layout/ArrayElementTypeImpl.java b/src/main/java/com/jozufozu/flywheel/impl/layout/ArrayElementTypeImpl.java new file mode 100644 index 000000000..5de8c9eee --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/impl/layout/ArrayElementTypeImpl.java @@ -0,0 +1,16 @@ +package com.jozufozu.flywheel.impl.layout; + +import org.jetbrains.annotations.Range; + +import com.jozufozu.flywheel.api.layout.ArrayElementType; +import com.jozufozu.flywheel.api.layout.ElementType; + +record ArrayElementTypeImpl(ElementType innerType, @Range(from = 1, to = 256) int length, int byteSize, int byteAlignment) implements ArrayElementType { + static ArrayElementTypeImpl create(ElementType innerType, @Range(from = 1, to = 256) int length) { + if (length < 1 || length > 256) { + throw new IllegalArgumentException("Array element length must be in range [1, 256]!"); + } + + return new ArrayElementTypeImpl(innerType, length, innerType.byteSize() * length, innerType.byteAlignment()); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/impl/layout/LayoutBuilderImpl.java b/src/main/java/com/jozufozu/flywheel/impl/layout/LayoutBuilderImpl.java index 3f9a5049b..73cdedfe1 100644 --- a/src/main/java/com/jozufozu/flywheel/impl/layout/LayoutBuilderImpl.java +++ b/src/main/java/com/jozufozu/flywheel/impl/layout/LayoutBuilderImpl.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Locale; import java.util.Set; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Range; import com.jozufozu.flywheel.api.layout.ElementType; @@ -14,6 +15,7 @@ import com.jozufozu.flywheel.api.layout.Layout.Element; import com.jozufozu.flywheel.api.layout.LayoutBuilder; import com.jozufozu.flywheel.api.layout.ValueRepr; import com.jozufozu.flywheel.impl.layout.LayoutImpl.ElementImpl; +import com.jozufozu.flywheel.lib.math.MoreMath; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -102,7 +104,9 @@ public class LayoutBuilderImpl implements LayoutBuilder { ); private final List elements = new ArrayList<>(); - private int offset = 0; + @Nullable + private UnpaddedElement lastElement; + private int maxByteAlignment; @Override public LayoutBuilder scalar(String name, ValueRepr repr) { @@ -124,15 +128,57 @@ public class LayoutBuilderImpl implements LayoutBuilder { return matrix(name, repr, size, size); } + @Override + public LayoutBuilder scalarArray(String name, ValueRepr repr, @Range(from = 1, to = 256) int length) { + return element(name, ArrayElementTypeImpl.create(ScalarElementTypeImpl.create(repr), length)); + } + + @Override + public LayoutBuilder vectorArray(String name, ValueRepr repr, @Range(from = 2, to = 4) int size, @Range(from = 1, to = 256) int length) { + return element(name, ArrayElementTypeImpl.create(VectorElementTypeImpl.create(repr, size), length)); + } + + @Override + public LayoutBuilder matrixArray(String name, FloatRepr repr, @Range(from = 2, to = 4) int rows, @Range(from = 2, to = 4) int columns, @Range(from = 1, to = 256) int length) { + return element(name, ArrayElementTypeImpl.create(MatrixElementTypeImpl.create(repr, rows, columns), length)); + } + + @Override + public LayoutBuilder matrixArray(String name, FloatRepr repr, @Range(from = 2, to = 4) int size, @Range(from = 1, to = 256) int length) { + return matrixArray(name, repr, size, size, length); + } + private LayoutBuilder element(String name, ElementType type) { - elements.add(new ElementImpl(name, type, offset)); - // type.byteSize() is guaranteed to be 4-aligned. - offset += type.byteSize(); + UnpaddedElement newElement; + + if (lastElement != null) { + int byteOffset = padLastElement(type.byteAlignment()); + newElement = new UnpaddedElement(name, type, byteOffset); + } else { + newElement = new UnpaddedElement(name, type, 0); + } + + lastElement = newElement; + maxByteAlignment = Math.max(lastElement.type.byteAlignment(), maxByteAlignment); return this; } + private int padLastElement(int byteAlignment) { + int nextByte = lastElement.byteOffset + lastElement.type.byteSize(); + int byteOffset = MoreMath.alignPot(nextByte, byteAlignment); + elements.add(lastElement.complete(byteOffset - nextByte)); + return byteOffset; + } + @Override public Layout build() { + int byteSize; + if (lastElement != null) { + byteSize = padLastElement(maxByteAlignment); + } else { + byteSize = 0; + } + Object2IntMap name2IndexMap = new Object2IntOpenHashMap<>(); name2IndexMap.defaultReturnValue(-1); @@ -172,7 +218,17 @@ public class LayoutBuilderImpl implements LayoutBuilder { } } - return new LayoutImpl(List.copyOf(elements), offset); + List elements = List.copyOf(this.elements); + int maxByteAlignment = this.maxByteAlignment; + clear(); + + return new LayoutImpl(elements, byteSize, maxByteAlignment); + } + + private void clear() { + elements.clear(); + lastElement = null; + maxByteAlignment = 0; } private static boolean isValidNameCharacter(char c) { @@ -182,4 +238,10 @@ public class LayoutBuilderImpl implements LayoutBuilder { private static boolean isLetter(char c) { return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'; } + + private static record UnpaddedElement(String name, ElementType type, int byteOffset) { + private ElementImpl complete(int paddingByteSize) { + return new ElementImpl(name, type, byteOffset, type.byteSize() + paddingByteSize, paddingByteSize); + } + } } diff --git a/src/main/java/com/jozufozu/flywheel/impl/layout/LayoutImpl.java b/src/main/java/com/jozufozu/flywheel/impl/layout/LayoutImpl.java index d6f4f9b34..788d2bdd9 100644 --- a/src/main/java/com/jozufozu/flywheel/impl/layout/LayoutImpl.java +++ b/src/main/java/com/jozufozu/flywheel/impl/layout/LayoutImpl.java @@ -17,8 +17,9 @@ final class LayoutImpl implements Layout { @Unmodifiable private final Map map; private final int byteSize; + private final int byteAlignment; - LayoutImpl(@Unmodifiable List elements, int byteSize) { + LayoutImpl(@Unmodifiable List elements, int byteSize, int byteAlignment) { this.elements = elements; Object2ObjectOpenHashMap map = new Object2ObjectOpenHashMap<>(); @@ -29,6 +30,7 @@ final class LayoutImpl implements Layout { this.map = Collections.unmodifiableMap(map); this.byteSize = byteSize; + this.byteAlignment = byteAlignment; } @Override @@ -48,6 +50,11 @@ final class LayoutImpl implements Layout { return byteSize; } + @Override + public int byteAlignment() { + return byteAlignment; + } + @Override public int hashCode() { final int prime = 31; @@ -71,6 +78,6 @@ final class LayoutImpl implements Layout { return elements.equals(other.elements); } - record ElementImpl(String name, ElementType type, int offset) implements Element { + record ElementImpl(String name, ElementType type, int byteOffset, int paddedByteSize, int paddingByteSize) implements Element { } } diff --git a/src/main/java/com/jozufozu/flywheel/impl/layout/LayoutInterpreter.java b/src/main/java/com/jozufozu/flywheel/impl/layout/LayoutInterpreter.java new file mode 100644 index 000000000..3df4af593 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/impl/layout/LayoutInterpreter.java @@ -0,0 +1,66 @@ +package com.jozufozu.flywheel.impl.layout; + +import com.jozufozu.flywheel.api.layout.ArrayElementType; +import com.jozufozu.flywheel.api.layout.ElementType; +import com.jozufozu.flywheel.api.layout.FloatRepr; +import com.jozufozu.flywheel.api.layout.IntegerRepr; +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; + +public class LayoutInterpreter { + public static String typeName(ElementType type) { + if (type instanceof ScalarElementType scalar) { + return scalarTypeName(scalar); + } else if (type instanceof VectorElementType vector) { + return vectorTypeName(vector); + } else if (type instanceof MatrixElementType matrix) { + return matrixTypeName(matrix); + } else if (type instanceof ArrayElementType array) { + return arrayTypeName(array); + } + + throw new IllegalArgumentException("Unknown type " + type); + } + + public static String scalarTypeName(ScalarElementType scalar) { + ValueRepr repr = scalar.repr(); + + if (repr instanceof IntegerRepr) { + return "int"; + } else if (repr instanceof UnsignedIntegerRepr) { + return "uint"; + } else if (repr instanceof FloatRepr) { + return "float"; + } + + throw new IllegalArgumentException("Unknown repr " + repr); + } + + public static String vectorTypeName(VectorElementType vector) { + ValueRepr repr = vector.repr(); + int size = vector.size(); + + if (repr instanceof IntegerRepr) { + return "ivec" + size; + } else if (repr instanceof UnsignedIntegerRepr) { + return "uvec" + size; + } else if (repr instanceof FloatRepr) { + return "vec" + size; + } + + throw new IllegalArgumentException("Unknown repr " + repr); + } + + public static String matrixTypeName(MatrixElementType matrix) { + return "mat" + matrix.columns() + "x" + matrix.rows(); + } + + // This will not produce correct types for multidimensional arrays (the lengths will be in the opposite order), + // but the API does not allow creating multidimensional arrays anyway because they are not core until GLSL 430. + public static String arrayTypeName(ArrayElementType array) { + return typeName(array.innerType()) + "[" + array.length() + "]"; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/impl/layout/MatrixElementTypeImpl.java b/src/main/java/com/jozufozu/flywheel/impl/layout/MatrixElementTypeImpl.java index e293dbf77..20e78d625 100644 --- a/src/main/java/com/jozufozu/flywheel/impl/layout/MatrixElementTypeImpl.java +++ b/src/main/java/com/jozufozu/flywheel/impl/layout/MatrixElementTypeImpl.java @@ -4,10 +4,9 @@ import org.jetbrains.annotations.Range; import com.jozufozu.flywheel.api.layout.FloatRepr; import com.jozufozu.flywheel.api.layout.MatrixElementType; -import com.jozufozu.flywheel.lib.math.MoreMath; record MatrixElementTypeImpl(FloatRepr repr, @Range(from = 2, to = 4) int rows, @Range(from = 2, to = 4) int columns, - int byteSize) implements MatrixElementType { + int byteSize, int byteAlignment) implements MatrixElementType { static MatrixElementTypeImpl create(FloatRepr repr, @Range(from = 2, to = 4) int rows, @Range(from = 2, to = 4) int columns) { if (rows < 2 || rows > 4) { throw new IllegalArgumentException("Matrix element row count must be in range [2, 4]!"); @@ -15,7 +14,7 @@ record MatrixElementTypeImpl(FloatRepr repr, @Range(from = 2, to = 4) int rows, if (columns < 2 || columns > 4) { throw new IllegalArgumentException("Matrix element column count must be in range [2, 4]!"); } - int byteSize = MoreMath.align4(repr.byteSize() * rows * columns); - return new MatrixElementTypeImpl(repr, rows, columns, byteSize); + + return new MatrixElementTypeImpl(repr, rows, columns, repr.byteSize() * rows * columns, repr.byteSize()); } } diff --git a/src/main/java/com/jozufozu/flywheel/impl/layout/ScalarElementTypeImpl.java b/src/main/java/com/jozufozu/flywheel/impl/layout/ScalarElementTypeImpl.java index c2e901724..258542e61 100644 --- a/src/main/java/com/jozufozu/flywheel/impl/layout/ScalarElementTypeImpl.java +++ b/src/main/java/com/jozufozu/flywheel/impl/layout/ScalarElementTypeImpl.java @@ -2,10 +2,9 @@ package com.jozufozu.flywheel.impl.layout; import com.jozufozu.flywheel.api.layout.ScalarElementType; import com.jozufozu.flywheel.api.layout.ValueRepr; -import com.jozufozu.flywheel.lib.math.MoreMath; -record ScalarElementTypeImpl(ValueRepr repr, int byteSize) implements ScalarElementType { +record ScalarElementTypeImpl(ValueRepr repr, int byteSize, int byteAlignment) implements ScalarElementType { static ScalarElementTypeImpl create(ValueRepr repr) { - return new ScalarElementTypeImpl(repr, MoreMath.align4(repr.byteSize())); + return new ScalarElementTypeImpl(repr, repr.byteSize(), repr.byteSize()); } } diff --git a/src/main/java/com/jozufozu/flywheel/impl/layout/VectorElementTypeImpl.java b/src/main/java/com/jozufozu/flywheel/impl/layout/VectorElementTypeImpl.java index 70b1c67ca..6088bb5ec 100644 --- a/src/main/java/com/jozufozu/flywheel/impl/layout/VectorElementTypeImpl.java +++ b/src/main/java/com/jozufozu/flywheel/impl/layout/VectorElementTypeImpl.java @@ -4,17 +4,14 @@ import org.jetbrains.annotations.Range; import com.jozufozu.flywheel.api.layout.ValueRepr; import com.jozufozu.flywheel.api.layout.VectorElementType; -import com.jozufozu.flywheel.lib.math.MoreMath; record VectorElementTypeImpl(ValueRepr repr, @Range(from = 2, to = 4) int size, - int byteSize) implements VectorElementType { - + int byteSize, int byteAlignment) implements VectorElementType { static VectorElementTypeImpl create(ValueRepr repr, @Range(from = 2, to = 4) int size) { if (size < 2 || size > 4) { throw new IllegalArgumentException("Vector element size must be in range [2, 4]!"); } - int byteSize = MoreMath.align4(repr.byteSize() * size); - return new VectorElementTypeImpl(repr, size, byteSize); + return new VectorElementTypeImpl(repr, size, repr.byteSize() * size, repr.byteSize()); } } diff --git a/src/main/java/com/jozufozu/flywheel/lib/math/MoreMath.java b/src/main/java/com/jozufozu/flywheel/lib/math/MoreMath.java index 220c1f2ea..f04db7593 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/math/MoreMath.java +++ b/src/main/java/com/jozufozu/flywheel/lib/math/MoreMath.java @@ -17,6 +17,10 @@ public final class MoreMath { return (size + 3) & ~3; } + public static int alignPot(int size, int to) { + return (size + (to - 1)) & ~(to - 1); + } + public static int ceilingDiv(int numerator, int denominator) { return (numerator + denominator - 1) / denominator; } diff --git a/src/main/java/com/jozufozu/flywheel/lib/visual/components/FireComponent.java b/src/main/java/com/jozufozu/flywheel/lib/visual/component/FireComponent.java similarity index 99% rename from src/main/java/com/jozufozu/flywheel/lib/visual/components/FireComponent.java rename to src/main/java/com/jozufozu/flywheel/lib/visual/component/FireComponent.java index 70af3ae2e..9a11224dc 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/visual/components/FireComponent.java +++ b/src/main/java/com/jozufozu/flywheel/lib/visual/component/FireComponent.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.lib.visual.components; +package com.jozufozu.flywheel.lib.visual.component; import org.joml.Vector4f; import org.joml.Vector4fc; diff --git a/src/main/java/com/jozufozu/flywheel/lib/visual/components/HitboxComponent.java b/src/main/java/com/jozufozu/flywheel/lib/visual/component/HitboxComponent.java similarity index 98% rename from src/main/java/com/jozufozu/flywheel/lib/visual/components/HitboxComponent.java rename to src/main/java/com/jozufozu/flywheel/lib/visual/component/HitboxComponent.java index bdcdb23c4..2cbba3709 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/visual/components/HitboxComponent.java +++ b/src/main/java/com/jozufozu/flywheel/lib/visual/component/HitboxComponent.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.lib.visual.components; +package com.jozufozu.flywheel.lib.visual.component; import org.joml.Quaternionf; diff --git a/src/main/java/com/jozufozu/flywheel/lib/visual/components/ShadowComponent.java b/src/main/java/com/jozufozu/flywheel/lib/visual/component/ShadowComponent.java similarity index 99% rename from src/main/java/com/jozufozu/flywheel/lib/visual/components/ShadowComponent.java rename to src/main/java/com/jozufozu/flywheel/lib/visual/component/ShadowComponent.java index 8c735bc7c..305cd1b34 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/visual/components/ShadowComponent.java +++ b/src/main/java/com/jozufozu/flywheel/lib/visual/component/ShadowComponent.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.lib.visual.components; +package com.jozufozu.flywheel.lib.visual.component; import org.jetbrains.annotations.Nullable; import org.joml.Vector4f; diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/MinecartVisual.java b/src/main/java/com/jozufozu/flywheel/vanilla/MinecartVisual.java index a31f82da0..c231457a7 100644 --- a/src/main/java/com/jozufozu/flywheel/vanilla/MinecartVisual.java +++ b/src/main/java/com/jozufozu/flywheel/vanilla/MinecartVisual.java @@ -14,9 +14,9 @@ import com.jozufozu.flywheel.lib.model.part.ModelPartConverter; import com.jozufozu.flywheel.lib.visual.SimpleDynamicVisual; import com.jozufozu.flywheel.lib.visual.SimpleEntityVisual; import com.jozufozu.flywheel.lib.visual.SimpleTickableVisual; -import com.jozufozu.flywheel.lib.visual.components.FireComponent; -import com.jozufozu.flywheel.lib.visual.components.HitboxComponent; -import com.jozufozu.flywheel.lib.visual.components.ShadowComponent; +import com.jozufozu.flywheel.lib.visual.component.FireComponent; +import com.jozufozu.flywheel.lib.visual.component.HitboxComponent; +import com.jozufozu.flywheel.lib.visual.component.ShadowComponent; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Axis;