Arrays and alignment

- Allow array elements in layouts with lengths in range [1, 256]
- Align each layout element to its primitive size instead of always
aligning to 4 bytes; this is the same as C struct alignment
- Rename package lib.visual.components -> component
- Add more methods to layout API interfaces to allow retrieving
information such as padding size and alignment
This commit is contained in:
PepperCode1 2024-04-05 17:00:04 -07:00
parent 09cb15e2ed
commit 2f7fce29c3
24 changed files with 350 additions and 218 deletions

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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();
}
}

View file

@ -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() {

View file

@ -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<VertexAttribute> 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");

View file

@ -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<VertexAttribute> 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<VertexAttribute> 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<VertexAttribute> 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<VertexAttribute> 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<VertexAttribute> 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;

View file

@ -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<GlslExpr>();
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));

View file

@ -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<GlslExpr, GlslExpr> 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<GlslExpr, GlslExpr> 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<GlslExpr, GlslExpr> 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<GlslExpr, GlslExpr> 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<GlslExpr, GlslExpr> 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<GlslExpr> 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<GlslExpr, GlslExpr> 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<GlslExpr, GlslExpr> perElement) {
GlslExpr e;
private GlslExpr unpackByteBackedScalar(int byteOffset, Function<GlslExpr, GlslExpr> 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<GlslExpr, GlslExpr> perElement) {
GlslExpr e;
private GlslExpr unpackShortBackedScalar(int shortOffset, Function<GlslExpr, GlslExpr> 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<GlslExpr, GlslExpr> unpackingFunc) {
return unpackingFunc.apply(access(intOffset));
}
private GlslExpr unpackVector(String outType, int size, int byteOffset, int byteSize, Function<GlslExpr, GlslExpr> 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<GlslExpr, GlslExpr> perElement) {
return perElement.apply(access(uintOffset));
}
private GlslExpr unpackByteBackedVector(String outType, int size, int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
private GlslExpr unpackByteBackedVector(String outType, int size, int byteOffset, Function<GlslExpr, GlslExpr> unpackingFunc) {
List<GlslExpr> 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<GlslExpr, GlslExpr> perElement) {
private GlslExpr unpackShortBackedVector(String outType, int size, int shortOffset, Function<GlslExpr, GlslExpr> unpackingFunc) {
List<GlslExpr> 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<GlslExpr, GlslExpr> perElement) {
private GlslExpr unpackIntBackedVector(String outType, int size, int intOffset, Function<GlslExpr, GlslExpr> unpackingFunc) {
List<GlslExpr> 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<GlslExpr, GlslExpr> 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);
}
}

View file

@ -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();
}
}

View file

@ -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<GlslExpr>();
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));

View file

@ -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<I extends Instance> {
private static final Comparator<IndirectDraw> DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::stage)
@ -58,8 +59,8 @@ public class IndirectCullingGroup<I extends Instance> {
IndirectCullingGroup(InstanceType<I> 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;

View file

@ -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<I extends Instance> extends AbstractInstancer<I> {
private final long instanceStride;
@ -27,8 +28,8 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
public IndirectInstancer(InstanceType<I> 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();
}

View file

@ -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());
}
}

View file

@ -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<Element> 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<String> name2IndexMap = new Object2IntOpenHashMap<>();
name2IndexMap.defaultReturnValue(-1);
@ -172,7 +218,17 @@ public class LayoutBuilderImpl implements LayoutBuilder {
}
}
return new LayoutImpl(List.copyOf(elements), offset);
List<Element> 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);
}
}
}

View file

@ -17,8 +17,9 @@ final class LayoutImpl implements Layout {
@Unmodifiable
private final Map<String, Element> map;
private final int byteSize;
private final int byteAlignment;
LayoutImpl(@Unmodifiable List<Element> elements, int byteSize) {
LayoutImpl(@Unmodifiable List<Element> elements, int byteSize, int byteAlignment) {
this.elements = elements;
Object2ObjectOpenHashMap<String, Element> 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 {
}
}

View file

@ -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() + "]";
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.lib.visual.components;
package com.jozufozu.flywheel.lib.visual.component;
import org.joml.Quaternionf;

View file

@ -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;

View file

@ -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;