Layout down to rest

- Remove BufferLayout and associated classes.
- Replace InternalVertex with new Layout.
- Calculate InternalVertex stuff as static final fields.
- Elements track their own byte size.
  - The byte size of an element type is unchanged.
  - The byte size of an element is 4-aligned.
- Layout byte size now accounts for alignment.
- Generated packed fields are now always prefixed with an underscore.
This commit is contained in:
Jozufozu 2024-01-05 23:03:09 -08:00
parent 5276a4456c
commit 0deac32fde
15 changed files with 41 additions and 437 deletions

View File

@ -24,5 +24,7 @@ public interface Layout {
ElementType type(); ElementType type();
int offset(); int offset();
int byteSize();
} }
} }

View File

@ -1,24 +1,33 @@
package com.jozufozu.flywheel.backend; package com.jozufozu.flywheel.backend;
import java.util.List;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
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.layout.UnsignedIntegerRepr;
import com.jozufozu.flywheel.api.vertex.VertexView; import com.jozufozu.flywheel.api.vertex.VertexView;
import com.jozufozu.flywheel.backend.gl.layout.BufferLayout; import com.jozufozu.flywheel.backend.engine.LayoutAttributes;
import com.jozufozu.flywheel.backend.gl.layout.CommonItems; import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.lib.vertex.FullVertexView; import com.jozufozu.flywheel.lib.vertex.FullVertexView;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public final class InternalVertex { public final class InternalVertex {
public static final BufferLayout LAYOUT = BufferLayout.builder() public static final Layout LAYOUT = LayoutBuilder.create()
.addItem(CommonItems.VEC3, "position") .vector("position", FloatRepr.FLOAT, 3)
.addItem(CommonItems.UNORM_4x8, "color") .vector("color", FloatRepr.NORMALIZED_UNSIGNED_BYTE, 4)
.addItem(CommonItems.VEC2, "tex") .vector("tex", FloatRepr.FLOAT, 2)
.addItem(CommonItems.LIGHT_COORD, "overlay") .vector("overlay", UnsignedIntegerRepr.UNSIGNED_SHORT, 2)
.addItem(CommonItems.LIGHT_COORD, "light") .vector("light", UnsignedIntegerRepr.UNSIGNED_SHORT, 2)
.addItem(CommonItems.NORM_3x8, "normal") .vector("normal", FloatRepr.NORMALIZED_BYTE, 3)
.withPadding(1)
.build(); .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"); public static final ResourceLocation LAYOUT_SHADER = Flywheel.rl("internal/vertex_input.vert");
private InternalVertex() { private InternalVertex() {

View File

@ -21,7 +21,7 @@ public class PipelineCompiler {
.withResource(pipeline.vertexApiImpl()) .withResource(pipeline.vertexApiImpl())
.withResource(InternalVertex.LAYOUT_SHADER) .withResource(InternalVertex.LAYOUT_SHADER)
.withComponent(key -> pipeline.assembler() .withComponent(key -> pipeline.assembler()
.assemble(new Pipeline.InstanceAssemblerContext(InternalVertex.LAYOUT.getAttributeCount(), key.instanceType()))) .assemble(new Pipeline.InstanceAssemblerContext(InternalVertex.ATTRIBUTE_COUNT, key.instanceType())))
.withComponents(vertexComponents) .withComponents(vertexComponents)
.withResource(key -> key.instanceType() .withResource(key -> key.instanceType()
.vertexShader()) .vertexShader())

View File

@ -113,13 +113,14 @@ public class IndirectComponent implements SourceComponent {
// FIXME: I don't think we're unpacking signed byte/short values correctly // 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. // FIXME: we definitely don't consider endianness. this all assumes little endian which works on my machine.
var type = element.type(); var type = element.type();
var name = "_" + element.name();
if (type instanceof ScalarElementType scalar) { if (type instanceof ScalarElementType scalar) {
return unpackScalar(element.name(), packed, scalar); return unpackScalar(name, packed, scalar);
} else if (type instanceof VectorElementType vector) { } else if (type instanceof VectorElementType vector) {
return unpackVector(element.name(), packed, vector); return unpackVector(name, packed, vector);
} else if (type instanceof MatrixElementType matrix) { } else if (type instanceof MatrixElementType matrix) {
return unpackMatrix(element.name(), packed, matrix); return unpackMatrix(name, packed, matrix);
} }
throw new IllegalArgumentException("Unknown type " + type); throw new IllegalArgumentException("Unknown type " + type);
@ -217,7 +218,7 @@ public class IndirectComponent implements SourceComponent {
private static GlslExpr unpack(String fieldName, GlslStruct packed, int size, String backingType, String outType, Function<GlslExpr, GlslExpr> perElement) { private static GlslExpr unpack(String fieldName, GlslStruct packed, int size, String backingType, String outType, Function<GlslExpr, GlslExpr> perElement) {
List<GlslExpr> args = new ArrayList<>(); List<GlslExpr> args = new ArrayList<>();
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
var name = "_" + fieldName + "_" + i; var name = fieldName + "_" + i;
packed.addField(backingType, name); packed.addField(backingType, name);
args.add(UNPACKING_VARIABLE.access(name) args.add(UNPACKING_VARIABLE.access(name)
.transform(perElement)); .transform(perElement));
@ -258,7 +259,7 @@ public class IndirectComponent implements SourceComponent {
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
int unpackField = i / 2; int unpackField = i / 2;
int bitPos = (i % 2) * 16; int bitPos = (i % 2) * 16;
var name = "_" + fieldName + "_" + unpackField; var name = fieldName + "_" + unpackField;
if (bitPos == 0) { if (bitPos == 0) {
// First time we're seeing this field, add it to the struct. // First time we're seeing this field, add it to the struct.
packed.addField("uint", name); packed.addField("uint", name);

View File

@ -37,8 +37,8 @@ public class IndirectMeshPool {
vertexArray = GlVertexArray.create(); vertexArray = GlVertexArray.create();
vertexArray.setElementBuffer(ebo.handle()); vertexArray.setElementBuffer(ebo.handle());
vertexArray.bindVertexBuffer(0, vbo.handle(), 0, InternalVertex.LAYOUT.getStride()); vertexArray.bindVertexBuffer(0, vbo.handle(), 0, InternalVertex.STRIDE);
vertexArray.bindAttributes(0, 0, InternalVertex.LAYOUT.attributes()); vertexArray.bindAttributes(0, 0, InternalVertex.ATTRIBUTES);
} }
/** /**
@ -151,7 +151,7 @@ public class IndirectMeshPool {
private BufferedMesh(Mesh mesh) { private BufferedMesh(Mesh mesh) {
this.mesh = mesh; this.mesh = mesh;
vertexCount = mesh.vertexCount(); vertexCount = mesh.vertexCount();
byteSize = vertexCount * InternalVertex.LAYOUT.getStride(); byteSize = vertexCount * InternalVertex.STRIDE;
} }
public int vertexCount() { public int vertexCount() {

View File

@ -36,7 +36,7 @@ public class DrawCall {
return; return;
} }
instancer.bindIfNeeded(vao, InternalVertex.LAYOUT.getAttributeCount()); instancer.bindIfNeeded(vao, InternalVertex.ATTRIBUTE_COUNT);
mesh.setup(vao); mesh.setup(vao);
vao.bindForDraw(); vao.bindForDraw();
@ -58,7 +58,7 @@ public class DrawCall {
var vao = lazyScratchVao(); var vao = lazyScratchVao();
instancer.bindRaw(vao, InternalVertex.LAYOUT.getAttributeCount(), impl.index); instancer.bindRaw(vao, InternalVertex.ATTRIBUTE_COUNT, impl.index);
mesh.setup(vao); mesh.setup(vao);
vao.bindForDraw(); vao.bindForDraw();

View File

@ -36,7 +36,7 @@ public class InstancedMeshPool {
*/ */
public InstancedMeshPool() { public InstancedMeshPool() {
vertexView = InternalVertex.createVertexView(); vertexView = InternalVertex.createVertexView();
int stride = InternalVertex.LAYOUT.getStride(); int stride = InternalVertex.STRIDE;
vbo = new GlBuffer(); vbo = new GlBuffer();
vbo.growthFunction(l -> Math.max(l + stride * 128L, (long) (l * 1.6))); vbo.growthFunction(l -> Math.max(l + stride * 128L, (long) (l * 1.6)));
} }
@ -149,7 +149,7 @@ public class InstancedMeshPool {
private BufferedMesh(Mesh mesh, long byteIndex, EboCache eboCache) { private BufferedMesh(Mesh mesh, long byteIndex, EboCache eboCache) {
this.mesh = mesh; this.mesh = mesh;
vertexCount = mesh.vertexCount(); vertexCount = mesh.vertexCount();
byteSize = vertexCount * InternalVertex.LAYOUT.getStride(); byteSize = vertexCount * InternalVertex.STRIDE;
this.byteIndex = byteIndex; this.byteIndex = byteIndex;
this.ebo = eboCache.get(mesh.indexSequence(), mesh.indexCount()); this.ebo = eboCache.get(mesh.indexSequence(), mesh.indexCount());
} }
@ -182,8 +182,8 @@ public class InstancedMeshPool {
public void setup(GlVertexArray vao) { public void setup(GlVertexArray vao) {
if (boundTo.add(vao)) { if (boundTo.add(vao)) {
vao.bindVertexBuffer(0, InstancedMeshPool.this.vbo.handle(), byteIndex, InternalVertex.LAYOUT.getStride()); vao.bindVertexBuffer(0, InstancedMeshPool.this.vbo.handle(), byteIndex, InternalVertex.STRIDE);
vao.bindAttributes(0, 0, InternalVertex.LAYOUT.attributes()); vao.bindAttributes(0, 0, InternalVertex.ATTRIBUTES);
vao.setElementBuffer(ebo); vao.setElementBuffer(ebo);
} }
} }

View File

@ -1,85 +0,0 @@
package com.jozufozu.flywheel.backend.gl.layout;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
/**
* Classic Vertex Format with a clever name.
*
* <p>
* Used for vertices and instances. Describes the layout of a datatype in a buffer object.
* </p>
*
* @see com.jozufozu.flywheel.api.instance.InstanceType
*/
public class BufferLayout {
public final List<LayoutItem> layoutItems;
public final List<VertexAttribute> attributes;
private final int stride;
public BufferLayout(List<LayoutItem> layoutItems, int padding) {
this.layoutItems = ImmutableList.copyOf(layoutItems);
ImmutableList.Builder<VertexAttribute> attributes = ImmutableList.builder();
for (var item : this.layoutItems) {
item.type()
.provideAttributes(attributes::add);
}
this.attributes = attributes.build();
this.stride = calculateStride(this.attributes) + padding;
}
public List<VertexAttribute> attributes() {
return attributes;
}
public int getAttributeCount() {
return attributes.size();
}
public int getStride() {
return stride;
}
public static Builder builder() {
return new Builder();
}
private static int calculateStride(List<VertexAttribute> layoutItems) {
int stride = 0;
for (var spec : layoutItems) {
stride += spec.byteWidth();
}
return stride;
}
public static class Builder {
private final ImmutableList.Builder<LayoutItem> allItems;
private int padding;
public Builder() {
allItems = ImmutableList.builder();
}
public Builder addItem(InputType type, String name) {
allItems.add(new LayoutItem(type, name));
return this;
}
public Builder withPadding(int padding) {
this.padding = padding;
return this;
}
public BufferLayout build() {
return new BufferLayout(allItems.build(), padding);
}
}
}

View File

@ -1,134 +0,0 @@
package com.jozufozu.flywheel.backend.gl.layout;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.backend.glsl.generate.FnSignature;
import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr;
public final class CommonItems {
private static final String VEC2_TYPE = "vec2";
private static final String VEC3_TYPE = "vec3";
private static final String VEC4_TYPE = "vec4";
private static final String VEC2F_TYPE = "Vec2F";
private static final String VEC3F_TYPE = "Vec3F";
private static final String VEC4F_TYPE = "Vec4F";
private static final String IVEC2_TYPE = "ivec2";
private static final String FLOAT_TYPE = "float";
private static final String UINT_TYPE = "uint";
private static final String LIGHT_COORD_TYPE = "uint";
public static final VecInput LIGHT_COORD = VecInput.builder()
.vertexAttribute(new VertexAttribute.Int(GlNumericType.USHORT, 2))
.typeName(IVEC2_TYPE)
.packedTypeName(LIGHT_COORD_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackLightCoord"))
.declaration(builder -> builder.function()
.signature(FnSignature.create()
.name("unpackLightCoord")
.returnType(IVEC2_TYPE)
.arg("uint", "light")
.build())
.body(block -> block.raw("return ivec2(light & 0xFFFFu, (light >> 16) & 0xFFFFu);")))
.build();
public static final VecInput FLOAT = VecInput.builder()
.vertexAttribute(new VertexAttribute.Float(GlNumericType.FLOAT, 1, false))
.typeName(FLOAT_TYPE)
.packedTypeName(FLOAT_TYPE)
.build();
public static final VecInput NORM_3x8 = VecInput.builder()
.vertexAttribute(new VertexAttribute.Float(GlNumericType.BYTE, 3, true))
.typeName(VEC3_TYPE)
.packedTypeName(UINT_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackSnorm4x8")
.swizzle("xyz"))
.build();
public static final VecInput UNORM_4x8 = VecInput.builder()
.vertexAttribute(new VertexAttribute.Float(GlNumericType.UBYTE, 4, true))
.typeName(VEC4_TYPE)
.packedTypeName(UINT_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackUnorm4x8"))
.build();
public static final VecInput UNORM_3x8 = VecInput.builder()
.vertexAttribute(new VertexAttribute.Float(GlNumericType.UBYTE, 3, true))
.typeName(VEC3_TYPE)
.packedTypeName(UINT_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackUnorm4x8")
.swizzle("xyz"))
.build();
public static final VecInput VEC4 = VecInput.builder()
.vertexAttribute(new VertexAttribute.Float(GlNumericType.FLOAT, 4, false))
.typeName(VEC4_TYPE)
.packedTypeName(VEC4F_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackVec4F"))
.declaration(builder -> {
builder.struct()
.setName(VEC4F_TYPE)
.addField(FLOAT_TYPE, "x")
.addField(FLOAT_TYPE, "y")
.addField(FLOAT_TYPE, "z")
.addField(FLOAT_TYPE, "w");
builder.function()
.signature(FnSignature.create()
.name("unpackVec4F")
.returnType(VEC4_TYPE)
.arg(VEC4F_TYPE, "v")
.build())
.body(block -> {
var v = GlslExpr.variable("v");
block.ret(GlslExpr.call("vec4", v.access("x"), v.access("y"), v.access("z"), v.access("w")));
});
})
.build();
public static final VecInput VEC3 = VecInput.builder()
.vertexAttribute(new VertexAttribute.Float(GlNumericType.FLOAT, 3, false))
.typeName(VEC3_TYPE)
.packedTypeName(VEC3F_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackVec3F"))
.declaration(builder -> {
builder.struct()
.setName(VEC3F_TYPE)
.addField(FLOAT_TYPE, "x")
.addField(FLOAT_TYPE, "y")
.addField(FLOAT_TYPE, "z");
builder.function()
.signature(FnSignature.create()
.name("unpackVec3F")
.returnType(VEC3_TYPE)
.arg(VEC3F_TYPE, "v")
.build())
.body(block -> {
var v = GlslExpr.variable("v");
block.ret(GlslExpr.call("vec3", v.access("x"), v.access("y"), v.access("z")));
});
})
.build();
public static final VecInput VEC2 = VecInput.builder()
.vertexAttribute(new VertexAttribute.Float(GlNumericType.FLOAT, 2, false))
.typeName(VEC2_TYPE)
.packedTypeName(VEC2F_TYPE)
.unpackingFunction(expr -> expr.callFunction("unpackVec2F"))
.declaration(builder -> {
builder.struct()
.setName(VEC2F_TYPE)
.addField(FLOAT_TYPE, "x")
.addField(FLOAT_TYPE, "y");
builder.function()
.signature(FnSignature.create()
.name("unpackVec2F")
.returnType(VEC2_TYPE)
.arg(VEC2F_TYPE, "v")
.build())
.body(block -> {
var v = GlslExpr.variable("v");
block.ret(GlslExpr.call("vec2", v.access("x"), v.access("y")));
});
})
.build();
public static final MatInput MAT3 = new MatInput(3, 3, "mat3", "Mat3F", "unpackMat3F");
public static final MatInput MAT4 = new MatInput(4, 4, "mat4", "Mat4F", "unpackMat4F");
private CommonItems() {
}
}

View File

@ -1,22 +0,0 @@
package com.jozufozu.flywheel.backend.gl.layout;
import java.util.function.Consumer;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder;
import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr;
public interface InputType {
void provideAttributes(Consumer<VertexAttribute> consumer);
String typeName();
String packedTypeName();
int attributeCount();
GlslExpr unpack(GlslExpr packed);
void declare(GlslBuilder builder);
}

View File

@ -1,5 +0,0 @@
package com.jozufozu.flywheel.backend.gl.layout;
public record LayoutItem(InputType type, String name) {
}

View File

@ -1,64 +0,0 @@
package com.jozufozu.flywheel.backend.gl.layout;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.backend.glsl.generate.FnSignature;
import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder;
import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr;
public record MatInput(int rows, int cols, String typeName, String packedTypeName,
String unpackingFunction) implements InputType {
@Override
public void provideAttributes(Consumer<VertexAttribute> consumer) {
for (int i = 0; i < rows; i++) {
consumer.accept(new VertexAttribute.Float(GlNumericType.FLOAT, cols, false));
}
}
@Override
public int attributeCount() {
return rows;
}
@Override
public GlslExpr unpack(GlslExpr packed) {
return packed.callFunction(unpackingFunction);
}
@Override
public void declare(GlslBuilder builder) {
var s = builder.struct();
s.setName(packedTypeName);
for (int c = 0; c < cols; c++) {
for (int r = 0; r < rows; r++) {
s.addField("float", "m" + c + r);
}
}
builder.function()
.signature(FnSignature.create()
.name(unpackingFunction)
.returnType(typeName)
.arg(packedTypeName, "p")
.build())
.body(block -> {
List<GlslExpr> args = new ArrayList<>();
var p = GlslExpr.variable("p");
for (int c = 0; c < cols; c++) {
for (int r = 0; r < rows; r++) {
args.add(p.access("m" + c + r));
}
}
block.ret(GlslExpr.call(typeName, args));
});
}
}

View File

@ -1,96 +0,0 @@
package com.jozufozu.flywheel.backend.gl.layout;
import java.util.function.Consumer;
import java.util.function.Function;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder;
import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr;
public class VecInput implements InputType {
private final VertexAttribute attribute;
private final String typeName;
private final String packedTypeName;
private final Function<GlslExpr, GlslExpr> unpackingFunction;
private final Consumer<GlslBuilder> declaration;
public VecInput(VertexAttribute attribute, String typeName, String packedTypeName, Function<GlslExpr, GlslExpr> unpackingFunction, Consumer<GlslBuilder> declaration) {
this.attribute = attribute;
this.typeName = typeName;
this.packedTypeName = packedTypeName;
this.unpackingFunction = unpackingFunction;
this.declaration = declaration;
}
@Override
public void provideAttributes(Consumer<VertexAttribute> consumer) {
consumer.accept(attribute);
}
@Override
public String typeName() {
return typeName;
}
@Override
public String packedTypeName() {
return packedTypeName;
}
@Override
public int attributeCount() {
return 1;
}
public static Builder builder() {
return new Builder();
}
@Override
public GlslExpr unpack(GlslExpr packed) {
return unpackingFunction.apply(packed);
}
@Override
public void declare(GlslBuilder builder) {
declaration.accept(builder);
}
public static class Builder {
private VertexAttribute attribute;
private String typeName;
private String packedTypeName;
private Function<GlslExpr, GlslExpr> unpackingFunction = Function.identity();
private Consumer<GlslBuilder> declaration = $ -> {
};
public Builder vertexAttribute(VertexAttribute attribute) {
this.attribute = attribute;
return this;
}
public Builder typeName(String typeName) {
this.typeName = typeName;
return this;
}
public Builder packedTypeName(String packedTypeName) {
this.packedTypeName = packedTypeName;
return this;
}
public Builder unpackingFunction(Function<GlslExpr, GlslExpr> f) {
this.unpackingFunction = f;
return this;
}
public Builder declaration(Consumer<GlslBuilder> declaration) {
this.declaration = declaration;
return this;
}
public VecInput build() {
return new VecInput(attribute, typeName, packedTypeName, unpackingFunction, declaration);
}
}
}

View File

@ -126,7 +126,7 @@ public class LayoutBuilderImpl implements LayoutBuilder {
} }
private LayoutBuilder element(String name, ElementType type) { private LayoutBuilder element(String name, ElementType type) {
elements.add(new ElementImpl(name, type, offset)); elements.add(new ElementImpl(name, type, offset, MoreMath.align4(type.byteSize())));
offset += type.byteSize(); offset += type.byteSize();
offset = MoreMath.align4(offset); offset = MoreMath.align4(offset);
return this; return this;
@ -173,7 +173,7 @@ public class LayoutBuilderImpl implements LayoutBuilder {
} }
} }
return new LayoutImpl(List.copyOf(elements)); return new LayoutImpl(List.copyOf(elements), offset);
} }
private static boolean isValidNameCharacter(char c) { private static boolean isValidNameCharacter(char c) {

View File

@ -18,14 +18,12 @@ final class LayoutImpl implements Layout {
private final Map<String, Element> map; private final Map<String, Element> map;
private final int byteSize; private final int byteSize;
LayoutImpl(@Unmodifiable List<Element> elements) { LayoutImpl(@Unmodifiable List<Element> elements, int byteSize) {
this.elements = elements; this.elements = elements;
Object2ObjectOpenHashMap<String, Element> map = new Object2ObjectOpenHashMap<>(); Object2ObjectOpenHashMap<String, Element> map = new Object2ObjectOpenHashMap<>();
int byteSize = 0;
for (Element element : this.elements) { for (Element element : this.elements) {
map.put(element.name(), element); map.put(element.name(), element);
byteSize += element.type().byteSize();
} }
map.trim(); map.trim();
@ -73,6 +71,6 @@ final class LayoutImpl implements Layout {
return elements.equals(other.elements); return elements.equals(other.elements);
} }
record ElementImpl(String name, ElementType type, int offset) implements Element { record ElementImpl(String name, ElementType type, int offset, int byteSize) implements Element {
} }
} }