mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-15 00:36:08 +01:00
Textures your buffer
- Use texture buffers instead of instanced arrays - Fixes issues with instance layouts using int elements - Fixes attribute binding hardware limitations - Create one texture for the entire instancing engine and bind each instancer's vbo when it's time to draw - Generate glsl to fetch FlwInstances from a usamplerBuffer according to the instance layout - Use RGBA32UI to get the most data from each texel fetch - Align instance stride to 16 to avoid complexity in unpacking
This commit is contained in:
parent
17130e22ea
commit
c6ed7c4132
11 changed files with 392 additions and 224 deletions
|
@ -2,7 +2,7 @@ package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.backend.compile.component.IndirectComponent;
|
import com.jozufozu.flywheel.backend.compile.component.IndirectComponent;
|
||||||
import com.jozufozu.flywheel.backend.compile.component.InstancedArraysComponent;
|
import com.jozufozu.flywheel.backend.compile.component.SamplerBufferComponent;
|
||||||
import com.jozufozu.flywheel.backend.glsl.GlslVersion;
|
import com.jozufozu.flywheel.backend.glsl.GlslVersion;
|
||||||
|
|
||||||
public final class Pipelines {
|
public final class Pipelines {
|
||||||
|
@ -13,7 +13,7 @@ public final class Pipelines {
|
||||||
.fragmentMain(Flywheel.rl("internal/instancing/main.frag"))
|
.fragmentMain(Flywheel.rl("internal/instancing/main.frag"))
|
||||||
.vertexApiImpl(Flywheel.rl("internal/instancing/api_impl.vert"))
|
.vertexApiImpl(Flywheel.rl("internal/instancing/api_impl.vert"))
|
||||||
.fragmentApiImpl(Flywheel.rl("internal/instancing/api_impl.frag"))
|
.fragmentApiImpl(Flywheel.rl("internal/instancing/api_impl.frag"))
|
||||||
.assembler(InstancedArraysComponent::new)
|
.assembler(SamplerBufferComponent::create)
|
||||||
.build();
|
.build();
|
||||||
public static final Pipeline INDIRECT = Pipeline.builder()
|
public static final Pipeline INDIRECT = Pipeline.builder()
|
||||||
.compilerMarker("indirect")
|
.compilerMarker("indirect")
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.compile.component;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
|
||||||
import com.jozufozu.flywheel.api.layout.Layout;
|
|
||||||
import com.jozufozu.flywheel.backend.compile.LayoutInterpreter;
|
|
||||||
import com.jozufozu.flywheel.backend.compile.Pipeline;
|
|
||||||
import com.jozufozu.flywheel.backend.glsl.SourceComponent;
|
|
||||||
import com.jozufozu.flywheel.backend.glsl.generate.FnSignature;
|
|
||||||
import com.jozufozu.flywheel.backend.glsl.generate.GlslBlock;
|
|
||||||
import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder;
|
|
||||||
import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
public class InstancedArraysComponent implements SourceComponent {
|
|
||||||
private static final String ATTRIBUTE_PREFIX = "_flw_i_";
|
|
||||||
private static final String STRUCT_NAME = "FlwInstance";
|
|
||||||
private static final String UNPACK_FN_NAME = "_flw_unpackInstance";
|
|
||||||
|
|
||||||
private final Layout layout;
|
|
||||||
private final int baseIndex;
|
|
||||||
|
|
||||||
public InstancedArraysComponent(Pipeline.InstanceAssemblerContext ctx) {
|
|
||||||
this.layout = ctx.instanceType()
|
|
||||||
.layout();
|
|
||||||
this.baseIndex = ctx.baseAttribute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<? extends SourceComponent> included() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation name() {
|
|
||||||
return Flywheel.rl("generated_instanced_arrays");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String source() {
|
|
||||||
var builder = new GlslBuilder();
|
|
||||||
|
|
||||||
generateVertexInput(builder);
|
|
||||||
|
|
||||||
builder.blankLine();
|
|
||||||
|
|
||||||
var structBuilder = builder.struct();
|
|
||||||
structBuilder.setName(STRUCT_NAME);
|
|
||||||
|
|
||||||
for (var element : layout.elements()) {
|
|
||||||
structBuilder.addField(LayoutInterpreter.typeName(element.type()), element.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.blankLine();
|
|
||||||
|
|
||||||
// unpacking function
|
|
||||||
builder.function()
|
|
||||||
.signature(FnSignature.of(STRUCT_NAME, UNPACK_FN_NAME))
|
|
||||||
.body(this::generateUnpackingBody);
|
|
||||||
|
|
||||||
builder.blankLine();
|
|
||||||
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateVertexInput(GlslBuilder builder) {
|
|
||||||
int i = baseIndex;
|
|
||||||
for (var element : layout.elements()) {
|
|
||||||
var type = element.type();
|
|
||||||
|
|
||||||
builder.vertexInput()
|
|
||||||
.binding(i)
|
|
||||||
.type(LayoutInterpreter.typeName(type))
|
|
||||||
.name(ATTRIBUTE_PREFIX + element.name());
|
|
||||||
|
|
||||||
i += LayoutInterpreter.attributeCount(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateUnpackingBody(GlslBlock b) {
|
|
||||||
var fields = layout.elements()
|
|
||||||
.stream()
|
|
||||||
.map(it -> new GlslExpr.Variable(ATTRIBUTE_PREFIX + it.name()))
|
|
||||||
.toList();
|
|
||||||
b.ret(GlslExpr.call(STRUCT_NAME, fields));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,322 @@
|
||||||
|
package com.jozufozu.flywheel.backend.compile.component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
import com.jozufozu.flywheel.api.layout.FloatRepr;
|
||||||
|
import com.jozufozu.flywheel.api.layout.IntegerRepr;
|
||||||
|
import com.jozufozu.flywheel.api.layout.Layout;
|
||||||
|
import com.jozufozu.flywheel.api.layout.MatrixElementType;
|
||||||
|
import com.jozufozu.flywheel.api.layout.ScalarElementType;
|
||||||
|
import com.jozufozu.flywheel.api.layout.UnsignedIntegerRepr;
|
||||||
|
import com.jozufozu.flywheel.api.layout.VectorElementType;
|
||||||
|
import com.jozufozu.flywheel.backend.compile.LayoutInterpreter;
|
||||||
|
import com.jozufozu.flywheel.backend.compile.Pipeline;
|
||||||
|
import com.jozufozu.flywheel.backend.glsl.SourceComponent;
|
||||||
|
import com.jozufozu.flywheel.backend.glsl.generate.FnSignature;
|
||||||
|
import com.jozufozu.flywheel.backend.glsl.generate.GlslBlock;
|
||||||
|
import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder;
|
||||||
|
import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr;
|
||||||
|
import com.jozufozu.flywheel.backend.glsl.generate.GlslStmt;
|
||||||
|
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public class SamplerBufferComponent implements SourceComponent {
|
||||||
|
private static final String STRUCT_NAME = "FlwInstance";
|
||||||
|
private static final String UNPACK_FN_NAME = "_flw_unpackInstance";
|
||||||
|
private static final String UNPACK_ARG = "index";
|
||||||
|
|
||||||
|
private final Layout layout;
|
||||||
|
|
||||||
|
public SamplerBufferComponent(InstanceType<?> type) {
|
||||||
|
this.layout = type.layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SamplerBufferComponent create(Pipeline.InstanceAssemblerContext ctx) {
|
||||||
|
return create(ctx.instanceType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SamplerBufferComponent create(InstanceType<?> instanceType) {
|
||||||
|
return new SamplerBufferComponent(instanceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends SourceComponent> included() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation name() {
|
||||||
|
return Flywheel.rl("generated_indirect");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String source() {
|
||||||
|
return generateIndirect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateIndirect() {
|
||||||
|
var builder = new GlslBuilder();
|
||||||
|
|
||||||
|
generateInstanceStruct(builder);
|
||||||
|
|
||||||
|
builder.blankLine();
|
||||||
|
|
||||||
|
builder._addRaw("uniform usamplerBuffer _flw_instances;");
|
||||||
|
|
||||||
|
builder.blankLine();
|
||||||
|
|
||||||
|
generateUnpacking(builder);
|
||||||
|
|
||||||
|
builder.blankLine();
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateInstanceStruct(GlslBuilder builder) {
|
||||||
|
var instance = builder.struct();
|
||||||
|
instance.setName(STRUCT_NAME);
|
||||||
|
for (var element : layout.elements()) {
|
||||||
|
instance.addField(LayoutInterpreter.typeName(element.type()), element.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateUnpacking(GlslBuilder builder) {
|
||||||
|
var block = new GlslBlock();
|
||||||
|
|
||||||
|
var texels = MoreMath.ceilingDiv(layout.byteSize(), 16);
|
||||||
|
|
||||||
|
block.add(GlslStmt.raw("int base = " + UNPACK_ARG + " * " + texels + ";"));
|
||||||
|
|
||||||
|
for (int i = 0; i < texels; i++) {
|
||||||
|
// Fetch all the texels for the given instance ahead of time to simplify the unpacking generators.
|
||||||
|
block.add(GlslStmt.raw("uvec4 u" + i + " = texelFetch(_flw_instances, base + " + i + ");"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var unpackArgs = new ArrayList<GlslExpr>();
|
||||||
|
int uintOffset = 0;
|
||||||
|
for (Layout.Element element : layout.elements()) {
|
||||||
|
unpackArgs.add(unpackElement(element, uintOffset));
|
||||||
|
uintOffset += MoreMath.ceilingDiv(element.type().byteSize(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.ret(GlslExpr.call(STRUCT_NAME, unpackArgs));
|
||||||
|
|
||||||
|
builder.function()
|
||||||
|
.signature(FnSignature.create()
|
||||||
|
.returnType(STRUCT_NAME)
|
||||||
|
.name(UNPACK_FN_NAME)
|
||||||
|
.arg("int", UNPACK_ARG)
|
||||||
|
.build())
|
||||||
|
.body(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GlslExpr access(int uintOffset) {
|
||||||
|
return GlslExpr.variable("u" + (uintOffset >> 2))
|
||||||
|
.swizzle(String.valueOf("xyzw".charAt(uintOffset & 3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: deduplicate this with IndirectComponent somehow?
|
||||||
|
|
||||||
|
public static GlslExpr unpackElement(Layout.Element element, int uintOffset) {
|
||||||
|
// FIXME: I don't think we're unpacking signed byte/short values correctly
|
||||||
|
// FIXME: we definitely don't consider endianness. this all assumes little endian which works on my machine.
|
||||||
|
var type = element.type();
|
||||||
|
|
||||||
|
if (type instanceof ScalarElementType scalar) {
|
||||||
|
return unpackScalar(uintOffset, scalar);
|
||||||
|
} else if (type instanceof VectorElementType vector) {
|
||||||
|
return unpackVector(uintOffset, vector);
|
||||||
|
} else if (type instanceof MatrixElementType matrix) {
|
||||||
|
return unpackMatrix(uintOffset, matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Unknown type " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackScalar(int uintOffset, ScalarElementType scalar) {
|
||||||
|
var repr = scalar.repr();
|
||||||
|
|
||||||
|
if (repr instanceof IntegerRepr intRepr) {
|
||||||
|
return unpackIntScalar(uintOffset, intRepr);
|
||||||
|
} else if (repr instanceof UnsignedIntegerRepr unsignedIntegerRepr) {
|
||||||
|
return unpackUnsignedScalar(uintOffset, unsignedIntegerRepr);
|
||||||
|
} else if (repr instanceof FloatRepr floatRepr) {
|
||||||
|
return unpackFloatScalar(uintOffset, floatRepr);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Unknown repr " + repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackIntScalar(int uintOffset, IntegerRepr intRepr) {
|
||||||
|
return switch (intRepr) {
|
||||||
|
case BYTE -> unpackScalar(uintOffset, e -> e.and(0xFF)
|
||||||
|
.cast("int"));
|
||||||
|
case SHORT -> unpackScalar(uintOffset, e -> e.and(0xFFFF)
|
||||||
|
.cast("int"));
|
||||||
|
case INT -> unpackScalar(uintOffset);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackUnsignedScalar(int uintOffset, UnsignedIntegerRepr repr) {
|
||||||
|
return switch (repr) {
|
||||||
|
case UNSIGNED_BYTE -> unpackScalar(uintOffset, e -> e.and(0xFF));
|
||||||
|
case UNSIGNED_SHORT -> unpackScalar(uintOffset, e -> e.and(0xFFFF));
|
||||||
|
case UNSIGNED_INT -> unpackScalar(uintOffset);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackFloatScalar(int uintOffset, FloatRepr repr) {
|
||||||
|
return switch (repr) {
|
||||||
|
case BYTE -> unpackScalar(uintOffset, e -> e.and(0xFF)
|
||||||
|
.cast("int")
|
||||||
|
.cast("float"));
|
||||||
|
case NORMALIZED_BYTE -> unpackScalar(uintOffset, e -> e.callFunction("unpackSnorm4x8")
|
||||||
|
.swizzle("x"));
|
||||||
|
case UNSIGNED_BYTE -> unpackScalar(uintOffset, e -> e.and(0xFF)
|
||||||
|
.cast("float"));
|
||||||
|
case NORMALIZED_UNSIGNED_BYTE ->
|
||||||
|
unpackScalar(uintOffset, e -> e.callFunction("unpackUnorm4x8")
|
||||||
|
.swizzle("x"));
|
||||||
|
case SHORT -> unpackScalar(uintOffset, e -> e.and(0xFFFF)
|
||||||
|
.cast("int")
|
||||||
|
.cast("float"));
|
||||||
|
case NORMALIZED_SHORT -> unpackScalar(uintOffset, e -> e.callFunction("unpackSnorm2x16")
|
||||||
|
.swizzle("x"));
|
||||||
|
case UNSIGNED_SHORT -> unpackScalar(uintOffset, e -> e.and(0xFFFF)
|
||||||
|
.cast("float"));
|
||||||
|
case NORMALIZED_UNSIGNED_SHORT ->
|
||||||
|
unpackScalar(uintOffset, e -> e.callFunction("unpackUnorm2x16")
|
||||||
|
.swizzle("x"));
|
||||||
|
case INT -> unpackScalar(uintOffset, e -> e.cast("int").cast("float"));
|
||||||
|
case NORMALIZED_INT -> unpackScalar(uintOffset, e -> e.div(2147483647f)
|
||||||
|
.clamp(-1, 1));
|
||||||
|
case UNSIGNED_INT -> unpackScalar(uintOffset, e -> e.cast("float"));
|
||||||
|
case NORMALIZED_UNSIGNED_INT -> unpackScalar(uintOffset, e -> e.div(4294967295f));
|
||||||
|
case FLOAT -> unpackScalar(uintOffset, e -> e.callFunction("uintBitsToFloat"));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackScalar(int uintOffset) {
|
||||||
|
return unpackScalar(uintOffset, Function.identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackScalar(int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
|
||||||
|
return perElement.apply(access(uintOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackVector(int uintOffset, VectorElementType vector) {
|
||||||
|
var repr = vector.repr();
|
||||||
|
|
||||||
|
int size = vector.size();
|
||||||
|
|
||||||
|
if (repr instanceof IntegerRepr intRepr) {
|
||||||
|
return unpackIntVector(uintOffset, intRepr, size);
|
||||||
|
} else if (repr instanceof UnsignedIntegerRepr unsignedIntegerRepr) {
|
||||||
|
return unpackUnsignedVector(uintOffset, unsignedIntegerRepr, size);
|
||||||
|
} else if (repr instanceof FloatRepr floatRepr) {
|
||||||
|
return unpackFloatVector(uintOffset, floatRepr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Unknown repr " + repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackIntVector(int uintOffset, IntegerRepr repr, int size) {
|
||||||
|
return switch (repr) {
|
||||||
|
case BYTE -> unpackByteBacked(uintOffset, size, "ivec" + size, e -> e.cast("int"));
|
||||||
|
case SHORT -> unpackShortBacked(uintOffset, size, "ivec" + size, e -> e.cast("int"));
|
||||||
|
case INT -> unpack(uintOffset, size, "ivec" + size);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackUnsignedVector(int uintOffset, UnsignedIntegerRepr unsignedIntegerRepr, int size) {
|
||||||
|
return switch (unsignedIntegerRepr) {
|
||||||
|
case UNSIGNED_BYTE -> unpackByteBacked(uintOffset, size, "uvec" + size, Function.identity());
|
||||||
|
case UNSIGNED_SHORT -> unpackShortBacked(uintOffset, size, "uvec" + size, Function.identity());
|
||||||
|
case UNSIGNED_INT -> unpack(uintOffset, size, "uvec" + size);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackFloatVector(int uintOffset, FloatRepr floatRepr, int size) {
|
||||||
|
return switch (floatRepr) {
|
||||||
|
case NORMALIZED_BYTE -> unpackByteBacked(uintOffset, size, "vec" + size, e -> e.div(127).clamp(-1, 1));
|
||||||
|
case NORMALIZED_UNSIGNED_BYTE -> unpackByteBacked(uintOffset, size, "vec" + size, e -> e.div(255));
|
||||||
|
case NORMALIZED_SHORT -> unpackShortBacked(uintOffset, size, "vec" + size, e -> e.div(32727).clamp(-1, 1));
|
||||||
|
case NORMALIZED_UNSIGNED_SHORT -> unpackShortBacked(uintOffset, size, "vec" + size, e -> e.div(65535));
|
||||||
|
case NORMALIZED_INT -> unpack(uintOffset, size, "vec" + size, e -> e.div(2147483647f)
|
||||||
|
.clamp(-1, 1));
|
||||||
|
case NORMALIZED_UNSIGNED_INT ->
|
||||||
|
unpack(uintOffset, size, "vec" + size, e -> e.div(4294967295f));
|
||||||
|
case BYTE -> unpackByteBacked(uintOffset, size, "vec" + size, e -> e.cast("int")
|
||||||
|
.cast("float"));
|
||||||
|
case UNSIGNED_BYTE -> unpackByteBacked(uintOffset, size, "vec" + size, e -> e.cast("float"));
|
||||||
|
case SHORT -> unpackShortBacked(uintOffset, size, "vec" + size, e -> e.cast("int")
|
||||||
|
.cast("float"));
|
||||||
|
case UNSIGNED_SHORT -> unpackShortBacked(uintOffset, size, "vec" + size, e -> e.cast("float"));
|
||||||
|
case INT -> unpack(uintOffset, size, "vec" + size, e -> e.cast("float"));
|
||||||
|
case UNSIGNED_INT -> unpack(uintOffset, size, "vec" + size, e -> e.cast("int").cast("float"));
|
||||||
|
case FLOAT -> unpack(uintOffset, size, "vec" + size, e -> e.callFunction("uintBitsToFloat"));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackByteBacked(int uintOffset, int size, String outType, Function<GlslExpr, GlslExpr> perElement) {
|
||||||
|
List<GlslExpr> args = new ArrayList<>();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
int bitPos = i * 8;
|
||||||
|
var element = access(uintOffset)
|
||||||
|
.and(0xFF << bitPos)
|
||||||
|
.rsh(bitPos);
|
||||||
|
args.add(perElement.apply(element));
|
||||||
|
}
|
||||||
|
return GlslExpr.call(outType, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackShortBacked(int uintOffset, int size, String outType, Function<GlslExpr, GlslExpr> perElement) {
|
||||||
|
List<GlslExpr> args = new ArrayList<>();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
int bitPos = (i % 2) * 16;
|
||||||
|
int wordOffset = i / 2;
|
||||||
|
var element = access(uintOffset + wordOffset)
|
||||||
|
.and(0xFFFF << bitPos)
|
||||||
|
.rsh(bitPos);
|
||||||
|
args.add(perElement.apply(element));
|
||||||
|
}
|
||||||
|
return GlslExpr.call(outType, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpack(int uintOffset, int size, String outType) {
|
||||||
|
return unpack(uintOffset, size, outType, Function.identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpack(int uintOffset, int size, String outType, Function<GlslExpr, GlslExpr> perElement) {
|
||||||
|
List<GlslExpr> args = new ArrayList<>();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
args.add(access(uintOffset + i)
|
||||||
|
.transform(perElement));
|
||||||
|
}
|
||||||
|
return GlslExpr.call(outType, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GlslExpr unpackMatrix(int uintOffset, MatrixElementType matrix) {
|
||||||
|
var repr = matrix.repr();
|
||||||
|
|
||||||
|
int rows = matrix.rows();
|
||||||
|
int columns = matrix.columns();
|
||||||
|
|
||||||
|
int columnWordSize = MoreMath.ceilingDiv(rows * repr.byteSize(), 4);
|
||||||
|
|
||||||
|
List<GlslExpr> args = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < columns; i++) {
|
||||||
|
args.add(unpackFloatVector(uintOffset + i * columnWordSize, repr, rows));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GlslExpr.call("mat" + columns + "x" + rows, args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,9 @@ package com.jozufozu.flywheel.backend.engine.instancing;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.InternalVertex;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
||||||
import com.jozufozu.flywheel.backend.engine.MeshPool;
|
import com.jozufozu.flywheel.backend.engine.MeshPool;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.TextureBuffer;
|
||||||
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
||||||
|
|
||||||
public class DrawCall {
|
public class DrawCall {
|
||||||
|
@ -31,12 +31,12 @@ public class DrawCall {
|
||||||
return deleted;
|
return deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render() {
|
public void render(TextureBuffer buffer) {
|
||||||
if (mesh.invalid()) {
|
if (mesh.invalid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
instancer.bindIfNeeded(vao, InternalVertex.ATTRIBUTE_COUNT);
|
instancer.bind(buffer);
|
||||||
mesh.setup(vao);
|
mesh.setup(vao);
|
||||||
|
|
||||||
vao.bindForDraw();
|
vao.bindForDraw();
|
||||||
|
@ -44,7 +44,7 @@ public class DrawCall {
|
||||||
mesh.draw(instancer.instanceCount());
|
mesh.draw(instancer.instanceCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderOne(InstanceHandleImpl impl) {
|
public void renderOne(TextureBuffer buffer, InstanceHandleImpl impl) {
|
||||||
if (mesh.invalid()) {
|
if (mesh.invalid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ public class DrawCall {
|
||||||
|
|
||||||
var vao = lazyScratchVao();
|
var vao = lazyScratchVao();
|
||||||
|
|
||||||
instancer.bindRaw(vao, InternalVertex.ATTRIBUTE_COUNT, impl.index);
|
instancer.bind(buffer);
|
||||||
mesh.setup(vao);
|
mesh.setup(vao);
|
||||||
|
|
||||||
vao.bindForDraw();
|
vao.bindForDraw();
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.instancing;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.model.IndexSequence;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.Buffer;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage;
|
|
||||||
import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
|
|
||||||
import com.jozufozu.flywheel.lib.model.QuadIndexSequence;
|
|
||||||
import com.mojang.blaze3d.platform.GlStateManager;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
|
|
||||||
|
|
||||||
public class EboCache {
|
|
||||||
private final List<Entry> quads = new ArrayList<>();
|
|
||||||
private final Object2ReferenceMap<Key, Entry> others = new Object2ReferenceOpenHashMap<>();
|
|
||||||
|
|
||||||
public void invalidate() {
|
|
||||||
quads.forEach(Entry::delete);
|
|
||||||
others.values()
|
|
||||||
.forEach(Entry::delete);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int get(IndexSequence indexSequence, int indexCount) {
|
|
||||||
if (indexSequence == QuadIndexSequence.INSTANCE) {
|
|
||||||
return getQuads(indexCount);
|
|
||||||
} else {
|
|
||||||
return others.computeIfAbsent(new Key(indexSequence, indexCount), Key::create).ebo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getQuads(int indexCount) {
|
|
||||||
// Use an existing quad EBO if there's one big enough.
|
|
||||||
for (Entry quadEbo : quads) {
|
|
||||||
if (quadEbo.gpuSize >= indexCount * GlNumericType.UINT.byteWidth()) {
|
|
||||||
return quadEbo.ebo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If not, create a new one.
|
|
||||||
var out = Entry.create(QuadIndexSequence.INSTANCE, indexCount);
|
|
||||||
quads.add(out);
|
|
||||||
return out.ebo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private record Key(IndexSequence provider, int indexCount) {
|
|
||||||
private Entry create() {
|
|
||||||
return Entry.create(provider, indexCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private record Entry(int ebo, int gpuSize) {
|
|
||||||
private static Entry create(IndexSequence provider, int indexCount) {
|
|
||||||
int byteSize = indexCount * GlNumericType.UINT.byteWidth();
|
|
||||||
var ebo = Buffer.IMPL.create();
|
|
||||||
|
|
||||||
final long ptr = MemoryUtil.nmemAlloc(byteSize);
|
|
||||||
provider.fill(ptr, indexCount);
|
|
||||||
|
|
||||||
Buffer.IMPL.data(ebo, byteSize, ptr, GlBufferUsage.STATIC_DRAW.glEnum);
|
|
||||||
FlwMemoryTracker._allocGPUMemory(byteSize);
|
|
||||||
|
|
||||||
MemoryUtil.nmemFree(ptr);
|
|
||||||
|
|
||||||
return new Entry(ebo, byteSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void delete() {
|
|
||||||
GlStateManager._glDeleteBuffers(this.ebo);
|
|
||||||
FlwMemoryTracker._freeGPUMemory(this.gpuSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
import com.jozufozu.flywheel.api.context.TextureSource;
|
import com.jozufozu.flywheel.api.context.TextureSource;
|
||||||
|
@ -15,6 +16,8 @@ import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
|
||||||
import com.jozufozu.flywheel.backend.engine.textures.TextureBinder;
|
import com.jozufozu.flywheel.backend.engine.textures.TextureBinder;
|
||||||
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
|
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.TextureBuffer;
|
||||||
import com.jozufozu.flywheel.lib.context.ContextShaders;
|
import com.jozufozu.flywheel.lib.context.ContextShaders;
|
||||||
import com.jozufozu.flywheel.lib.context.Contexts;
|
import com.jozufozu.flywheel.lib.context.Contexts;
|
||||||
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
|
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
|
||||||
|
@ -24,7 +27,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
|
|
||||||
public class InstancedCrumbling {
|
public class InstancedCrumbling {
|
||||||
public static void render(List<Engine.CrumblingBlock> crumblingBlocks, InstancingPrograms programs, TextureSource textureSource) {
|
public static void render(List<Engine.CrumblingBlock> crumblingBlocks, InstancingPrograms programs, TextureSource textureSource, TextureBuffer instanceTexture) {
|
||||||
// Sort draw calls into buckets, so we don't have to do as many shader binds.
|
// Sort draw calls into buckets, so we don't have to do as many shader binds.
|
||||||
var byShaderState = doCrumblingSort(crumblingBlocks);
|
var byShaderState = doCrumblingSort(crumblingBlocks);
|
||||||
|
|
||||||
|
@ -56,7 +59,7 @@ public class InstancedCrumbling {
|
||||||
|
|
||||||
MaterialRenderState.setup(crumblingMaterial);
|
MaterialRenderState.setup(crumblingMaterial);
|
||||||
|
|
||||||
for (Int2ObjectMap.Entry<List<Runnable>> progressEntry : byProgress.int2ObjectEntrySet()) {
|
for (var progressEntry : byProgress.int2ObjectEntrySet()) {
|
||||||
var drawCalls = progressEntry.getValue();
|
var drawCalls = progressEntry.getValue();
|
||||||
|
|
||||||
if (drawCalls.isEmpty()) {
|
if (drawCalls.isEmpty()) {
|
||||||
|
@ -66,7 +69,12 @@ public class InstancedCrumbling {
|
||||||
var context = Contexts.CRUMBLING.get(progressEntry.getIntKey());
|
var context = Contexts.CRUMBLING.get(progressEntry.getIntKey());
|
||||||
context.prepare(crumblingMaterial, program, textureSource);
|
context.prepare(crumblingMaterial, program, textureSource);
|
||||||
|
|
||||||
drawCalls.forEach(Runnable::run);
|
GlTextureUnit.T3.makeActive();
|
||||||
|
program.setSamplerBinding("_flw_instances", 3);
|
||||||
|
|
||||||
|
for (Consumer<TextureBuffer> drawCall : drawCalls) {
|
||||||
|
drawCall.accept(instanceTexture);
|
||||||
|
}
|
||||||
|
|
||||||
TextureBinder.resetTextureBindings();
|
TextureBinder.resetTextureBindings();
|
||||||
}
|
}
|
||||||
|
@ -77,8 +85,8 @@ public class InstancedCrumbling {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<ShaderState, Int2ObjectMap<List<Runnable>>> doCrumblingSort(List<Engine.CrumblingBlock> instances) {
|
private static Map<ShaderState, Int2ObjectMap<List<Consumer<TextureBuffer>>>> doCrumblingSort(List<Engine.CrumblingBlock> instances) {
|
||||||
Map<ShaderState, Int2ObjectMap<List<Runnable>>> out = new HashMap<>();
|
Map<ShaderState, Int2ObjectMap<List<Consumer<TextureBuffer>>>> out = new HashMap<>();
|
||||||
|
|
||||||
for (Engine.CrumblingBlock triple : instances) {
|
for (Engine.CrumblingBlock triple : instances) {
|
||||||
int progress = triple.progress();
|
int progress = triple.progress();
|
||||||
|
@ -101,7 +109,7 @@ public class InstancedCrumbling {
|
||||||
for (DrawCall draw : instancer.drawCalls()) {
|
for (DrawCall draw : instancer.drawCalls()) {
|
||||||
out.computeIfAbsent(draw.shaderState, $ -> new Int2ObjectArrayMap<>())
|
out.computeIfAbsent(draw.shaderState, $ -> new Int2ObjectArrayMap<>())
|
||||||
.computeIfAbsent(progress, $ -> new ArrayList<>())
|
.computeIfAbsent(progress, $ -> new ArrayList<>())
|
||||||
.add(() -> draw.renderOne(impl));
|
.add(buf -> draw.renderOne(buf, impl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.instancing;
|
package com.jozufozu.flywheel.backend.engine.instancing;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -12,18 +10,15 @@ import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.instance.InstanceWriter;
|
import com.jozufozu.flywheel.api.instance.InstanceWriter;
|
||||||
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
||||||
import com.jozufozu.flywheel.backend.engine.LayoutAttributes;
|
import com.jozufozu.flywheel.backend.gl.TextureBuffer;
|
||||||
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage;
|
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage;
|
||||||
|
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||||
|
|
||||||
public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I> {
|
public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I> {
|
||||||
private final List<VertexAttribute> instanceAttributes;
|
|
||||||
private final int instanceStride;
|
private final int instanceStride;
|
||||||
|
|
||||||
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
|
||||||
private final InstanceWriter<I> writer;
|
private final InstanceWriter<I> writer;
|
||||||
@Nullable
|
@Nullable
|
||||||
private GlBuffer vbo;
|
private GlBuffer vbo;
|
||||||
|
@ -33,8 +28,8 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
|
||||||
public InstancedInstancer(InstanceType<I> type, Context context) {
|
public InstancedInstancer(InstanceType<I> type, Context context) {
|
||||||
super(type, context);
|
super(type, context);
|
||||||
var layout = type.layout();
|
var layout = type.layout();
|
||||||
instanceAttributes = LayoutAttributes.attributes(layout);
|
// Align to one texel in the texture buffer
|
||||||
instanceStride = layout.byteSize();
|
instanceStride = MoreMath.align16(layout.byteSize());
|
||||||
writer = type.writer();
|
writer = type.writer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,35 +106,6 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
|
||||||
return capacity > vbo.size();
|
return capacity > vbo.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind this instancer's vbo to the given vao if it hasn't already been bound.
|
|
||||||
* @param vao The vao to bind to.
|
|
||||||
* @param startAttrib The first attribute to bind. This method will bind attributes in the half open range
|
|
||||||
* {@code [startAttrib, startAttrib + instanceFormat.getAttributeCount())}.
|
|
||||||
*/
|
|
||||||
public void bindIfNeeded(GlVertexArray vao, int startAttrib) {
|
|
||||||
if (!boundTo.add(vao)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bindRaw(vao, startAttrib, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind this instancer's vbo to the given vao with the given base instance to calculate the binding offset.
|
|
||||||
* @param vao The vao to bind to.
|
|
||||||
* @param startAttrib The first attribute to bind. This method will bind attributes in the half open range
|
|
||||||
* {@code [startAttrib, startAttrib + instanceFormat.getAttributeCount())}.
|
|
||||||
* @param baseInstance The base instance to calculate the binding offset from.
|
|
||||||
*/
|
|
||||||
public void bindRaw(GlVertexArray vao, int startAttrib, int baseInstance) {
|
|
||||||
long offset = (long) baseInstance * instanceStride;
|
|
||||||
vao.bindVertexBuffer(1, vbo.handle(), offset, instanceStride);
|
|
||||||
vao.setBindingDivisor(1, 1);
|
|
||||||
vao.bindAttributes(1, startAttrib, instanceAttributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
if (vbo == null) {
|
if (vbo == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -159,4 +125,12 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
|
||||||
public List<DrawCall> drawCalls() {
|
public List<DrawCall> drawCalls() {
|
||||||
return drawCalls;
|
return drawCalls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void bind(TextureBuffer buffer) {
|
||||||
|
if (vbo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.bind(vbo.handle());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import com.jozufozu.flywheel.backend.engine.textures.TextureBinder;
|
||||||
import com.jozufozu.flywheel.backend.engine.textures.TextureSourceImpl;
|
import com.jozufozu.flywheel.backend.engine.textures.TextureSourceImpl;
|
||||||
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
|
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.TextureBuffer;
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||||
import com.jozufozu.flywheel.lib.task.Flag;
|
import com.jozufozu.flywheel.lib.task.Flag;
|
||||||
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
||||||
|
@ -28,6 +30,7 @@ import com.jozufozu.flywheel.lib.task.SyncedPlan;
|
||||||
public class InstancingEngine extends AbstractEngine {
|
public class InstancingEngine extends AbstractEngine {
|
||||||
private final InstancingPrograms programs;
|
private final InstancingPrograms programs;
|
||||||
private final TextureSourceImpl textures = new TextureSourceImpl();
|
private final TextureSourceImpl textures = new TextureSourceImpl();
|
||||||
|
private final TextureBuffer instanceTexture = new TextureBuffer();
|
||||||
private final InstancedDrawManager drawManager = new InstancedDrawManager();
|
private final InstancedDrawManager drawManager = new InstancedDrawManager();
|
||||||
private final Flag flushFlag = new NamedFlag("flushed");
|
private final Flag flushFlag = new NamedFlag("flushed");
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
// Need to wait for flush before we can inspect instancer state.
|
// Need to wait for flush before we can inspect instancer state.
|
||||||
executor.syncUntil(flushFlag::isRaised);
|
executor.syncUntil(flushFlag::isRaised);
|
||||||
|
|
||||||
InstancedCrumbling.render(crumblingBlocks, programs, textures);
|
InstancedCrumbling.render(crumblingBlocks, programs, textures, instanceTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -86,6 +89,7 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
public void delete() {
|
public void delete() {
|
||||||
drawManager.delete();
|
drawManager.delete();
|
||||||
programs.release();
|
programs.release();
|
||||||
|
instanceTexture.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void render(InstancedDrawManager.DrawSet drawSet) {
|
private void render(InstancedDrawManager.DrawSet drawSet) {
|
||||||
|
@ -110,8 +114,12 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
context.prepare(material, program, textures);
|
context.prepare(material, program, textures);
|
||||||
MaterialRenderState.setup(material);
|
MaterialRenderState.setup(material);
|
||||||
|
|
||||||
|
GlTextureUnit.T3.makeActive();
|
||||||
|
|
||||||
|
program.setSamplerBinding("_flw_instances", 3);
|
||||||
|
|
||||||
for (var drawCall : drawCalls) {
|
for (var drawCall : drawCalls) {
|
||||||
drawCall.render();
|
drawCall.render(instanceTexture);
|
||||||
}
|
}
|
||||||
TextureBinder.resetTextureBindings();
|
TextureBinder.resetTextureBindings();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,9 @@ public class TextureBinder {
|
||||||
// 0 is reserved for diffuse
|
// 0 is reserved for diffuse
|
||||||
// 1 is overlay
|
// 1 is overlay
|
||||||
// 2 is light
|
// 2 is light
|
||||||
// 3..n are for whatever else the context needs
|
// 3 is the instance buffer
|
||||||
private static final int baseSamplerUnit = 3;
|
// 4..n are for whatever else the context needs
|
||||||
|
private static final int baseSamplerUnit = 4;
|
||||||
private static int nextSamplerUnit = baseSamplerUnit;
|
private static int nextSamplerUnit = baseSamplerUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.jozufozu.flywheel.backend.gl;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL32;
|
||||||
|
|
||||||
|
public class TextureBuffer extends GlObject {
|
||||||
|
public static final int MAX_TEXELS = GL32.glGetInteger(GL32.GL_MAX_TEXTURE_BUFFER_SIZE);
|
||||||
|
public static final int MAX_BYTES = MAX_TEXELS * 16; // 4 channels * 4 bytes
|
||||||
|
|
||||||
|
public TextureBuffer() {
|
||||||
|
handle(GL32.glGenTextures());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(int buffer) {
|
||||||
|
GL32.glBindTexture(GL32.GL_TEXTURE_BUFFER, handle());
|
||||||
|
GL32.glTexBuffer(GL32.GL_TEXTURE_BUFFER, GL32.GL_RGBA32UI, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void deleteInternal(int handle) {
|
||||||
|
GL32.glDeleteTextures(handle);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ void main() {
|
||||||
_flw_uberMaterialVertexIndex = _flw_packedMaterial.x;
|
_flw_uberMaterialVertexIndex = _flw_packedMaterial.x;
|
||||||
_flw_unpackMaterialProperties(_flw_packedMaterial.w, flw_material);
|
_flw_unpackMaterialProperties(_flw_packedMaterial.w, flw_material);
|
||||||
|
|
||||||
FlwInstance instance = _flw_unpackInstance();
|
FlwInstance instance = _flw_unpackInstance(gl_InstanceID);
|
||||||
|
|
||||||
_flw_main(instance, uint(gl_InstanceID));
|
_flw_main(instance, uint(gl_InstanceID));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue