diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java b/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java index 0fe34b95e..57188b533 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java @@ -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 vertexComponents = List.of(vertexMaterialComponent); - List fragmentComponents = List.of(fragmentMaterialComponent, fogComponent, cutoutComponent); + List vertexComponents = List.of(vertexComponentsHeader, vertexMaterialComponent); + List 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 { diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java b/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java index ae785fe7f..ba7cb6a08 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java @@ -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); diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/Pipeline.java b/src/main/java/com/jozufozu/flywheel/backend/compile/Pipeline.java index 710825fa4..a73962f00 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/Pipeline.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/Pipeline.java @@ -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 onLink) { + InstanceAssembler assembler, String compilerMarker, Consumer 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); } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/PipelineCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/compile/PipelineCompiler.java index 4dad59e8e..3a86d9f87 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/PipelineCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/PipelineCompiler.java @@ -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 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 create(ShaderSources sources, Pipeline pipeline, List vertexComponents, List 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); diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/Pipelines.java b/src/main/java/com/jozufozu/flywheel/backend/compile/Pipelines.java index aba476ec9..3dca51fd7 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/Pipelines.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/Pipelines.java @@ -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() { + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/BufferTextureInstanceComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/BufferTextureInstanceComponent.java index 14f6f073d..6afcc5eec 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/BufferTextureInstanceComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/BufferTextureInstanceComponent.java @@ -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> INT_UNPACKING_FUNCS = new EnumMap<>(IntegerRepr.class); - private static final EnumMap> UINT_UNPACKING_FUNCS = new EnumMap<>(UnsignedIntegerRepr.class); - private static final EnumMap> 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(); @@ -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 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 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 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 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 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 perElement) { - return perElement.apply(access(uintOffset)); - } - - private static GlslExpr unpackByteBackedVector(String outType, int size, int uintOffset, Function perElement) { - List 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 perElement) { - List 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 perElement) { - List 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]); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceAssemblerComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceAssemblerComponent.java index 2a32399ef..478db66e2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceAssemblerComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceAssemblerComponent.java @@ -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> INT_UNPACKING_FUNCS = new EnumMap<>(IntegerRepr.class); + private static final EnumMap> UINT_UNPACKING_FUNCS = new EnumMap<>(UnsignedIntegerRepr.class); + private static final EnumMap> 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 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 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 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 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 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 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 perElement) { + return perElement.apply(access(uintOffset)); + } + + private GlslExpr unpackByteBackedVector(String outType, int size, int uintOffset, Function perElement) { + List 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 perElement) { + List 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 perElement) { + List args = new ArrayList<>(); + for (int i = 0; i < size; i++) { + args.add(perElement.apply(access(uintOffset + i))); + } + return GlslExpr.call(outType, args); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/LayoutInterpreter.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceStructComponent.java similarity index 51% rename from src/main/java/com/jozufozu/flywheel/backend/compile/LayoutInterpreter.java rename to src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceStructComponent.java index c7693405d..4c3992b16 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/LayoutInterpreter.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/InstanceStructComponent.java @@ -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 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(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/SsboInstanceComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/SsboInstanceComponent.java new file mode 100644 index 000000000..8c287c302 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/SsboInstanceComponent.java @@ -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(); + 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); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/StructInstanceComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/StructInstanceComponent.java deleted file mode 100644 index b2177978e..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/StructInstanceComponent.java +++ /dev/null @@ -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(); - - 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 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 perElement) { - packed.addField("uint", fieldName); - List 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 perElement) { - List 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 perElement) { - List 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 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); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/component/UberShaderComponent.java b/src/main/java/com/jozufozu/flywheel/backend/compile/component/UberShaderComponent.java index 7f8ecad89..e93c49af2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/component/UberShaderComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/component/UberShaderComponent.java @@ -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 { diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/core/Compile.java b/src/main/java/com/jozufozu/flywheel/backend/compile/core/Compile.java index 83d723d11..e087e1cdd 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/core/Compile.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/core/Compile.java @@ -43,7 +43,6 @@ public class Compile { public static class ProgramStitcher implements CompilationHarness.KeyCompiler { private final Map> compilers = new EnumMap<>(ShaderType.class); private BiConsumer postLink = (k, p) -> { - }; private BiConsumer preLink = (k, p) -> { }; diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectBuffers.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectBuffers.java index 259b7c1c3..16e886926 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectBuffers.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectBuffers.java @@ -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. *
* 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(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java index d0ffde51e..22ecb60d4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -39,7 +39,7 @@ public class IndirectCullingGroup { private final InstanceType instanceType; private final Environment environment; - private final long objectStride; + private final long instanceStride; private final IndirectBuffers buffers; private final List> instancers = new ArrayList<>(); private final List indirectDraws = new ArrayList<>(); @@ -57,9 +57,9 @@ public class IndirectCullingGroup { IndirectCullingGroup(InstanceType 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 { 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 { } } - 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()); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java index 75ce1ed43..1be0e39f3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java @@ -14,21 +14,20 @@ import com.jozufozu.flywheel.backend.engine.AbstractInstancer; import com.jozufozu.flywheel.backend.engine.embed.Environment; public class IndirectInstancer extends AbstractInstancer { - private final long objectStride; + private final long instanceStride; private final InstanceWriter writer; private final List 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 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 extends AbstractInstancer 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 diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/Uniforms.java b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/Uniforms.java index abb6b751b..988869125 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/Uniforms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/uniform/Uniforms.java @@ -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; diff --git a/src/main/java/com/jozufozu/flywheel/backend/mixin/OptionsMixin.java b/src/main/java/com/jozufozu/flywheel/backend/mixin/OptionsMixin.java index 5d8497c76..9a745a7cb 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/mixin/OptionsMixin.java +++ b/src/main/java/com/jozufozu/flywheel/backend/mixin/OptionsMixin.java @@ -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) diff --git a/src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.frag b/src/main/resources/assets/flywheel/flywheel/internal/api_impl.frag similarity index 100% rename from src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.frag rename to src/main/resources/assets/flywheel/flywheel/internal/api_impl.frag diff --git a/src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.vert b/src/main/resources/assets/flywheel/flywheel/internal/api_impl.vert similarity index 100% rename from src/main/resources/assets/flywheel/flywheel/internal/common_api_impl.vert rename to src/main/resources/assets/flywheel/flywheel/internal/api_impl.vert diff --git a/src/main/resources/assets/flywheel/flywheel/internal/instancing/api_impl.frag b/src/main/resources/assets/flywheel/flywheel/internal/components_header.frag similarity index 63% rename from src/main/resources/assets/flywheel/flywheel/internal/instancing/api_impl.frag rename to src/main/resources/assets/flywheel/flywheel/internal/components_header.frag index cf01e512f..ef45f6d68 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/instancing/api_impl.frag +++ b/src/main/resources/assets/flywheel/flywheel/internal/components_header.frag @@ -1,5 +1,3 @@ -#include "flywheel:internal/common_api_impl.frag" - uint _flw_uberMaterialFragmentIndex; uint _flw_uberFogIndex; uint _flw_uberCutoutIndex; diff --git a/src/main/resources/assets/flywheel/flywheel/internal/components_header.vert b/src/main/resources/assets/flywheel/flywheel/internal/components_header.vert new file mode 100644 index 000000000..a7c1f27f3 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/internal/components_header.vert @@ -0,0 +1 @@ +uint _flw_uberMaterialVertexIndex; diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/api_impl.frag b/src/main/resources/assets/flywheel/flywheel/internal/indirect/api_impl.frag deleted file mode 100644 index cf01e512f..000000000 --- a/src/main/resources/assets/flywheel/flywheel/internal/indirect/api_impl.frag +++ /dev/null @@ -1,5 +0,0 @@ -#include "flywheel:internal/common_api_impl.frag" - -uint _flw_uberMaterialFragmentIndex; -uint _flw_uberFogIndex; -uint _flw_uberCutoutIndex; diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/api_impl.vert b/src/main/resources/assets/flywheel/flywheel/internal/indirect/api_impl.vert deleted file mode 100644 index fafdd66f4..000000000 --- a/src/main/resources/assets/flywheel/flywheel/internal/indirect/api_impl.vert +++ /dev/null @@ -1,3 +0,0 @@ -#include "flywheel:internal/common_api_impl.vert" - -uint _flw_uberMaterialVertexIndex; diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/apply.glsl b/src/main/resources/assets/flywheel/flywheel/internal/indirect/apply.glsl index 4b9f6e2bc..eac037891 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/indirect/apply.glsl +++ b/src/main/resources/assets/flywheel/flywheel/internal/indirect/apply.glsl @@ -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; diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/buffer_bindings.glsl b/src/main/resources/assets/flywheel/flywheel/internal/indirect/buffer_bindings.glsl new file mode 100644 index 000000000..4aa9edc56 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/internal/indirect/buffer_bindings.glsl @@ -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 diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/buffers.glsl b/src/main/resources/assets/flywheel/flywheel/internal/indirect/buffers.glsl deleted file mode 100644 index 0fd422350..000000000 --- a/src/main/resources/assets/flywheel/flywheel/internal/indirect/buffers.glsl +++ /dev/null @@ -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 diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl b/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl index 6b50a358a..20ad771bb 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl +++ b/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl @@ -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; } } diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull_header.glsl b/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull_api_impl.glsl similarity index 100% rename from src/main/resources/assets/flywheel/flywheel/internal/indirect/cull_header.glsl rename to src/main/resources/assets/flywheel/flywheel/internal/indirect/cull_api_impl.glsl diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/main.vert b/src/main/resources/assets/flywheel/flywheel/internal/indirect/main.vert index 68c48ae58..d7ac82016 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/indirect/main.vert +++ b/src/main/resources/assets/flywheel/flywheel/internal/indirect/main.vert @@ -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); } diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/object.glsl b/src/main/resources/assets/flywheel/flywheel/internal/indirect/object.glsl deleted file mode 100644 index 3a880d47f..000000000 --- a/src/main/resources/assets/flywheel/flywheel/internal/indirect/object.glsl +++ /dev/null @@ -1,4 +0,0 @@ -struct Object { - uint modelIndex; - FlwPackedInstance instance; -}; diff --git a/src/main/resources/assets/flywheel/flywheel/internal/instancing/api_impl.vert b/src/main/resources/assets/flywheel/flywheel/internal/instancing/api_impl.vert deleted file mode 100644 index fafdd66f4..000000000 --- a/src/main/resources/assets/flywheel/flywheel/internal/instancing/api_impl.vert +++ /dev/null @@ -1,3 +0,0 @@ -#include "flywheel:internal/common_api_impl.vert" - -uint _flw_uberMaterialVertexIndex; diff --git a/src/main/resources/assets/flywheel/flywheel/internal/vertex_input.vert b/src/main/resources/assets/flywheel/flywheel/internal/vertex_input.vert index d77f66665..423edf2b8 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/vertex_input.vert +++ b/src/main/resources/assets/flywheel/flywheel/internal/vertex_input.vert @@ -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; }