The uintification

- Store packed instances in a uint[] in indirect
  - Split the object buffer into an instance buffer and a model index
buffer
  - Rename StructInstanceComponent to SsboInstanceComponent
  - Expand abstraction in InstanceAssemblerComponent to allow both types
of instance assemblers to reuse per-element unpacking generation
- Separate FlwInstance struct generation into separate component,
InstanceStructComponent
  - Also inline LayoutInterpreter into this class
- Move necessary internal definitions for components from api_impl to
common components_header shaders
- Use the same api_impl shader between pipelines
This commit is contained in:
PepperCode1 2024-03-24 12:36:48 -07:00
parent 84515316a7
commit 277597d6bf
32 changed files with 532 additions and 771 deletions

View file

@ -18,12 +18,16 @@ import com.jozufozu.flywheel.backend.glsl.SourceComponent;
import com.jozufozu.flywheel.backend.glsl.generate.FnSignature;
import com.jozufozu.flywheel.backend.glsl.generate.GlslExpr;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
public final class FlwPrograms {
public static final Logger LOGGER = LoggerFactory.getLogger(Flywheel.ID + "/shaders");
private static final ResourceLocation COMPONENTS_HEADER_VERT = Flywheel.rl("internal/components_header.vert");
private static final ResourceLocation COMPONENTS_HEADER_FRAG = Flywheel.rl("internal/components_header.frag");
private FlwPrograms() {
}
@ -34,21 +38,24 @@ public final class FlwPrograms {
var sources = new ShaderSources(resourceManager);
var stats = new CompilerStats("ubershaders");
var loadChecker = new SourceLoader(sources, stats);
var loader = new SourceLoader(sources, stats);
var vertexMaterialComponent = createVertexMaterialComponent(loadChecker);
var fragmentMaterialComponent = createFragmentMaterialComponent(loadChecker);
var fogComponent = createFogComponent(loadChecker);
var cutoutComponent = createCutoutComponent(loadChecker);
var vertexComponentsHeader = loader.find(COMPONENTS_HEADER_VERT);
var fragmentComponentsHeader = loader.find(COMPONENTS_HEADER_FRAG);
if (stats.errored() || vertexMaterialComponent == null || fragmentMaterialComponent == null || fogComponent == null || cutoutComponent == null) {
var vertexMaterialComponent = createVertexMaterialComponent(loader);
var fragmentMaterialComponent = createFragmentMaterialComponent(loader);
var fogComponent = createFogComponent(loader);
var cutoutComponent = createCutoutComponent(loader);
if (stats.errored() || vertexComponentsHeader == null || fragmentComponentsHeader == null || vertexMaterialComponent == null || fragmentMaterialComponent == null || fogComponent == null || cutoutComponent == null) {
// Probably means the shader sources are missing.
stats.emitErrorLog();
return;
}
List<SourceComponent> vertexComponents = List.of(vertexMaterialComponent);
List<SourceComponent> fragmentComponents = List.of(fragmentMaterialComponent, fogComponent, cutoutComponent);
List<SourceComponent> vertexComponents = List.of(vertexComponentsHeader, vertexMaterialComponent);
List<SourceComponent> fragmentComponents = List.of(fragmentComponentsHeader, fragmentMaterialComponent, fogComponent, cutoutComponent);
var pipelineKeys = createPipelineKeys();
InstancingPrograms.reload(sources, pipelineKeys, vertexComponents, fragmentComponents);
@ -66,27 +73,27 @@ public final class FlwPrograms {
}
@Nullable
private static UberShaderComponent createVertexMaterialComponent(SourceLoader loadChecker) {
private static UberShaderComponent createVertexMaterialComponent(SourceLoader loader) {
return UberShaderComponent.builder(Flywheel.rl("material_vertex"))
.materialSources(ShaderIndices.materialVertex()
.all())
.adapt(FnSignature.ofVoid("flw_materialVertex"))
.switchOn(GlslExpr.variable("_flw_uberMaterialVertexIndex"))
.build(loadChecker);
.build(loader);
}
@Nullable
private static UberShaderComponent createFragmentMaterialComponent(SourceLoader loadChecker) {
private static UberShaderComponent createFragmentMaterialComponent(SourceLoader loader) {
return UberShaderComponent.builder(Flywheel.rl("material_fragment"))
.materialSources(ShaderIndices.materialFragment()
.all())
.adapt(FnSignature.ofVoid("flw_materialFragment"))
.switchOn(GlslExpr.variable("_flw_uberMaterialFragmentIndex"))
.build(loadChecker);
.build(loader);
}
@Nullable
private static UberShaderComponent createFogComponent(SourceLoader loadChecker) {
private static UberShaderComponent createFogComponent(SourceLoader loader) {
return UberShaderComponent.builder(Flywheel.rl("fog"))
.materialSources(ShaderIndices.fog()
.all())
@ -96,11 +103,11 @@ public final class FlwPrograms {
.arg("vec4", "color")
.build(), GlslExpr.variable("color"))
.switchOn(GlslExpr.variable("_flw_uberFogIndex"))
.build(loadChecker);
.build(loader);
}
@Nullable
private static UberShaderComponent createCutoutComponent(SourceLoader loadChecker) {
private static UberShaderComponent createCutoutComponent(SourceLoader loader) {
return UberShaderComponent.builder(Flywheel.rl("cutout"))
.materialSources(ShaderIndices.cutout()
.all())
@ -110,7 +117,7 @@ public final class FlwPrograms {
.arg("vec4", "color")
.build(), GlslExpr.boolLiteral(false))
.switchOn(GlslExpr.variable("_flw_uberCutoutIndex"))
.build(loadChecker);
.build(loader);
}
public static class ResourceReloadListener implements ResourceManagerReloadListener {

View file

@ -8,7 +8,8 @@ import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.backend.compile.component.StructInstanceComponent;
import com.jozufozu.flywheel.backend.compile.component.InstanceStructComponent;
import com.jozufozu.flywheel.backend.compile.component.SsboInstanceComponent;
import com.jozufozu.flywheel.backend.compile.core.CompilationHarness;
import com.jozufozu.flywheel.backend.compile.core.Compile;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
@ -24,7 +25,7 @@ import com.jozufozu.flywheel.lib.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class IndirectPrograms extends AtomicReferenceCounted {
private static final ResourceLocation CULL_SHADER_HEADER = Flywheel.rl("internal/indirect/cull_header.glsl");
private static final ResourceLocation CULL_SHADER_API_IMPL = Flywheel.rl("internal/indirect/cull_api_impl.glsl");
private static final ResourceLocation CULL_SHADER_MAIN = Flywheel.rl("internal/indirect/cull.glsl");
private static final ResourceLocation APPLY_SHADER_MAIN = Flywheel.rl("internal/indirect/apply.glsl");
private static final ResourceLocation SCATTER_SHADER_MAIN = Flywheel.rl("internal/indirect/scatter.glsl");
@ -78,9 +79,10 @@ public class IndirectPrograms extends AtomicReferenceCounted {
.link(CULL.shader(GlslVersion.V460, ShaderType.COMPUTE)
.nameMapper(instanceType -> "culling/" + ResourceUtil.toDebugFileNameNoExtension(instanceType.cullShader()))
.define("_FLW_SUBGROUP_SIZE", GlCompat.SUBGROUP_SIZE)
.withResource(CULL_SHADER_HEADER)
.withComponent(StructInstanceComponent::create)
.withResource(CULL_SHADER_API_IMPL)
.withComponent(InstanceStructComponent::new)
.withResource(InstanceType::cullShader)
.withComponent(SsboInstanceComponent::new)
.withResource(CULL_SHADER_MAIN))
.postLink((key, program) -> {
program.setUniformBlockBinding("_FlwFrameUniforms", Uniforms.FRAME_INDEX);

View file

@ -5,6 +5,7 @@ import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.glsl.GlslVersion;
@ -13,20 +14,16 @@ import com.jozufozu.flywheel.backend.glsl.SourceComponent;
import net.minecraft.resources.ResourceLocation;
public record Pipeline(GlslVersion glslVersion, ResourceLocation vertexMain, ResourceLocation fragmentMain,
ResourceLocation vertexApiImpl, ResourceLocation fragmentApiImpl, InstanceAssembler assembler,
String compilerMarker, Consumer<GlProgram> onLink) {
InstanceAssembler assembler, String compilerMarker, Consumer<GlProgram> onLink) {
@FunctionalInterface
public interface InstanceAssembler {
/**
* Generate the source component necessary to convert a packed {@link InstanceType} into its shader representation.
* Generate the source component necessary to convert a packed {@link Instance} into its shader representation.
*
* @return A source component defining functions that unpack a representation of the given instance type.
*/
SourceComponent assemble(InstanceAssemblerContext context);
}
public record InstanceAssemblerContext(int baseAttribute, InstanceType<?> instanceType) {
SourceComponent assemble(InstanceType<?> instanceType);
}
public static Builder builder() {
@ -41,10 +38,6 @@ public record Pipeline(GlslVersion glslVersion, ResourceLocation vertexMain, Res
@Nullable
private ResourceLocation fragmentMain;
@Nullable
private ResourceLocation vertexApiImpl;
@Nullable
private ResourceLocation fragmentApiImpl;
@Nullable
private InstanceAssembler assembler;
@Nullable
private String compilerMarker;
@ -66,16 +59,6 @@ public record Pipeline(GlslVersion glslVersion, ResourceLocation vertexMain, Res
return this;
}
public Builder vertexApiImpl(ResourceLocation shader) {
this.vertexApiImpl = shader;
return this;
}
public Builder fragmentApiImpl(ResourceLocation shader) {
this.fragmentApiImpl = shader;
return this;
}
public Builder assembler(InstanceAssembler assembler) {
this.assembler = assembler;
return this;
@ -95,12 +78,10 @@ public record Pipeline(GlslVersion glslVersion, ResourceLocation vertexMain, Res
Objects.requireNonNull(glslVersion);
Objects.requireNonNull(vertexMain);
Objects.requireNonNull(fragmentMain);
Objects.requireNonNull(vertexApiImpl);
Objects.requireNonNull(fragmentApiImpl);
Objects.requireNonNull(assembler);
Objects.requireNonNull(compilerMarker);
Objects.requireNonNull(onLink);
return new Pipeline(glslVersion, vertexMain, fragmentMain, vertexApiImpl, fragmentApiImpl, assembler, compilerMarker, onLink);
return new Pipeline(glslVersion, vertexMain, fragmentMain, assembler, compilerMarker, onLink);
}
}
}

View file

@ -2,8 +2,10 @@ package com.jozufozu.flywheel.backend.compile;
import java.util.List;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.InternalVertex;
import com.jozufozu.flywheel.backend.Samplers;
import com.jozufozu.flywheel.backend.compile.component.InstanceStructComponent;
import com.jozufozu.flywheel.backend.compile.core.CompilationHarness;
import com.jozufozu.flywheel.backend.compile.core.Compile;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
@ -13,9 +15,14 @@ import com.jozufozu.flywheel.backend.glsl.ShaderSources;
import com.jozufozu.flywheel.backend.glsl.SourceComponent;
import com.jozufozu.flywheel.lib.util.ResourceUtil;
public class PipelineCompiler {
import net.minecraft.resources.ResourceLocation;
public final class PipelineCompiler {
private static final Compile<PipelineProgramKey> PIPELINE = new Compile<>();
private static final ResourceLocation API_IMPL_VERT = Flywheel.rl("internal/api_impl.vert");
private static final ResourceLocation API_IMPL_FRAG = Flywheel.rl("internal/api_impl.frag");
static CompilationHarness<PipelineProgramKey> create(ShaderSources sources, Pipeline pipeline, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents) {
return PIPELINE.program()
.link(PIPELINE.shader(pipeline.glslVersion(), ShaderType.VERTEX)
@ -29,13 +36,14 @@ public class PipelineCompiler {
})
.onCompile((key, comp) -> key.contextShader()
.onCompile(comp))
.withResource(pipeline.vertexApiImpl())
.withResource(InternalVertex.LAYOUT_SHADER)
.withComponent(key -> pipeline.assembler()
.assemble(new Pipeline.InstanceAssemblerContext(InternalVertex.ATTRIBUTE_COUNT, key.instanceType())))
.withComponents(vertexComponents)
.withResource(API_IMPL_VERT)
.withComponent(key -> new InstanceStructComponent(key.instanceType()))
.withResource(key -> key.instanceType()
.vertexShader())
.withComponents(vertexComponents)
.withResource(InternalVertex.LAYOUT_SHADER)
.withComponent(key -> pipeline.assembler()
.assemble(key.instanceType()))
.withResource(pipeline.vertexMain()))
.link(PIPELINE.shader(pipeline.glslVersion(), ShaderType.FRAGMENT)
.nameMapper(key -> {
@ -46,16 +54,16 @@ public class PipelineCompiler {
.enableExtension("GL_ARB_conservative_depth")
.onCompile((key, comp) -> key.contextShader()
.onCompile(comp))
.withResource(pipeline.fragmentApiImpl())
.withResource(API_IMPL_FRAG)
.withComponents(fragmentComponents)
.withResource(pipeline.fragmentMain()))
.preLink((key, program) -> {
program.bindAttribLocation("_flw_a_pos", 0);
program.bindAttribLocation("_flw_a_color", 1);
program.bindAttribLocation("_flw_a_texCoord", 2);
program.bindAttribLocation("_flw_a_overlay", 3);
program.bindAttribLocation("_flw_a_light", 4);
program.bindAttribLocation("_flw_a_normal", 5);
program.bindAttribLocation("_flw_aPos", 0);
program.bindAttribLocation("_flw_aColor", 1);
program.bindAttribLocation("_flw_aTexCoord", 2);
program.bindAttribLocation("_flw_aOverlay", 3);
program.bindAttribLocation("_flw_aLight", 4);
program.bindAttribLocation("_flw_aNormal", 5);
})
.postLink((key, program) -> {
program.setUniformBlockBinding("_FlwFrameUniforms", Uniforms.FRAME_INDEX);

View file

@ -3,7 +3,7 @@ package com.jozufozu.flywheel.backend.compile;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.Samplers;
import com.jozufozu.flywheel.backend.compile.component.BufferTextureInstanceComponent;
import com.jozufozu.flywheel.backend.compile.component.StructInstanceComponent;
import com.jozufozu.flywheel.backend.compile.component.SsboInstanceComponent;
import com.jozufozu.flywheel.backend.glsl.GlslVersion;
public final class Pipelines {
@ -12,20 +12,20 @@ public final class Pipelines {
.glslVersion(GlslVersion.V330)
.vertexMain(Flywheel.rl("internal/instancing/main.vert"))
.fragmentMain(Flywheel.rl("internal/instancing/main.frag"))
.vertexApiImpl(Flywheel.rl("internal/instancing/api_impl.vert"))
.fragmentApiImpl(Flywheel.rl("internal/instancing/api_impl.frag"))
.assembler(BufferTextureInstanceComponent::create)
.assembler(BufferTextureInstanceComponent::new)
.onLink(program -> program.setSamplerBinding("_flw_instances", Samplers.INSTANCE_BUFFER))
.build();
public static final Pipeline INDIRECT = Pipeline.builder()
.compilerMarker("indirect")
.glslVersion(GlslVersion.V460)
.vertexMain(Flywheel.rl("internal/indirect/main.vert"))
.fragmentMain(Flywheel.rl("internal/indirect/main.frag"))
.vertexApiImpl(Flywheel.rl("internal/indirect/api_impl.vert"))
.fragmentApiImpl(Flywheel.rl("internal/indirect/api_impl.frag"))
.assembler(StructInstanceComponent::create)
.assembler(SsboInstanceComponent::new)
.onLink($ -> {
})
.build();
private Pipelines() {
}
}

View file

@ -1,21 +1,10 @@
package com.jozufozu.flywheel.backend.compile.component;
import java.util.ArrayList;
import java.util.EnumMap;
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.ValueRepr;
import com.jozufozu.flywheel.api.layout.VectorElementType;
import com.jozufozu.flywheel.backend.compile.Pipeline;
import com.jozufozu.flywheel.backend.glsl.generate.FnSignature;
import com.jozufozu.flywheel.backend.glsl.generate.GlslBlock;
import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder;
@ -24,66 +13,12 @@ import com.jozufozu.flywheel.backend.glsl.generate.GlslStmt;
import com.jozufozu.flywheel.lib.math.MoreMath;
public class BufferTextureInstanceComponent extends InstanceAssemblerComponent {
private static final String UNPACK_ARG = "index";
private static final String[] SWIZZLE_SELECTORS = { "x", "y", "z", "w" };
// Each function receives a uint expression as the input.
// For byte unpacking, the lowest 8 bits contain the value. For short unpacking, the lowest 16 bits contain the value.
// In both cases, all other bits are 0.
private static final EnumMap<IntegerRepr, Function<GlslExpr, GlslExpr>> INT_UNPACKING_FUNCS = new EnumMap<>(IntegerRepr.class);
private static final EnumMap<UnsignedIntegerRepr, Function<GlslExpr, GlslExpr>> UINT_UNPACKING_FUNCS = new EnumMap<>(UnsignedIntegerRepr.class);
private static final EnumMap<FloatRepr, Function<GlslExpr, GlslExpr>> FLOAT_UNPACKING_FUNCS = new EnumMap<>(FloatRepr.class);
static {
INT_UNPACKING_FUNCS.put(IntegerRepr.BYTE, e -> signExtendByte(e).cast("int"));
INT_UNPACKING_FUNCS.put(IntegerRepr.SHORT, e -> signExtendShort(e).cast("int"));
INT_UNPACKING_FUNCS.put(IntegerRepr.INT, e -> e.cast("int"));
UINT_UNPACKING_FUNCS.put(UnsignedIntegerRepr.UNSIGNED_BYTE, Function.identity());
UINT_UNPACKING_FUNCS.put(UnsignedIntegerRepr.UNSIGNED_SHORT, Function.identity());
UINT_UNPACKING_FUNCS.put(UnsignedIntegerRepr.UNSIGNED_INT, Function.identity());
FLOAT_UNPACKING_FUNCS.put(FloatRepr.BYTE, e -> signExtendByte(e).cast("int").cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_BYTE, e -> signExtendByte(e).cast("int").cast("float").div(127f).clamp(-1, 1));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.UNSIGNED_BYTE, e -> e.cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_UNSIGNED_BYTE, e -> e.cast("float").div(255f));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.SHORT, e -> signExtendShort(e).cast("int").cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_SHORT, e -> signExtendShort(e).cast("int").cast("float").div(32767f).clamp(-1, 1));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.UNSIGNED_SHORT, e -> e.cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_UNSIGNED_SHORT, e -> e.cast("float").div(65535f));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.INT, e -> e.cast("int").cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_INT, e -> e.cast("int").cast("float").div(2147483647f).clamp(-1, 1));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.UNSIGNED_INT, e -> e.cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_UNSIGNED_INT, e -> e.cast("float").div(4294967295f));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.FLOAT, e -> e.callFunction("uintBitsToFloat")); // FIXME: GLSL 330+
}
public BufferTextureInstanceComponent(InstanceType<?> type) {
super(type);
}
public static BufferTextureInstanceComponent create(InstanceType<?> type) {
return new BufferTextureInstanceComponent(type);
}
public static BufferTextureInstanceComponent create(Pipeline.InstanceAssemblerContext ctx) {
return create(ctx.instanceType());
}
// https://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend
// Assumes bits higher than sign bit are zero
private static GlslExpr signExtendByte(GlslExpr e) {
return e.xor(0x80).sub(0x80);
}
private static GlslExpr signExtendShort(GlslExpr e) {
return e.xor(0x8000).sub(0x8000);
}
@Override
public String name() {
return Flywheel.rl("buffer_texture_instance_assembler").toString();
@ -91,16 +26,15 @@ public class BufferTextureInstanceComponent extends InstanceAssemblerComponent {
@Override
protected void generateUnpacking(GlslBuilder builder) {
var block = new GlslBlock();
var fnBody = new GlslBlock();
// TODO: don't require writing to be 16 byte aligned
var texels = MoreMath.ceilingDiv(layout.byteSize(), 16);
block.add(GlslStmt.raw("int base = " + UNPACK_ARG + " * " + texels + ";"));
fnBody.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 + ");"));
fnBody.add(GlslStmt.raw("uvec4 u" + i + " = texelFetch(_flw_instances, base + " + i + ");"));
}
var unpackArgs = new ArrayList<GlslExpr>();
@ -111,7 +45,7 @@ public class BufferTextureInstanceComponent extends InstanceAssemblerComponent {
uintOffset += element.type().byteSize() / 4;
}
block.ret(GlslExpr.call(STRUCT_NAME, unpackArgs));
fnBody.ret(GlslExpr.call(STRUCT_NAME, unpackArgs));
builder._addRaw("uniform usamplerBuffer _flw_instances;");
builder.blankLine();
@ -121,171 +55,11 @@ public class BufferTextureInstanceComponent extends InstanceAssemblerComponent {
.name(UNPACK_FN_NAME)
.arg("int", UNPACK_ARG)
.build())
.body(block);
.body(fnBody);
}
private static GlslExpr unpackElement(Layout.Element element, int uintOffset) {
var type = element.type();
if (type instanceof ScalarElementType scalar) {
return unpackScalar(scalar, uintOffset);
} else if (type instanceof VectorElementType vector) {
return unpackVector(vector, uintOffset);
} else if (type instanceof MatrixElementType matrix) {
return unpackMatrix(matrix, uintOffset);
}
throw new IllegalArgumentException("Unknown type " + type);
}
private static GlslExpr unpackScalar(ScalarElementType type, int uintOffset) {
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);
}
}
private static GlslExpr unpackVector(VectorElementType type, int uintOffset) {
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);
}
}
private static GlslExpr unpackMatrix(MatrixElementType type, int uintOffset) {
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;
int size = rows * columns;
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);
}
}
private static boolean isByteBacked(ValueRepr repr) {
return repr.byteSize() == Byte.BYTES;
}
private static boolean isShortBacked(ValueRepr repr) {
return repr.byteSize() == Short.BYTES;
}
private static GlslExpr unpackByteBackedScalar(int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
GlslExpr e;
if (BIG_ENDIAN) {
e = access(uintOffset)
.rsh(24)
.and(0xFF);
} else {
e = access(uintOffset)
.and(0xFF);
}
return perElement.apply(e);
}
private static GlslExpr unpackShortBackedScalar(int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
GlslExpr e;
if (BIG_ENDIAN) {
e = access(uintOffset)
.rsh(16)
.and(0xFFFF);
} else {
e = access(uintOffset)
.and(0xFFFF);
}
return perElement.apply(e);
}
private static GlslExpr unpackIntBackedScalar(int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
return perElement.apply(access(uintOffset));
}
private static GlslExpr unpackByteBackedVector(String outType, int size, int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
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));
}
return GlslExpr.call(outType, args);
}
private static GlslExpr unpackShortBackedVector(String outType, int size, int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
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));
}
return GlslExpr.call(outType, args);
}
private static GlslExpr unpackIntBackedVector(String outType, int size, int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
List<GlslExpr> args = new ArrayList<>();
for (int i = 0; i < size; i++) {
args.add(perElement.apply(access(uintOffset + i)));
}
return GlslExpr.call(outType, args);
}
private static GlslExpr access(int uintOffset) {
@Override
protected GlslExpr access(int uintOffset) {
return GlslExpr.variable("u" + (uintOffset >> 2))
.swizzle(SWIZZLE_SELECTORS[uintOffset & 3]);
}

View file

@ -1,20 +1,66 @@
package com.jozufozu.flywheel.backend.compile.component;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.function.Function;
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.backend.compile.LayoutInterpreter;
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.backend.glsl.generate.GlslExpr;
public abstract class InstanceAssemblerComponent implements SourceComponent {
protected static final String STRUCT_NAME = "FlwInstance";
protected static final String UNPACK_FN_NAME = "_flw_unpackInstance";
protected static final String UNPACK_ARG = "index";
protected static final boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
private static final boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
// Each function receives a uint expression as the input.
// For byte unpacking, the lowest 8 bits contain the value. For short unpacking, the lowest 16 bits contain the value.
// In both cases, all other bits are 0.
private static final EnumMap<IntegerRepr, Function<GlslExpr, GlslExpr>> INT_UNPACKING_FUNCS = new EnumMap<>(IntegerRepr.class);
private static final EnumMap<UnsignedIntegerRepr, Function<GlslExpr, GlslExpr>> UINT_UNPACKING_FUNCS = new EnumMap<>(UnsignedIntegerRepr.class);
private static final EnumMap<FloatRepr, Function<GlslExpr, GlslExpr>> FLOAT_UNPACKING_FUNCS = new EnumMap<>(FloatRepr.class);
static {
INT_UNPACKING_FUNCS.put(IntegerRepr.BYTE, e -> signExtendByte(e).cast("int"));
INT_UNPACKING_FUNCS.put(IntegerRepr.SHORT, e -> signExtendShort(e).cast("int"));
INT_UNPACKING_FUNCS.put(IntegerRepr.INT, e -> e.cast("int"));
UINT_UNPACKING_FUNCS.put(UnsignedIntegerRepr.UNSIGNED_BYTE, Function.identity());
UINT_UNPACKING_FUNCS.put(UnsignedIntegerRepr.UNSIGNED_SHORT, Function.identity());
UINT_UNPACKING_FUNCS.put(UnsignedIntegerRepr.UNSIGNED_INT, Function.identity());
FLOAT_UNPACKING_FUNCS.put(FloatRepr.BYTE, e -> signExtendByte(e).cast("int").cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_BYTE, e -> signExtendByte(e).cast("int").cast("float").div(127f).clamp(-1, 1));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.UNSIGNED_BYTE, e -> e.cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_UNSIGNED_BYTE, e -> e.cast("float").div(255f));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.SHORT, e -> signExtendShort(e).cast("int").cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_SHORT, e -> signExtendShort(e).cast("int").cast("float").div(32767f).clamp(-1, 1));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.UNSIGNED_SHORT, e -> e.cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_UNSIGNED_SHORT, e -> e.cast("float").div(65535f));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.INT, e -> e.cast("int").cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_INT, e -> e.cast("int").cast("float").div(2147483647f).clamp(-1, 1));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.UNSIGNED_INT, e -> e.cast("float"));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.NORMALIZED_UNSIGNED_INT, e -> e.cast("float").div(4294967295f));
FLOAT_UNPACKING_FUNCS.put(FloatRepr.FLOAT, e -> e.callFunction("uintBitsToFloat")); // FIXME: GLSL 330+
}
protected final Layout layout;
@ -22,6 +68,16 @@ public abstract class InstanceAssemblerComponent implements SourceComponent {
layout = type.layout();
}
// https://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend
// Assumes bits higher than sign bit are zero
private static GlslExpr signExtendByte(GlslExpr e) {
return e.xor(0x80).sub(0x80);
}
private static GlslExpr signExtendShort(GlslExpr e) {
return e.xor(0x8000).sub(0x8000);
}
@Override
public Collection<? extends SourceComponent> included() {
return Collections.emptyList();
@ -30,20 +86,173 @@ public abstract class InstanceAssemblerComponent implements SourceComponent {
@Override
public String source() {
var builder = new GlslBuilder();
generateInstanceStruct(builder);
builder.blankLine();
generateUnpacking(builder);
builder.blankLine();
return builder.build();
}
protected 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());
protected abstract void generateUnpacking(GlslBuilder builder);
protected abstract GlslExpr access(int uintOffset);
protected GlslExpr unpackElement(Layout.Element element, int uintOffset) {
var type = element.type();
if (type instanceof ScalarElementType scalar) {
return unpackScalar(scalar, uintOffset);
} else if (type instanceof VectorElementType vector) {
return unpackVector(vector, uintOffset);
} else if (type instanceof MatrixElementType matrix) {
return unpackMatrix(matrix, uintOffset);
}
throw new IllegalArgumentException("Unknown type " + type);
}
private GlslExpr unpackScalar(ScalarElementType type, int uintOffset) {
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);
}
}
protected abstract void generateUnpacking(GlslBuilder builder);
private GlslExpr unpackVector(VectorElementType type, int uintOffset) {
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);
}
}
private GlslExpr unpackMatrix(MatrixElementType type, int uintOffset) {
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;
int size = rows * columns;
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);
}
}
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;
if (BIG_ENDIAN) {
e = access(uintOffset)
.rsh(24)
.and(0xFF);
} else {
e = access(uintOffset)
.and(0xFF);
}
return perElement.apply(e);
}
private GlslExpr unpackShortBackedScalar(int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
GlslExpr e;
if (BIG_ENDIAN) {
e = access(uintOffset)
.rsh(16)
.and(0xFFFF);
} else {
e = access(uintOffset)
.and(0xFFFF);
}
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) {
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));
}
return GlslExpr.call(outType, args);
}
private GlslExpr unpackShortBackedVector(String outType, int size, int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
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));
}
return GlslExpr.call(outType, args);
}
private GlslExpr unpackIntBackedVector(String outType, int size, int uintOffset, Function<GlslExpr, GlslExpr> perElement) {
List<GlslExpr> args = new ArrayList<>();
for (int i = 0; i < size; i++) {
args.add(perElement.apply(access(uintOffset + i)));
}
return GlslExpr.call(outType, args);
}
}

View file

@ -1,31 +1,56 @@
package com.jozufozu.flywheel.backend.compile;
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.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;
public final class LayoutInterpreter {
private LayoutInterpreter() {
public class InstanceStructComponent implements SourceComponent {
private static final String STRUCT_NAME = "FlwInstance";
private final Layout layout;
public InstanceStructComponent(InstanceType<?> type) {
layout = type.layout();
}
public static int attributeCount(ElementType type) {
if (type instanceof ScalarElementType) {
return 1;
} else if (type instanceof VectorElementType) {
return 1;
} else if (type instanceof MatrixElementType matrix) {
return matrix.rows();
@Override
public String name() {
return Flywheel.rl("instance_struct").toString();
}
@Override
public Collection<? extends SourceComponent> included() {
return Collections.emptyList();
}
@Override
public String source() {
var builder = new GlslBuilder();
var instance = builder.struct();
instance.setName(STRUCT_NAME);
for (var element : layout.elements()) {
instance.addField(typeName(element.type()), element.name());
}
throw new IllegalArgumentException("Unknown type " + type);
builder.blankLine();
return builder.build();
}
public static String typeName(ElementType type) {
private static String typeName(ElementType type) {
if (type instanceof ScalarElementType scalar) {
return scalarTypeName(scalar.repr());
} else if (type instanceof VectorElementType vector) {
@ -37,7 +62,7 @@ public final class LayoutInterpreter {
throw new IllegalArgumentException("Unknown type " + type);
}
public static String scalarTypeName(ValueRepr repr) {
private static String scalarTypeName(ValueRepr repr) {
if (repr instanceof IntegerRepr) {
return "int";
} else if (repr instanceof UnsignedIntegerRepr) {
@ -48,7 +73,7 @@ public final class LayoutInterpreter {
throw new IllegalArgumentException("Unknown repr " + repr);
}
public static String vectorTypeName(ValueRepr repr, int size) {
private static String vectorTypeName(ValueRepr repr, int size) {
if (repr instanceof IntegerRepr) {
return "ivec" + size;
} else if (repr instanceof UnsignedIntegerRepr) {
@ -59,7 +84,7 @@ public final class LayoutInterpreter {
throw new IllegalArgumentException("Unknown repr " + repr);
}
public static String matrixTypeName(MatrixElementType matrix) {
private static String matrixTypeName(MatrixElementType matrix) {
return "mat" + matrix.columns() + "x" + matrix.rows();
}
}

View file

@ -0,0 +1,65 @@
package com.jozufozu.flywheel.backend.compile.component;
import java.util.ArrayList;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.api.layout.Layout;
import com.jozufozu.flywheel.backend.engine.indirect.IndirectBuffers;
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;
public class SsboInstanceComponent extends InstanceAssemblerComponent {
public SsboInstanceComponent(InstanceType<?> type) {
super(type);
}
@Override
public String name() {
return Flywheel.rl("ssbo_instance_assembler").toString();
}
@Override
protected void generateUnpacking(GlslBuilder builder) {
var fnBody = new GlslBlock();
var uintCount = layout.byteSize() / 4;
fnBody.add(GlslStmt.raw("uint base = " + UNPACK_ARG + " * " + uintCount + "u;"));
for (int i = 0; i < uintCount; i++) {
// Retrieve all the uints for the given instance ahead of time to simplify the unpacking generators.
fnBody.add(GlslStmt.raw("uint u" + i + " = _flw_instances[base + " + i + "u];"));
}
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;
}
fnBody.ret(GlslExpr.call(STRUCT_NAME, unpackArgs));
builder._addRaw("layout(std430, binding = " + IndirectBuffers.INSTANCE_INDEX + ") restrict readonly buffer InstanceBuffer {\n"
+ " uint _flw_instances[];\n"
+ "};");
builder.blankLine();
builder.function()
.signature(FnSignature.create()
.returnType(STRUCT_NAME)
.name(UNPACK_FN_NAME)
.arg("uint", UNPACK_ARG)
.build())
.body(fnBody);
}
@Override
protected GlslExpr access(int uintOffset) {
return GlslExpr.variable("u" + uintOffset);
}
}

View file

@ -1,308 +0,0 @@
package com.jozufozu.flywheel.backend.compile.component;
import java.util.ArrayList;
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.Pipeline;
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.GlslStruct;
// TODO: Use a uvec4[] instead of a FlwPackedInstance[] in the SSBO to store data and reuse unpacking code from BufferTextureInstanceComponent.
// Unpacking should be moved from BufferTextureInstanceComponent to InstanceAssemblerComponent and this class should be renamed to SsboInstanceComponent. Further abstraction may be possible.
// Currently, some of the unpacking code generated by this class is incorrect. It is correct in BufferTextureInstanceComponent.
public class StructInstanceComponent extends InstanceAssemblerComponent {
private static final String UNPACK_ARG = "p";
private static final GlslExpr.Variable UNPACKING_VARIABLE = GlslExpr.variable(UNPACK_ARG);
private static final String PACKED_STRUCT_NAME = "FlwPackedInstance";
public StructInstanceComponent(InstanceType<?> type) {
super(type);
}
public static StructInstanceComponent create(InstanceType<?> type) {
return new StructInstanceComponent(type);
}
public static StructInstanceComponent create(Pipeline.InstanceAssemblerContext ctx) {
return create(ctx.instanceType());
}
@Override
public String name() {
return Flywheel.rl("struct_instance_assembler").toString();
}
@Override
protected void generateUnpacking(GlslBuilder builder) {
var packed = builder.struct();
packed.setName(PACKED_STRUCT_NAME);
var unpackArgs = new ArrayList<GlslExpr>();
for (Layout.Element element : layout.elements()) {
unpackArgs.add(unpackElement(element, packed));
}
var block = new GlslBlock();
block.ret(GlslExpr.call(STRUCT_NAME, unpackArgs));
builder.blankLine();
builder.function()
.signature(FnSignature.create()
.returnType(STRUCT_NAME)
.name(UNPACK_FN_NAME)
.arg(PACKED_STRUCT_NAME, UNPACK_ARG)
.build())
.body(block);
}
private static GlslExpr unpackElement(Layout.Element element, GlslStruct packed) {
// 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(scalar, name, packed);
} else if (type instanceof VectorElementType vector) {
return unpackVector(vector, name, packed);
} else if (type instanceof MatrixElementType matrix) {
return unpackMatrix(matrix, name, packed);
}
throw new IllegalArgumentException("Unknown type " + type);
}
private static GlslExpr unpackScalar(ScalarElementType type, String fieldName, GlslStruct packed) {
var repr = type.repr();
if (repr instanceof IntegerRepr intRepr) {
return unpackIntScalar(intRepr, fieldName, packed);
} else if (repr instanceof UnsignedIntegerRepr uintRepr) {
return unpackUintScalar(uintRepr, fieldName, packed);
} else if (repr instanceof FloatRepr floatRepr) {
return unpackFloatScalar(floatRepr, fieldName, packed);
}
throw new IllegalArgumentException("Unknown repr " + repr);
}
private static GlslExpr unpackIntScalar(IntegerRepr repr, String fieldName, GlslStruct packed) {
return switch (repr) {
case BYTE -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFF)
.cast("int"));
case SHORT -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFFFF)
.cast("int"));
case INT -> unpackScalar("int", fieldName, packed);
};
}
private static GlslExpr unpackUintScalar(UnsignedIntegerRepr repr, String fieldName, GlslStruct packed) {
return switch (repr) {
case UNSIGNED_BYTE -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFF));
case UNSIGNED_SHORT -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFFFF));
case UNSIGNED_INT -> unpackScalar("uint", fieldName, packed);
};
}
private static GlslExpr unpackFloatScalar(FloatRepr repr, String fieldName, GlslStruct packed) {
return switch (repr) {
case BYTE -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFF)
.cast("int")
.cast("float"));
case NORMALIZED_BYTE -> unpackScalar("uint", fieldName, packed, e -> e.callFunction("unpackSnorm4x8")
.swizzle("x"));
case UNSIGNED_BYTE -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFF)
.cast("float"));
case NORMALIZED_UNSIGNED_BYTE ->
unpackScalar("uint", fieldName, packed, e -> e.callFunction("unpackUnorm4x8")
.swizzle("x"));
case SHORT -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFFFF)
.cast("int")
.cast("float"));
case NORMALIZED_SHORT -> unpackScalar("uint", fieldName, packed, e -> e.callFunction("unpackSnorm2x16")
.swizzle("x"));
case UNSIGNED_SHORT -> unpackScalar("uint", fieldName, packed, e -> e.and(0xFFFF)
.cast("float"));
case NORMALIZED_UNSIGNED_SHORT ->
unpackScalar("uint", fieldName, packed, e -> e.callFunction("unpackUnorm2x16")
.swizzle("x"));
case INT -> unpackScalar("int", fieldName, packed, e -> e.cast("float"));
case NORMALIZED_INT -> unpackScalar("int", fieldName, packed, e -> e.div(2147483647f)
.clamp(-1, 1));
case UNSIGNED_INT -> unpackScalar("uint", fieldName, packed, e -> e.cast("float"));
case NORMALIZED_UNSIGNED_INT -> unpackScalar("uint", fieldName, packed, e -> e.div(4294967295f));
case FLOAT -> unpackScalar("float", fieldName, packed);
};
}
private static GlslExpr unpackScalar(String packedType, String fieldName, GlslStruct packed) {
return unpackScalar(packedType, fieldName, packed, Function.identity());
}
private static GlslExpr unpackScalar(String packedType, String fieldName, GlslStruct packed, Function<GlslExpr, GlslExpr> perElement) {
packed.addField(packedType, fieldName);
return perElement.apply(UNPACKING_VARIABLE.access(fieldName));
}
private static GlslExpr unpackVector(VectorElementType type, String fieldName, GlslStruct packed) {
var repr = type.repr();
int size = type.size();
if (repr instanceof IntegerRepr intRepr) {
return unpackIntVector(intRepr, size, fieldName, packed);
} else if (repr instanceof UnsignedIntegerRepr uintRepr) {
return unpackUintVector(uintRepr, size, fieldName, packed);
} else if (repr instanceof FloatRepr floatRepr) {
return unpackFloatVector(floatRepr, size, fieldName, packed);
}
throw new IllegalArgumentException("Unknown repr " + repr);
}
private static GlslExpr unpackIntVector(IntegerRepr repr, int size, String fieldName, GlslStruct packed) {
return switch (repr) {
case BYTE -> unpackByteBackedVector("ivec" + size, size, fieldName, packed, e -> e.cast("int"));
case SHORT -> unpackShortBackedVector("ivec" + size, size, fieldName, packed, e -> e.cast("int"));
case INT -> unpackVector("ivec" + size, size, "int", fieldName, packed);
};
}
private static GlslExpr unpackUintVector(UnsignedIntegerRepr repr, int size, String fieldName, GlslStruct packed) {
return switch (repr) {
case UNSIGNED_BYTE -> unpackByteBackedVector("uvec" + size, size, fieldName, packed, e -> e.cast("uint"));
case UNSIGNED_SHORT -> unpackShortBackedVector("uvec" + size, size, fieldName, packed, e -> e.cast("uint"));
case UNSIGNED_INT -> unpackVector("uvec" + size, size, "uint", fieldName, packed);
};
}
private static GlslExpr unpackFloatVector(FloatRepr repr, int size, String fieldName, GlslStruct packed) {
return switch (repr) {
case BYTE -> unpackByteBackedVector("vec" + size, size, fieldName, packed, e -> e.cast("int")
.cast("float"));
case NORMALIZED_BYTE -> unpackByteBuiltinVector(size, fieldName, packed, "unpackSnorm4x8");
case UNSIGNED_BYTE -> unpackByteBackedVector("vec" + size, size, fieldName, packed, e -> e.cast("float"));
case NORMALIZED_UNSIGNED_BYTE -> unpackByteBuiltinVector(size, fieldName, packed, "unpackUnorm4x8");
case SHORT -> unpackShortBackedVector("vec" + size, size, fieldName, packed, e -> e.cast("int")
.cast("float"));
case NORMALIZED_SHORT -> unpackShortBuiltinVector(size, fieldName, packed, "unpackSnorm2x16");
case UNSIGNED_SHORT -> unpackShortBackedVector("vec" + size, size, fieldName, packed, e -> e.cast("float"));
case NORMALIZED_UNSIGNED_SHORT -> unpackShortBuiltinVector(size, fieldName, packed, "unpackUnorm2x16");
case INT -> unpackVector("vec" + size, size, "int", fieldName, packed, e -> e.cast("float"));
case NORMALIZED_INT -> unpackVector("vec" + size, size, "int", fieldName, packed, e -> e.div(2147483647f)
.clamp(-1, 1));
case UNSIGNED_INT -> unpackVector("vec" + size, size, "float", fieldName, packed, e -> e.cast("float"));
case NORMALIZED_UNSIGNED_INT ->
unpackVector("vec" + size, size, "uint", fieldName, packed, e -> e.div(4294967295f));
case FLOAT -> unpackVector("vec" + size, size, "float", fieldName, packed);
};
}
private static GlslExpr unpackByteBackedVector(String outType, int size, String fieldName, GlslStruct packed, Function<GlslExpr, GlslExpr> perElement) {
packed.addField("uint", fieldName);
List<GlslExpr> args = new ArrayList<>();
for (int i = 0; i < size; i++) {
int bitPos = i * 8;
var element = UNPACKING_VARIABLE.access(fieldName)
.rsh(bitPos)
.and(0xFF);
args.add(perElement.apply(element));
}
return GlslExpr.call(outType, args);
}
private static GlslExpr unpackShortBackedVector(String outType, int size, String fieldName, GlslStruct packed, Function<GlslExpr, GlslExpr> perElement) {
List<GlslExpr> args = new ArrayList<>();
for (int i = 0; i < size; i++) {
int unpackField = i / 2;
int bitPos = (i % 2) * 16;
var name = fieldName + "_" + unpackField;
if (bitPos == 0) {
// First time we're seeing this field, add it to the struct.
packed.addField("uint", name);
}
var element = UNPACKING_VARIABLE.access(name)
.rsh(bitPos)
.and(0xFFFF);
args.add(perElement.apply(element));
}
return GlslExpr.call(outType, args);
}
private static GlslExpr unpackByteBuiltinVector(int size, String fieldName, GlslStruct packed, String func) {
packed.addField("uint", fieldName);
GlslExpr expr = UNPACKING_VARIABLE.access(fieldName)
.callFunction(func);
return switch (size) {
case 2 -> expr.swizzle("xy");
case 3 -> expr.swizzle("xyz");
case 4 -> expr;
default -> throw new IllegalArgumentException("Invalid vector size " + size);
};
}
private static GlslExpr unpackShortBuiltinVector(int size, String fieldName, GlslStruct packed, String func) {
if (size == 2) {
packed.addField("uint", fieldName);
return UNPACKING_VARIABLE.access(fieldName)
.callFunction(func);
} else {
var name0 = fieldName + "_" + 0;
var name1 = fieldName + "_" + 1;
packed.addField("uint", name0);
packed.addField("uint", name1);
GlslExpr xy = UNPACKING_VARIABLE.access(name0)
.callFunction(func);
GlslExpr zw = UNPACKING_VARIABLE.access(name1)
.callFunction(func);
if (size == 3) {
return GlslExpr.call("vec3", List.of(xy.swizzle("xy"), zw.swizzle("x")));
} else {
return GlslExpr.call("vec4", List.of(xy, zw));
}
}
}
private static GlslExpr unpackVector(String outType, int size, String backingType, String fieldName, GlslStruct packed) {
return unpackVector(outType, size, backingType, fieldName, packed, Function.identity());
}
private static GlslExpr unpackVector(String outType, int size, String backingType, String fieldName, GlslStruct packed, Function<GlslExpr, GlslExpr> perElement) {
List<GlslExpr> args = new ArrayList<>();
for (int i = 0; i < size; i++) {
var name = fieldName + "_" + i;
packed.addField(backingType, name);
args.add(perElement.apply(UNPACKING_VARIABLE.access(name)));
}
return GlslExpr.call(outType, args);
}
private static GlslExpr unpackMatrix(MatrixElementType type, String name, GlslStruct packed) {
var repr = type.repr();
int rows = type.rows();
int columns = type.columns();
List<GlslExpr> args = new ArrayList<>();
for (int i = 0; i < columns; i++) {
args.add(unpackFloatVector(repr, rows, name + "_" + i, packed));
}
return GlslExpr.call("mat" + columns + "x" + rows, args);
}
}

View file

@ -148,8 +148,8 @@ public class UberShaderComponent implements SourceComponent {
int index = 0;
for (var rl : materialSources) {
SourceFile sourceFile = sources.find(rl);
final int finalIndex = index;
if (sourceFile != null) {
final int finalIndex = index;
var adapterMap = createAdapterMap(adaptedFunctions, fnName -> "_" + fnName + "_" + finalIndex);
transformed.add(new StringSubstitutionComponent(sourceFile, adapterMap));
} else {

View file

@ -43,7 +43,6 @@ public class Compile<K> {
public static class ProgramStitcher<K> implements CompilationHarness.KeyCompiler<K> {
private final Map<ShaderType, ShaderCompiler<K>> compilers = new EnumMap<>(ShaderType.class);
private BiConsumer<K, GlProgram> postLink = (k, p) -> {
};
private BiConsumer<K, GlProgram> preLink = (k, p) -> {
};

View file

@ -11,7 +11,7 @@ import com.jozufozu.flywheel.lib.memory.MemoryBlock;
public class IndirectBuffers {
// Number of vbos created.
public static final int BUFFER_COUNT = 4;
public static final int BUFFER_COUNT = 5;
public static final long INT_SIZE = Integer.BYTES;
public static final long PTR_SIZE = Pointer.POINTER_SIZE;
@ -22,10 +22,11 @@ public class IndirectBuffers {
public static final long DRAW_COMMAND_STRIDE = 40;
public static final long DRAW_COMMAND_OFFSET = 0;
public static final int OBJECT_INDEX = 0;
public static final int INSTANCE_INDEX = 0;
public static final int TARGET_INDEX = 1;
public static final int MODEL_INDEX = 2;
public static final int DRAW_INDEX = 3;
public static final int MODEL_INDEX_INDEX = 2;
public static final int MODEL_INDEX = 3;
public static final int DRAW_INDEX = 4;
// Offsets to the 3 segments
@ -36,18 +37,20 @@ public class IndirectBuffers {
private static final long BUFFERS_SIZE_BYTES = SIZE_OFFSET + BUFFER_COUNT * PTR_SIZE;
// Offsets to the vbos
private static final long OBJECT_HANDLE_OFFSET = HANDLE_OFFSET;
private static final long INSTANCE_HANDLE_OFFSET = HANDLE_OFFSET;
private static final long TARGET_HANDLE_OFFSET = INT_SIZE;
private static final long MODEL_HANDLE_OFFSET = INT_SIZE * 2;
private static final long DRAW_HANDLE_OFFSET = INT_SIZE * 3;
private static final long MODEL_INDEX_HANDLE_OFFSET = INT_SIZE * 2;
private static final long MODEL_HANDLE_OFFSET = INT_SIZE * 3;
private static final long DRAW_HANDLE_OFFSET = INT_SIZE * 4;
// Offsets to the sizes
private static final long OBJECT_SIZE_OFFSET = SIZE_OFFSET;
private static final long INSTANCE_SIZE_OFFSET = SIZE_OFFSET;
private static final long TARGET_SIZE_OFFSET = SIZE_OFFSET + PTR_SIZE;
private static final long MODEL_SIZE_OFFSET = SIZE_OFFSET + PTR_SIZE * 2;
private static final long DRAW_SIZE_OFFSET = SIZE_OFFSET + PTR_SIZE * 3;
private static final long MODEL_INDEX_SIZE_OFFSET = SIZE_OFFSET + PTR_SIZE * 2;
private static final long MODEL_SIZE_OFFSET = SIZE_OFFSET + PTR_SIZE * 3;
private static final long DRAW_SIZE_OFFSET = SIZE_OFFSET + PTR_SIZE * 4;
private static final float OBJECT_GROWTH_FACTOR = 1.25f;
private static final float INSTANCE_GROWTH_FACTOR = 1.25f;
private static final float MODEL_GROWTH_FACTOR = 2f;
private static final float DRAW_GROWTH_FACTOR = 2f;
@ -61,39 +64,44 @@ public class IndirectBuffers {
* {@code sizes}: an array of {@link IndirectBuffers#PTR_SIZE} byte lengths of the buffers.
* <br>
* Each segment stores {@link IndirectBuffers#BUFFER_COUNT} elements,
* one for the object buffer, target buffer, model buffer, and draw buffer.
* one for the instance buffer, target buffer, model index buffer, model buffer, and draw buffer.
*/
private final MemoryBlock multiBindBlock;
private final long objectStride;
public final ResizableStorageArray object;
private final long instanceStride;
public final ResizableStorageArray instance;
public final ResizableStorageArray target;
public final ResizableStorageArray modelIndex;
public final ResizableStorageArray model;
public final ResizableStorageArray draw;
IndirectBuffers(long objectStride) {
this.objectStride = objectStride;
IndirectBuffers(long instanceStride) {
this.instanceStride = instanceStride;
this.multiBindBlock = MemoryBlock.calloc(BUFFERS_SIZE_BYTES, 1);
object = new ResizableStorageArray(objectStride, OBJECT_GROWTH_FACTOR);
target = new ResizableStorageArray(INT_SIZE, OBJECT_GROWTH_FACTOR);
instance = new ResizableStorageArray(instanceStride, INSTANCE_GROWTH_FACTOR);
target = new ResizableStorageArray(INT_SIZE, INSTANCE_GROWTH_FACTOR);
modelIndex = new ResizableStorageArray(INT_SIZE, INSTANCE_GROWTH_FACTOR);
model = new ResizableStorageArray(MODEL_STRIDE, MODEL_GROWTH_FACTOR);
draw = new ResizableStorageArray(DRAW_COMMAND_STRIDE, DRAW_GROWTH_FACTOR);
}
void updateCounts(int objectCount, int modelCount, int drawCount) {
object.ensureCapacity(objectCount);
target.ensureCapacity(objectCount);
void updateCounts(int instanceCount, int modelCount, int drawCount) {
instance.ensureCapacity(instanceCount);
target.ensureCapacity(instanceCount);
modelIndex.ensureCapacity(instanceCount);
model.ensureCapacity(modelCount);
draw.ensureCapacity(drawCount);
final long ptr = multiBindBlock.ptr();
MemoryUtil.memPutInt(ptr + OBJECT_HANDLE_OFFSET, object.handle());
MemoryUtil.memPutInt(ptr + INSTANCE_HANDLE_OFFSET, instance.handle());
MemoryUtil.memPutInt(ptr + TARGET_HANDLE_OFFSET, target.handle());
MemoryUtil.memPutInt(ptr + MODEL_INDEX_HANDLE_OFFSET, modelIndex.handle());
MemoryUtil.memPutInt(ptr + MODEL_HANDLE_OFFSET, model.handle());
MemoryUtil.memPutInt(ptr + DRAW_HANDLE_OFFSET, draw.handle());
MemoryUtil.memPutAddress(ptr + OBJECT_SIZE_OFFSET, objectStride * objectCount);
MemoryUtil.memPutAddress(ptr + TARGET_SIZE_OFFSET, INT_SIZE * objectCount);
MemoryUtil.memPutAddress(ptr + INSTANCE_SIZE_OFFSET, instanceStride * instanceCount);
MemoryUtil.memPutAddress(ptr + TARGET_SIZE_OFFSET, INT_SIZE * instanceCount);
MemoryUtil.memPutAddress(ptr + MODEL_INDEX_SIZE_OFFSET, INT_SIZE * instanceCount);
MemoryUtil.memPutAddress(ptr + MODEL_SIZE_OFFSET, MODEL_STRIDE * modelCount);
MemoryUtil.memPutAddress(ptr + DRAW_SIZE_OFFSET, DRAW_COMMAND_STRIDE * drawCount);
}
@ -112,16 +120,20 @@ public class IndirectBuffers {
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, IndirectBuffers.BUFFER_COUNT, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
}
/**
* Bind all buffers except the draw command buffer.
*/
public void bindForCrumbling() {
final long ptr = multiBindBlock.ptr();
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, 3, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, 4, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
}
public void delete() {
multiBindBlock.free();
object.delete();
instance.delete();
target.delete();
modelIndex.delete();
model.delete();
draw.delete();
}

View file

@ -39,7 +39,7 @@ public class IndirectCullingGroup<I extends Instance> {
private final InstanceType<I> instanceType;
private final Environment environment;
private final long objectStride;
private final long instanceStride;
private final IndirectBuffers buffers;
private final List<IndirectInstancer<?>> instancers = new ArrayList<>();
private final List<IndirectDraw> indirectDraws = new ArrayList<>();
@ -57,9 +57,9 @@ public class IndirectCullingGroup<I extends Instance> {
IndirectCullingGroup(InstanceType<I> instanceType, Environment environment, IndirectPrograms programs) {
this.instanceType = instanceType;
this.environment = environment;
objectStride = instanceType.layout()
.byteSize() + IndirectBuffers.INT_SIZE;
buffers = new IndirectBuffers(objectStride);
instanceStride = instanceType.layout()
.byteSize();
buffers = new IndirectBuffers(instanceStride);
this.programs = programs;
cullProgram = programs.getCullingProgram(instanceType);
@ -100,8 +100,8 @@ public class IndirectCullingGroup<I extends Instance> {
buffers.updateCounts(instanceCountThisFrame, instancers.size(), indirectDraws.size());
// Upload only objects that have changed.
uploadObjects(stagingBuffer);
// Upload only instances that have changed.
uploadInstances(stagingBuffer);
// We need to upload the models every frame to reset the instance count.
uploadModels(stagingBuffer);
@ -234,13 +234,9 @@ public class IndirectCullingGroup<I extends Instance> {
}
}
private void uploadObjects(StagingBuffer stagingBuffer) {
long pos = 0;
private void uploadInstances(StagingBuffer stagingBuffer) {
for (var model : instancers) {
var instanceCount = model.instanceCount();
model.uploadObjects(stagingBuffer, pos, buffers.object.handle());
pos += instanceCount * objectStride;
model.uploadInstances(stagingBuffer, buffers.instance.handle(), buffers.modelIndex.handle());
}
}

View file

@ -14,21 +14,20 @@ import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
import com.jozufozu.flywheel.backend.engine.embed.Environment;
public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I> {
private final long objectStride;
private final long instanceStride;
private final InstanceWriter<I> writer;
private final List<IndirectDraw> associatedDraws = new ArrayList<>();
private final Vector4fc boundingSphere;
public int index;
public int index = -1;
public int baseInstance = -1;
private int lastModelIndex = -1;
private long lastStartPos = -1;
private int lastBaseInstance = -1;
public IndirectInstancer(InstanceType<I> type, Environment environment, Model model) {
super(type, environment);
this.objectStride = type.layout()
.byteSize() + IndirectBuffers.INT_SIZE;
instanceStride = type.layout()
.byteSize();
writer = this.type.writer();
boundingSphere = model.boundingSphere();
}
@ -54,52 +53,65 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
MemoryUtil.memPutFloat(ptr + 20, boundingSphere.w());
}
public void uploadObjects(StagingBuffer stagingBuffer, long startPos, int dstVbo) {
if (shouldUploadAll(startPos)) {
uploadAll(stagingBuffer, startPos, dstVbo);
public void uploadInstances(StagingBuffer stagingBuffer, int instanceVbo, int modelIndexVbo) {
long baseByte = baseInstance * instanceStride;
long modelIndexBaseByte = baseInstance * IndirectBuffers.INT_SIZE;
if (shouldUploadAll()) {
uploadAll(stagingBuffer, baseByte, modelIndexBaseByte, instanceVbo, modelIndexVbo);
} else {
uploadChanged(stagingBuffer, startPos, dstVbo);
uploadChanged(stagingBuffer, baseByte, modelIndexBaseByte, instanceVbo, modelIndexVbo);
}
changed.clear();
lastStartPos = startPos;
lastModelIndex = index;
lastBaseInstance = baseInstance;
}
private boolean shouldUploadAll(long startPos) {
return startPos != lastStartPos || index != lastModelIndex;
private boolean shouldUploadAll() {
return baseInstance != lastBaseInstance || index != lastModelIndex;
}
private void uploadChanged(StagingBuffer stagingBuffer, long baseByte, int dstVbo) {
private void uploadChanged(StagingBuffer stagingBuffer, long baseByte, long modelIndexBaseByte, int instanceVbo, int modelIndexVbo) {
changed.forEachSetSpan((startInclusive, endInclusive) -> {
var totalSize = (endInclusive - startInclusive + 1) * objectStride;
int instanceCount = endInclusive - startInclusive + 1;
long totalSize = instanceCount * instanceStride;
long modelIndexTotalSize = instanceCount * IndirectBuffers.INT_SIZE;
stagingBuffer.enqueueCopy(totalSize, dstVbo, baseByte + startInclusive * objectStride, ptr -> {
stagingBuffer.enqueueCopy(totalSize, instanceVbo, baseByte + startInclusive * instanceStride, ptr -> {
for (int i = startInclusive; i <= endInclusive; i++) {
var instance = instances.get(i);
writeOne(ptr, instance);
ptr += objectStride;
writer.write(ptr, instance);
ptr += instanceStride;
}
});
stagingBuffer.enqueueCopy(modelIndexTotalSize, modelIndexVbo, modelIndexBaseByte + startInclusive * IndirectBuffers.INT_SIZE, ptr -> {
for (int i = startInclusive; i <= endInclusive; i++) {
MemoryUtil.memPutInt(ptr, index);
ptr += IndirectBuffers.INT_SIZE;
}
});
});
}
private void uploadAll(StagingBuffer stagingBuffer, long start, int dstVbo) {
long totalSize = objectStride * instances.size();
private void uploadAll(StagingBuffer stagingBuffer, long baseByte, long modelIndexBaseByte, int instanceVbo, int modelIndexVbo) {
long totalSize = instances.size() * instanceStride;
long modelIndexTotalSize = instances.size() * IndirectBuffers.INT_SIZE;
stagingBuffer.enqueueCopy(totalSize, dstVbo, start, this::uploadAll);
}
stagingBuffer.enqueueCopy(totalSize, instanceVbo, baseByte, ptr -> {
for (I instance : instances) {
writer.write(ptr, instance);
ptr += instanceStride;
}
});
private void uploadAll(long ptr) {
for (I instance : instances) {
writeOne(ptr, instance);
ptr += objectStride;
}
}
private void writeOne(long ptr, I instance) {
MemoryUtil.memPutInt(ptr, index);
writer.write(ptr + IndirectBuffers.INT_SIZE, instance);
stagingBuffer.enqueueCopy(modelIndexTotalSize, modelIndexVbo, modelIndexBaseByte, ptr -> {
for (int i = 0; i < instances.size(); i++) {
MemoryUtil.memPutInt(ptr, index);
ptr += IndirectBuffers.INT_SIZE;
}
});
}
@Override

View file

@ -1,13 +1,13 @@
package com.jozufozu.flywheel.backend.engine.uniform;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.config.DebugMode;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.system.MemoryUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.Level;

View file

@ -1,12 +1,12 @@
package com.jozufozu.flywheel.backend.mixin;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import net.minecraft.client.Options;
@Mixin(Options.class)

View file

@ -1,5 +1,3 @@
#include "flywheel:internal/common_api_impl.frag"
uint _flw_uberMaterialFragmentIndex;
uint _flw_uberFogIndex;
uint _flw_uberCutoutIndex;

View file

@ -0,0 +1 @@
uint _flw_uberMaterialVertexIndex;

View file

@ -1,5 +0,0 @@
#include "flywheel:internal/common_api_impl.frag"
uint _flw_uberMaterialFragmentIndex;
uint _flw_uberFogIndex;
uint _flw_uberCutoutIndex;

View file

@ -1,3 +0,0 @@
#include "flywheel:internal/common_api_impl.vert"
uint _flw_uberMaterialVertexIndex;

View file

@ -1,6 +1,6 @@
#include "flywheel:internal/indirect/buffers.glsl"
#include "flywheel:internal/indirect/model_descriptor.glsl"
#include "flywheel:internal/indirect/buffer_bindings.glsl"
#include "flywheel:internal/indirect/draw_command.glsl"
#include "flywheel:internal/indirect/model_descriptor.glsl"
layout(local_size_x = _FLW_SUBGROUP_SIZE) in;

View file

@ -0,0 +1,5 @@
#define _FLW_INSTANCE_BUFFER_BINDING 0
#define _FLW_TARGET_BUFFER_BINDING 1
#define _FLW_MODEL_INDEX_BUFFER_BINDING 2
#define _FLW_MODEL_BUFFER_BINDING 3
#define _FLW_DRAW_BUFFER_BINDING 4

View file

@ -1,4 +0,0 @@
#define _FLW_OBJECT_BUFFER_BINDING 0
#define _FLW_TARGET_BUFFER_BINDING 1
#define _FLW_MODEL_BUFFER_BINDING 2
#define _FLW_DRAW_BUFFER_BINDING 3

View file

@ -1,21 +1,20 @@
#include "flywheel:internal/indirect/buffers.glsl"
#include "flywheel:internal/indirect/buffer_bindings.glsl"
#include "flywheel:internal/indirect/model_descriptor.glsl"
#include "flywheel:internal/indirect/object.glsl"
#include "flywheel:internal/uniforms/uniforms.glsl"
#include "flywheel:util/matrix.glsl"
layout(local_size_x = _FLW_SUBGROUP_SIZE) in;
layout(std430, binding = _FLW_OBJECT_BUFFER_BINDING) restrict readonly buffer ObjectBuffer {
Object objects[];
layout(std430, binding = _FLW_TARGET_BUFFER_BINDING) restrict writeonly buffer TargetBuffer {
uint _flw_instanceIndices[];
};
layout(std430, binding = _FLW_TARGET_BUFFER_BINDING) restrict writeonly buffer TargetBuffer {
uint objectIndices[];
layout(std430, binding = _FLW_MODEL_INDEX_BUFFER_BINDING) restrict readonly buffer ModelIndexBuffer {
uint _flw_modelIndices[];
};
layout(std430, binding = _FLW_MODEL_BUFFER_BINDING) restrict buffer ModelBuffer {
ModelDescriptor models[];
ModelDescriptor _flw_models[];
};
uniform mat4 _flw_embeddedModel;
@ -34,14 +33,14 @@ bool _flw_testSphere(vec3 center, float radius) {
return all(xyInside) && all(zInside);
}
bool _flw_isVisible(uint objectIndex, uint modelIndex) {
BoundingSphere sphere = models[modelIndex].boundingSphere;
bool _flw_isVisible(uint instanceIndex, uint modelIndex) {
BoundingSphere sphere = _flw_models[modelIndex].boundingSphere;
vec3 center;
float radius;
_flw_unpackBoundingSphere(sphere, center, radius);
FlwInstance instance = _flw_unpackInstance(objects[objectIndex].instance);
FlwInstance instance = _flw_unpackInstance(instanceIndex);
flw_transformBoundingSphere(instance, center, radius);
@ -53,17 +52,17 @@ bool _flw_isVisible(uint objectIndex, uint modelIndex) {
}
void main() {
uint objectIndex = gl_GlobalInvocationID.x;
uint instanceIndex = gl_GlobalInvocationID.x;
if (objectIndex >= objects.length()) {
if (instanceIndex >= _flw_modelIndices.length()) {
return;
}
uint modelIndex = objects[objectIndex].modelIndex;
uint modelIndex = _flw_modelIndices[instanceIndex];
if (_flw_isVisible(objectIndex, modelIndex)) {
uint localIndex = atomicAdd(models[modelIndex].instanceCount, 1);
uint targetIndex = models[modelIndex].baseInstance + localIndex;
objectIndices[targetIndex] = objectIndex;
if (_flw_isVisible(instanceIndex, modelIndex)) {
uint localIndex = atomicAdd(_flw_models[modelIndex].instanceCount, 1);
uint targetIndex = _flw_models[modelIndex].baseInstance + localIndex;
_flw_instanceIndices[targetIndex] = instanceIndex;
}
}

View file

@ -1,19 +1,14 @@
#include "flywheel:internal/common.vert"
#include "flywheel:internal/packed_material.glsl"
#include "flywheel:internal/indirect/buffers.glsl"
#include "flywheel:internal/indirect/buffer_bindings.glsl"
#include "flywheel:internal/indirect/draw_command.glsl"
#include "flywheel:internal/indirect/object.glsl"
layout(std430, binding = _FLW_OBJECT_BUFFER_BINDING) restrict readonly buffer ObjectBuffer {
Object objects[];
};
layout(std430, binding = _FLW_TARGET_BUFFER_BINDING) restrict readonly buffer TargetBuffer {
uint objectIndices[];
uint _flw_instanceIndices[];
};
layout(std430, binding = _FLW_DRAW_BUFFER_BINDING) restrict readonly buffer DrawBuffer {
MeshDrawCommand drawCommands[];
MeshDrawCommand _flw_drawCommands[];
};
uniform uint _flw_baseDraw;
@ -22,15 +17,15 @@ flat out uvec3 _flw_packedMaterial;
void main() {
uint drawIndex = gl_DrawID + _flw_baseDraw;
MeshDrawCommand draw = drawCommands[drawIndex];
MeshDrawCommand draw = _flw_drawCommands[drawIndex];
_flw_uberMaterialVertexIndex = draw.materialVertexIndex;
uint packedMaterialProperties = draw.packedMaterialProperties;
_flw_unpackMaterialProperties(packedMaterialProperties, flw_material);
_flw_packedMaterial = uvec3(draw.materialFragmentIndex, draw.packedFogAndCutout, packedMaterialProperties);
uint objectIndex = objectIndices[gl_BaseInstance + gl_InstanceID];
FlwInstance instance = _flw_unpackInstance(objects[objectIndex].instance);
uint instanceIndex = _flw_instanceIndices[gl_BaseInstance + gl_InstanceID];
FlwInstance instance = _flw_unpackInstance(instanceIndex);
_flw_main(instance, objectIndex);
_flw_main(instance, instanceIndex);
}

View file

@ -1,4 +0,0 @@
struct Object {
uint modelIndex;
FlwPackedInstance instance;
};

View file

@ -1,3 +0,0 @@
#include "flywheel:internal/common_api_impl.vert"
uint _flw_uberMaterialVertexIndex;

View file

@ -1,17 +1,17 @@
in vec3 _flw_a_pos;
in vec4 _flw_a_color;
in vec2 _flw_a_texCoord;
in vec2 _flw_a_overlay;
in vec2 _flw_a_light;
in vec3 _flw_a_normal;
in vec3 _flw_aPos;
in vec4 _flw_aColor;
in vec2 _flw_aTexCoord;
in vec2 _flw_aOverlay;
in vec2 _flw_aLight;
in vec3 _flw_aNormal;
void _flw_layoutVertex() {
flw_vertexPos = vec4(_flw_a_pos, 1.0);
flw_vertexColor = _flw_a_color;
flw_vertexTexCoord = _flw_a_texCoord;
flw_vertexPos = vec4(_flw_aPos, 1.0);
flw_vertexColor = _flw_aColor;
flw_vertexTexCoord = _flw_aTexCoord;
// Integer vertex attributes explode on some drivers for some draw calls, so get the driver
// to cast the int to a float so we can cast it back to an int and reliably get a sane value.
flw_vertexOverlay = ivec2(_flw_a_overlay);
flw_vertexLight = _flw_a_light / 256.0;
flw_vertexNormal = _flw_a_normal;
flw_vertexOverlay = ivec2(_flw_aOverlay);
flw_vertexLight = _flw_aLight / 256.0;
flw_vertexNormal = _flw_aNormal;
}