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 f1554e959e
commit cddac38f76
15 changed files with 41 additions and 437 deletions

View File

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

View File

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

View File

@ -21,7 +21,7 @@ public class PipelineCompiler {
.withResource(pipeline.vertexApiImpl())
.withResource(InternalVertex.LAYOUT_SHADER)
.withComponent(key -> pipeline.assembler()
.assemble(new Pipeline.InstanceAssemblerContext(InternalVertex.LAYOUT.getAttributeCount(), key.instanceType())))
.assemble(new Pipeline.InstanceAssemblerContext(InternalVertex.ATTRIBUTE_COUNT, key.instanceType())))
.withComponents(vertexComponents)
.withResource(key -> key.instanceType()
.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: we definitely don't consider endianness. this all assumes little endian which works on my machine.
var type = element.type();
var name = "_" + element.name();
if (type instanceof ScalarElementType scalar) {
return unpackScalar(element.name(), packed, scalar);
return unpackScalar(name, packed, scalar);
} else if (type instanceof VectorElementType vector) {
return unpackVector(element.name(), packed, vector);
return unpackVector(name, packed, vector);
} else if (type instanceof MatrixElementType matrix) {
return unpackMatrix(element.name(), packed, matrix);
return unpackMatrix(name, packed, matrix);
}
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) {
List<GlslExpr> args = new ArrayList<>();
for (int i = 0; i < size; i++) {
var name = "_" + fieldName + "_" + i;
var name = fieldName + "_" + i;
packed.addField(backingType, name);
args.add(UNPACKING_VARIABLE.access(name)
.transform(perElement));
@ -258,7 +259,7 @@ public class IndirectComponent implements SourceComponent {
for (int i = 0; i < size; i++) {
int unpackField = i / 2;
int bitPos = (i % 2) * 16;
var name = "_" + fieldName + "_" + unpackField;
var name = fieldName + "_" + unpackField;
if (bitPos == 0) {
// First time we're seeing this field, add it to the struct.
packed.addField("uint", name);

View File

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

View File

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

View File

@ -36,7 +36,7 @@ public class InstancedMeshPool {
*/
public InstancedMeshPool() {
vertexView = InternalVertex.createVertexView();
int stride = InternalVertex.LAYOUT.getStride();
int stride = InternalVertex.STRIDE;
vbo = new GlBuffer();
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) {
this.mesh = mesh;
vertexCount = mesh.vertexCount();
byteSize = vertexCount * InternalVertex.LAYOUT.getStride();
byteSize = vertexCount * InternalVertex.STRIDE;
this.byteIndex = byteIndex;
this.ebo = eboCache.get(mesh.indexSequence(), mesh.indexCount());
}
@ -182,8 +182,8 @@ public class InstancedMeshPool {
public void setup(GlVertexArray vao) {
if (boundTo.add(vao)) {
vao.bindVertexBuffer(0, InstancedMeshPool.this.vbo.handle(), byteIndex, InternalVertex.LAYOUT.getStride());
vao.bindAttributes(0, 0, InternalVertex.LAYOUT.attributes());
vao.bindVertexBuffer(0, InstancedMeshPool.this.vbo.handle(), byteIndex, InternalVertex.STRIDE);
vao.bindAttributes(0, 0, InternalVertex.ATTRIBUTES);
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) {
elements.add(new ElementImpl(name, type, offset));
elements.add(new ElementImpl(name, type, offset, MoreMath.align4(type.byteSize())));
offset += type.byteSize();
offset = MoreMath.align4(offset);
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) {

View File

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