diff --git a/.editorconfig b/.editorconfig index 1eeca6433..2c468ead4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,7 +4,7 @@ root = true [*] indent_style = space indent_size = 4 -continuation_indent_size = 8 +ij_continuation_indent_size = 8 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true @@ -16,6 +16,10 @@ indent_size = 2 [*.java] indent_style = tab +ij_java_blank_lines_before_class_end = 0 +ij_java_blank_lines_after_anonymous_class_header = 0 +ij_java_blank_lines_after_class_header = 0 +ij_java_blank_lines_before_method_body = 0 ij_java_else_on_new_line = false ij_continuation_indent_size = 8 ij_java_class_count_to_use_import_on_demand = 99 diff --git a/build.gradle b/build.gradle index b0d9b40e5..c7f9b0696 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,8 @@ apply plugin: 'eclipse' apply plugin: 'maven-publish' apply plugin: 'org.spongepowered.mixin' +jarJar.enable() + boolean dev = System.getenv('RELEASE') == null || System.getenv('RELEASE').equalsIgnoreCase('false'); ext.buildNumber = System.getenv('BUILD_NUMBER') @@ -45,6 +47,7 @@ minecraft { property 'mixin.env.remapRefMap', 'true' property 'mixin.env.refMapRemappingFile', "${projectDir}/build/createSrgToMcp/output.srg" property 'flw.dumpShaderSource', 'true' + property 'flw.loadRenderDoc', 'true' property 'flw.debugMemorySafety', 'true' arg '-mixin.config=flywheel.mixins.json' @@ -107,11 +110,32 @@ repositories { includeGroup "maven.modrinth" } } + mavenCentral() } +// Fix for loading non-mod libraries in dev-env, used for miniball. +// https://gist.github.com/SizableShrimp/66b22f1b24c255e1491c8d98d3f11f83 +// v--------------------------------------------------------------------v +configurations { + library + implementation.extendsFrom library +} + +minecraft.runs.all { + lazyToken('minecraft_classpath') { + configurations.library.copyRecursive().resolve().collect { it.absolutePath }.join(File.pathSeparator) + } +} +// ^--------------------------------------------------------------------^ + dependencies { minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" + jarJar(group: 'com.dreizak', name: 'miniball', version: "1.0.3") { + jarJar.ranged(it, "[1.0,2.0)") + } + library "com.dreizak:miniball:1.0.3" + // switch to implementation for debugging compileOnly fg.deobf("maven.modrinth:starlight-forge:1.0.2+1.18.2") @@ -133,6 +157,7 @@ mixin { // Workaround for SpongePowered/MixinGradle#38 afterEvaluate { tasks.configureReobfTaskForReobfJar.mustRunAfter(tasks.compileJava) + tasks.configureReobfTaskForReobfJarJar.mustRunAfter(tasks.compileJava) } tasks.withType(JavaCompile).configureEach { @@ -145,6 +170,10 @@ javadoc { options.addStringOption('Xdoclint:none', '-quiet') } +compileJava { + options.compilerArgs = ['-Xdiags:verbose'] +} + jar { manifest { attributes([ @@ -172,7 +201,9 @@ void addLicense(jarTask) { } jar.finalizedBy('reobfJar') +tasks.jarJar.finalizedBy('reobfJarJar') addLicense(jar) +addLicense(tasks.jarJar) publishing { publications { @@ -181,6 +212,7 @@ publishing { from components.java fg.component(it) + jarJar.component(it) } } diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java index b239c2926..27bb51e40 100644 --- a/src/main/java/com/jozufozu/flywheel/Flywheel.java +++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java @@ -7,15 +7,16 @@ import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.RenderWork; import com.jozufozu.flywheel.backend.ShadersModHandler; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; +import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer; import com.jozufozu.flywheel.config.BackendTypeArgument; import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.DebugRender; import com.jozufozu.flywheel.core.PartialModel; import com.jozufozu.flywheel.core.QuadConverter; import com.jozufozu.flywheel.core.StitchedSprite; -import com.jozufozu.flywheel.core.compile.ProgramCompiler; import com.jozufozu.flywheel.core.model.Models; import com.jozufozu.flywheel.event.EntityWorldHandler; import com.jozufozu.flywheel.event.ForgeEvents; @@ -78,7 +79,7 @@ public class Flywheel { forgeEventBus.addListener(FlwCommands::registerClientCommands); forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onReloadRenderers); - forgeEventBus.addListener(ProgramCompiler::onReloadRenderers); + forgeEventBus.addListener(PipelineCompiler::onReloadRenderers); forgeEventBus.addListener(Models::onReloadRenderers); forgeEventBus.addListener(DrawBuffer::onReloadRenderers); @@ -105,6 +106,7 @@ public class Flywheel { // forgeEventBus.addListener(ExampleEffect::onReload); Components.init(); + DebugRender.init(); VanillaInstances.init(); diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ContextShader.java b/src/main/java/com/jozufozu/flywheel/api/context/ContextShader.java similarity index 91% rename from src/main/java/com/jozufozu/flywheel/core/compile/ContextShader.java rename to src/main/java/com/jozufozu/flywheel/api/context/ContextShader.java index 66d8c850f..37e9320ed 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/ContextShader.java +++ b/src/main/java/com/jozufozu/flywheel/api/context/ContextShader.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.core.compile; +package com.jozufozu.flywheel.api.context; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.core.source.FileResolution; diff --git a/src/main/java/com/jozufozu/flywheel/api/instance/Instance.java b/src/main/java/com/jozufozu/flywheel/api/instance/Instance.java index 6cc6e89d8..c52837e45 100644 --- a/src/main/java/com/jozufozu/flywheel/api/instance/Instance.java +++ b/src/main/java/com/jozufozu/flywheel/api/instance/Instance.java @@ -1,9 +1,19 @@ package com.jozufozu.flywheel.api.instance; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; + import net.minecraft.core.BlockPos; public interface Instance { BlockPos getWorldPosition(); + /** + * Check this instance against a frustum.

+ * An implementor may choose to return a constant to skip the frustum check. + * @param frustum A frustum intersection tester for the current frame. + * @return {@code true} if this instance should be considered for updates. + */ + boolean checkFrustum(FrustumIntersection frustum); + boolean isRemoved(); } diff --git a/src/main/java/com/jozufozu/flywheel/api/instancer/InstancedPart.java b/src/main/java/com/jozufozu/flywheel/api/instancer/InstancedPart.java index 50379c54e..e06799ba1 100644 --- a/src/main/java/com/jozufozu/flywheel/api/instancer/InstancedPart.java +++ b/src/main/java/com/jozufozu/flywheel/api/instancer/InstancedPart.java @@ -25,12 +25,9 @@ public abstract class InstancedPart { } public final boolean checkDirtyAndClear() { - if (dirty) { - dirty = false; - return true; - } else { - return false; - } + boolean wasDirty = dirty; + dirty = false; + return wasDirty; } public final boolean isRemoved() { diff --git a/src/main/java/com/jozufozu/flywheel/api/pipeline/PipelineShader.java b/src/main/java/com/jozufozu/flywheel/api/pipeline/PipelineShader.java new file mode 100644 index 000000000..155614856 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/api/pipeline/PipelineShader.java @@ -0,0 +1,8 @@ +package com.jozufozu.flywheel.api.pipeline; + +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.core.source.FileResolution; + +public record PipelineShader(GLSLVersion glslVersion, FileResolution vertex, FileResolution fragment) { + +} diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/StorageBufferWriter.java b/src/main/java/com/jozufozu/flywheel/api/struct/StorageBufferWriter.java new file mode 100644 index 000000000..02eb42db9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/api/struct/StorageBufferWriter.java @@ -0,0 +1,10 @@ +package com.jozufozu.flywheel.api.struct; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; + +public interface StorageBufferWriter { + + void write(final long ptr, final T instance); + + int getAlignment(); +} diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java index ef14279de..0c35a7124 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java @@ -29,6 +29,10 @@ public interface StructType { VertexTransformer getVertexTransformer(); + StorageBufferWriter getStorageBufferWriter(); + + FileResolution getIndirectShader(); + interface VertexTransformer { void transform(MutableVertexList vertexList, S struct, ClientLevel level); } diff --git a/src/main/java/com/jozufozu/flywheel/api/uniform/UniformProvider.java b/src/main/java/com/jozufozu/flywheel/api/uniform/UniformProvider.java index 982be51be..06716f2bc 100644 --- a/src/main/java/com/jozufozu/flywheel/api/uniform/UniformProvider.java +++ b/src/main/java/com/jozufozu/flywheel/api/uniform/UniformProvider.java @@ -7,7 +7,7 @@ public abstract class UniformProvider { protected long ptr; protected Notifier notifier; - public abstract int getSize(); + public abstract int getActualByteSize(); public void updatePtr(long ptr, Notifier notifier) { this.ptr = ptr; diff --git a/src/main/java/com/jozufozu/flywheel/api/vertex/VertexList.java b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexList.java index ec47c17c8..b8edf82a9 100644 --- a/src/main/java/com/jozufozu/flywheel/api/vertex/VertexList.java +++ b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexList.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.api.vertex; +import com.dreizak.miniball.model.PointSet; + /** * A read only view of a vertex buffer. * @@ -9,7 +11,7 @@ package com.jozufozu.flywheel.api.vertex; *

* TODO: more flexible elements? */ -public interface VertexList { +public interface VertexList extends PointSet { float x(int index); float y(int index); @@ -78,4 +80,24 @@ public interface VertexList { default boolean isEmpty() { return getVertexCount() == 0; } + + @Override + default int size() { + return getVertexCount(); + } + + @Override + default int dimension() { + return 3; + } + + @Override + default double coord(int i, int j) { + return switch (j) { + case 0 -> x(i); + case 1 -> y(i); + case 2 -> z(i); + default -> throw new IllegalArgumentException("Invalid dimension: " + j); + }; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index b24df2eb6..b48077615 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -95,6 +95,7 @@ public class Backend { case OFF -> true; case BATCHING -> !usingShaders; case INSTANCING -> !usingShaders && GlCompat.getInstance().instancedArraysSupported(); + case INDIRECT -> !usingShaders && GlCompat.getInstance().supportsIndirect(); }; return canUseEngine ? preferredChoice : BackendType.OFF; diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index d37b19a23..7400c5cfa 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -5,10 +5,10 @@ import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.core.ComponentRegistry; -import com.jozufozu.flywheel.core.compile.ContextShader; -import com.jozufozu.flywheel.core.compile.ProgramCompiler; +import com.jozufozu.flywheel.api.context.ContextShader; +import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; +import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.source.FileResolution; -import com.jozufozu.flywheel.core.source.ShaderLoadingException; import com.jozufozu.flywheel.core.source.ShaderSources; import com.jozufozu.flywheel.core.source.error.ErrorReporter; import com.jozufozu.flywheel.util.StringUtil; @@ -51,8 +51,7 @@ public class Loader implements ResourceManagerReloadListener { FileResolution.run(errorReporter, sources); if (errorReporter.hasErrored()) { - errorReporter.dump(); - throw new ShaderLoadingException("Failed to resolve all source files, see log for details"); + throw errorReporter.dump(); } sources.postResolve(); @@ -69,8 +68,8 @@ public class Loader implements ResourceManagerReloadListener { for (StructType structType : ComponentRegistry.structTypes) { for (VertexType vertexType : ComponentRegistry.vertexTypes) { for (ContextShader contextShader : ComponentRegistry.contextShaders) { - var ctx = new ProgramCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader); - ProgramCompiler.INSTANCE.getProgram(ctx); + var ctx = new PipelineCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader, Components.INSTANCED_ARRAYS); + PipelineCompiler.INSTANCE.getProgram(ctx); } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java index eb128ad69..52ac01c2a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java @@ -26,4 +26,8 @@ public enum GLSLVersion { public String toString() { return Integer.toString(version); } + + public String getVersionLine() { + return "#version " + version + '\n'; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttribute.java b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttribute.java index 18c458643..d18a568d6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttribute.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttribute.java @@ -3,5 +3,18 @@ package com.jozufozu.flywheel.backend.gl.array; public interface VertexAttribute { int getByteWidth(); + /** + * Apply this vertex attribute to the bound vertex array. + * @param offset The byte offset to the first element of the attribute. + * @param i The attribute index. + * @param stride The byte stride between consecutive elements of the attribute. + */ void pointer(long offset, int i, int stride); + + /** + * Use DSA to apply this vertex attribute to the given vertex array. + * @param vaobj The vertex array object to modify. + * @param i The attribute index. + */ + void format(int vaobj, int i); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeF.java b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeF.java index 001e73d06..65cb51b7c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeF.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeF.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.backend.gl.array; import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL45; import com.jozufozu.flywheel.backend.gl.GlNumericType; @@ -22,4 +23,9 @@ public record VertexAttributeF(GlNumericType type, int size, boolean normalized) public void pointer(long offset, int i, int stride) { GL32.glVertexAttribPointer(i, size(), type().getGlEnum(), normalized(), stride, offset); } + + @Override + public void format(int vaobj, int i) { + GL45.glVertexArrayAttribFormat(vaobj, i, size(), type().getGlEnum(), normalized(), 0); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeI.java b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeI.java index d0f0bc903..64934cd3a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeI.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/array/VertexAttributeI.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.backend.gl.array; import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL45; import com.jozufozu.flywheel.backend.gl.GlNumericType; @@ -21,4 +22,9 @@ public record VertexAttributeI(GlNumericType type, int size) implements VertexAt public void pointer(long offset, int i, int stride) { GL32.glVertexAttribIPointer(i, size(), type().getGlEnum(), stride, offset); } + + @Override + public void format(int vaobj, int i) { + GL45.glVertexArrayAttribIFormat(vaobj, i, size(), type().getGlEnum(), 0); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java index 84650d9b2..ea80d4cf5 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java @@ -5,25 +5,19 @@ import static org.lwjgl.opengl.GL20.glGetUniformLocation; import static org.lwjgl.opengl.GL20.glUniform1i; import static org.lwjgl.opengl.GL20.glUniformMatrix4fv; -import java.nio.FloatBuffer; - import org.jetbrains.annotations.NotNull; -import org.lwjgl.system.MemoryStack; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlObject; import com.mojang.blaze3d.shaders.ProgramManager; -import com.mojang.math.Matrix4f; import net.minecraft.resources.ResourceLocation; -public abstract class GlProgram extends GlObject { - private static final FloatBuffer floatBuffer = MemoryStack.stackGet() - .mallocFloat(16); +public class GlProgram extends GlObject { public final ResourceLocation name; - protected GlProgram(ResourceLocation name, int handle) { + public GlProgram(ResourceLocation name, int handle) { this.name = name; setHandle(handle); } @@ -70,11 +64,6 @@ public abstract class GlProgram extends GlObject { return samplerUniform; } - protected static void uploadMatrixUniform(int uniform, Matrix4f mat) { - mat.store(floatBuffer); - glUniformMatrix4fv(uniform, false, floatBuffer); - } - @Override protected void deleteInternal(int handle) { glDeleteProgram(handle); diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java index ca42adc46..f5b106232 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java @@ -1,10 +1,12 @@ package com.jozufozu.flywheel.backend.gl.shader; import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL43; public enum ShaderType { VERTEX("vertex", "VERTEX_SHADER", "vert", GL20.GL_VERTEX_SHADER), FRAGMENT("fragment", "FRAGMENT_SHADER", "frag", GL20.GL_FRAGMENT_SHADER), + COMPUTE("compute", "COMPUTE_SHADER", "glsl", GL43.GL_COMPUTE_SHADER), ; public final String name; diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java index 97a9f6850..8380d111e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java @@ -31,12 +31,15 @@ public class GlCompat { public final InstancedArrays instancedArrays; public final BufferStorage bufferStorage; public final boolean amd; + public final boolean supportsIndirect; private GlCompat() { GLCapabilities caps = GL.createCapabilities(); instancedArrays = getLatest(InstancedArrays.class, caps); bufferStorage = getLatest(BufferStorage.class, caps); + supportsIndirect = true; + amd = _isAmdWindows(); } @@ -108,5 +111,9 @@ public class GlCompat { // vendor string I got was "ATI Technologies Inc." return vendor.contains("ATI") || vendor.contains("AMD"); } + + public boolean supportsIndirect() { + return supportsIndirect; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java index 78bf26d6a..9bbcc3c2e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.instancing; import java.util.ArrayList; import java.util.BitSet; +import java.util.List; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.Instancer; @@ -28,7 +29,7 @@ public abstract class AbstractInstancer implements Inst /** * Copy a data from another Instancer to this. - * + *

* This has the effect of swapping out one model for another. * @param inOther the data associated with a different model. */ @@ -59,6 +60,14 @@ public abstract class AbstractInstancer implements Inst return data.size(); } + public List getRange(int start, int end) { + return data.subList(start, end); + } + + public List getAll() { + return data; + } + protected void removeDeletedInstances() { // Figure out which elements are to be removed. final int oldSize = this.data.size(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 297d9d0e9..319fb9607 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -15,9 +15,8 @@ import com.jozufozu.flywheel.backend.instancing.ratelimit.NonLimiter; import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.light.LightUpdater; -import com.mojang.math.Vector3f; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; -import net.minecraft.client.Camera; import net.minecraft.core.BlockPos; public abstract class InstanceManager { @@ -100,27 +99,26 @@ public abstract class InstanceManager { int dY = pos.getY() - cY; int dZ = pos.getZ() - cZ; - if (tick.shouldUpdate(dX, dY, dZ)) instance.tick(); + if (!tick.shouldUpdate(dX, dY, dZ)) { + return; + } + + instance.tick(); } public void beginFrame(TaskEngine taskEngine, RenderContext context) { frame.tick(); processQueuedAdditions(); - Camera camera = context.camera(); - Vector3f look = camera.getLookVector(); - float lookX = look.x(); - float lookY = look.y(); - float lookZ = look.z(); - // integer camera pos - BlockPos cameraIntPos = camera.getBlockPosition(); + BlockPos cameraIntPos = context.camera().getBlockPosition(); int cX = cameraIntPos.getX(); int cY = cameraIntPos.getY(); int cZ = cameraIntPos.getZ(); + FrustumIntersection culler = context.culler(); var instances = getStorage().getInstancesForUpdate(); - distributeWork(taskEngine, instances, instance -> updateInstance(instance, lookX, lookY, lookZ, cX, cY, cZ)); + distributeWork(taskEngine, instances, instance -> updateInstance(instance, culler, cX, cY, cZ)); } private static void distributeWork(TaskEngine taskEngine, List instances, Consumer action) { @@ -140,7 +138,7 @@ public abstract class InstanceManager { } } - protected void updateInstance(DynamicInstance dyn, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) { + protected void updateInstance(DynamicInstance dyn, FrustumIntersection test, int cX, int cY, int cZ) { if (!dyn.decreaseFramerateWithDistance()) { dyn.beginFrame(); return; @@ -151,15 +149,14 @@ public abstract class InstanceManager { int dY = worldPos.getY() - cY; int dZ = worldPos.getZ() - cZ; - // is it more than 2 blocks behind the camera? - int dist = 2; - float dot = (dX + lookX * dist) * lookX + (dY + lookY * dist) * lookY + (dZ + lookZ * dist) * lookZ; - if (dot < 0) { + if (!frame.shouldUpdate(dX, dY, dZ)) { return; } - if (frame.shouldUpdate(dX, dY, dZ)) + if (dyn.checkFrustum(test)) { dyn.beginFrame(); + } + } public void add(T obj) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index 4eb9af7d8..387cb98bf 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -9,6 +9,7 @@ import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceM import com.jozufozu.flywheel.backend.instancing.effect.Effect; import com.jozufozu.flywheel.backend.instancing.effect.EffectInstanceManager; import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager; +import com.jozufozu.flywheel.backend.instancing.indirect.IndirectEngine; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.RenderContext; @@ -37,6 +38,7 @@ public class InstanceWorld implements AutoCloseable { public static InstanceWorld create(LevelAccessor level) { var engine = switch (Backend.getBackendType()) { + case INDIRECT -> new IndirectEngine(Components.WORLD); case INSTANCING -> new InstancingEngine(Components.WORLD, 100 * 100); case BATCHING -> new BatchingEngine(); case OFF -> throw new IllegalStateException("Cannot create instance world when backend is off."); @@ -133,10 +135,7 @@ public class InstanceWorld implements AutoCloseable { */ public void renderStage(RenderContext context, RenderStage stage) { taskEngine.syncPoint(); - context.pushPose(); - context.translateBack(context.camera().getPosition()); engine.renderStage(taskEngine, context, stage); - context.popPose(); } /** diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/PipelineCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/PipelineCompiler.java new file mode 100644 index 000000000..ee78cc709 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/PipelineCompiler.java @@ -0,0 +1,157 @@ +package com.jozufozu.flywheel.backend.instancing; + +import java.util.List; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.api.context.ContextShader; +import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.api.pipeline.PipelineShader; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.compile.CompileUtil; +import com.jozufozu.flywheel.core.compile.Memoizer; +import com.jozufozu.flywheel.core.compile.ProgramAssembler; +import com.jozufozu.flywheel.core.compile.ShaderCompilationException; +import com.jozufozu.flywheel.core.source.CompilationContext; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.event.ReloadRenderersEvent; + +import net.minecraft.resources.ResourceLocation; + +/** + * A caching compiler. + * + *

+ * This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of + * compiled programs, and will only compile a program if it is not already in the cache. + *

+ *

+ * A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload. + *

+ */ +public class PipelineCompiler extends Memoizer { + + public static final PipelineCompiler INSTANCE = new PipelineCompiler(); + + private final ShaderCompiler shaderCompiler; + + private PipelineCompiler() { + this.shaderCompiler = new ShaderCompiler(); + } + + /** + * Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec. + * + * @param ctx The context of compilation. + * @return A compiled GlProgram. + */ + public GlProgram getProgram(PipelineCompiler.Context ctx) { + return super.get(ctx); + } + + @Override + public void invalidate() { + super.invalidate(); + shaderCompiler.invalidate(); + } + + @Override + protected GlProgram _create(PipelineCompiler.Context ctx) { + + var glslVersion = ctx.pipelineShader() + .glslVersion(); + + var vertex = new ShaderCompiler.Context(glslVersion, ShaderType.VERTEX, ctx.getVertexComponents()); + var fragment = new ShaderCompiler.Context(glslVersion, ShaderType.FRAGMENT, ctx.getFragmentComponents()); + + return new ProgramAssembler(ctx.instanceShader.getFileLoc()) + .attachShader(shaderCompiler.get(vertex)) + .attachShader(shaderCompiler.get(fragment)) + .link() + .build(ctx.contextShader.factory()); + } + + @Override + protected void _destroy(GlProgram value) { + value.delete(); + } + + public static void onReloadRenderers(ReloadRenderersEvent event) { + INSTANCE.invalidate(); + } + + /** + * Represents the entire context of a program's usage. + * + * @param vertexType The vertexType the program should be adapted for. + * @param material The material shader to use. TODO: Flatten materials + * @param instanceShader The instance shader to use. + * @param contextShader The context shader to use. + */ + public record Context(VertexType vertexType, Material material, FileResolution instanceShader, + ContextShader contextShader, PipelineShader pipelineShader) { + + ImmutableList getVertexComponents() { + return ImmutableList.of(vertexType.getLayoutShader().getFile(), instanceShader.getFile(), material.getVertexShader().getFile(), + contextShader.getVertexShader(), pipelineShader.vertex().getFile()); + } + + ImmutableList getFragmentComponents() { + return ImmutableList.of(material.getFragmentShader().getFile(), contextShader.getFragmentShader(), + pipelineShader.fragment().getFile()); + } + } + + /** + * Handles compilation and deletion of vertex shaders. + */ + public static class ShaderCompiler extends Memoizer { + + private ShaderCompiler() { + } + + @Override + protected GlShader _create(Context key) { + StringBuilder finalSource = new StringBuilder(); + + finalSource.append(key.generateHeader()); + finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n"); + finalSource.append("#extension GL_ARB_conservative_depth : enable\n"); + finalSource.append("#extension GL_ARB_enhanced_layouts : enable\n"); + + var ctx = new CompilationContext(); + + var names = ImmutableList.builder(); + for (var file : key.components) { + finalSource.append(file.generateFinalSource(ctx)); + names.add(file.name); + } + + try { + return new GlShader(finalSource.toString(), key.shaderType, names.build()); + } catch (ShaderCompilationException e) { + throw e.withErrorLog(ctx); + } + } + + @Override + protected void _destroy(GlShader value) { + value.delete(); + } + + /** + * @param glslVersion The GLSL version to use. + * @param components A list of shader components to stitch together, in order. + */ + public record Context(GLSLVersion glslVersion, ShaderType shaderType, List components) { + + public String generateHeader() { + return CompileUtil.generateHeader(glslVersion, shaderType); + } + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java index 97fc5737f..61cc12016 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java @@ -19,14 +19,6 @@ public class CPUInstancer extends AbstractInstancer } } - public List getRange(int start, int end) { - return data.subList(start, end); - } - - public List getAll() { - return data; - } - @Override public void notifyDirty() { // noop diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/DrawBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/DrawBuffer.java index 52675cdfb..053b9d52c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/DrawBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/DrawBuffer.java @@ -120,6 +120,10 @@ public class DrawBuffer { } public void free() { + if (memory == null) { + return; + } + memory.free(); memory = null; buffer = null; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/blockentity/BlockEntityInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/blockentity/BlockEntityInstance.java index add54b13b..7689ca9a2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/blockentity/BlockEntityInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/blockentity/BlockEntityInstance.java @@ -14,6 +14,7 @@ import com.jozufozu.flywheel.core.structs.oriented.OrientedPart; import com.jozufozu.flywheel.core.structs.transformed.TransformedPart; import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.ImmutableBox; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.entity.BlockEntity; @@ -90,16 +91,13 @@ public abstract class BlockEntityInstance extends Abstrac return pos; } - protected InstancerFactory getTransformFactory() { - return instancerManager.factory(StructTypes.TRANSFORMED); - } - - protected InstancerFactory getOrientedFactory() { - return instancerManager.factory(StructTypes.ORIENTED); - } - @Override public ImmutableBox getVolume() { return GridAlignedBB.from(pos); } + + @Override + public boolean checkFrustum(FrustumIntersection frustum) { + return frustum.testAab(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java index 4e6926317..352aa832b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java @@ -8,6 +8,7 @@ import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceM import com.jozufozu.flywheel.light.LightListener; import com.jozufozu.flywheel.light.TickingLightListener; import com.jozufozu.flywheel.util.box.GridAlignedBB; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; import com.mojang.math.Vector3f; import net.minecraft.core.BlockPos; @@ -96,4 +97,11 @@ public abstract class EntityInstance extends AbstractInstance public BlockPos getWorldPosition() { return entity.blockPosition(); } + + @Override + public boolean checkFrustum(FrustumIntersection frustum) { + AABB aabb = entity.getBoundingBox(); + return frustum.testAab((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ, + (float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java new file mode 100644 index 000000000..5f664367b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java @@ -0,0 +1,56 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.compile.CompileUtil; +import com.jozufozu.flywheel.core.compile.Memoizer; +import com.jozufozu.flywheel.core.compile.ProgramAssembler; +import com.jozufozu.flywheel.core.compile.ShaderCompilationException; +import com.jozufozu.flywheel.core.source.CompilationContext; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.event.ReloadRenderersEvent; + +public class ComputeCullerCompiler extends Memoizer { + + public static final ComputeCullerCompiler INSTANCE = new ComputeCullerCompiler(); + + private ComputeCullerCompiler() { + } + + @Override + protected GlProgram _create(FileResolution file) { + + var finalSource = new StringBuilder(); + CompilationContext context = new CompilationContext(); + + finalSource.append(CompileUtil.generateHeader(GLSLVersion.V460, ShaderType.COMPUTE)); + finalSource.append(file.getFile() + .generateFinalSource(context)); + + finalSource.append(Components.Pipeline.INDIRECT_CULL.getFile() + .generateFinalSource(context)); + + try { + var shader = new GlShader(finalSource.toString(), ShaderType.COMPUTE, ImmutableList.of(file.getFileLoc())); + + return new ProgramAssembler(file.getFileLoc()).attachShader(shader) + .link() + .build(GlProgram::new); + } catch (ShaderCompilationException e) { + throw e.withErrorLog(context); + } + } + + @Override + protected void _destroy(GlProgram value) { + value.delete(); + } + + public static void invalidateAll(ReloadRenderersEvent ignored) { + INSTANCE.invalidate(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java new file mode 100644 index 000000000..f99a6d799 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java @@ -0,0 +1,223 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import static org.lwjgl.opengl.GL45.glCreateBuffers; +import static org.lwjgl.opengl.GL46.GL_DRAW_INDIRECT_BUFFER; +import static org.lwjgl.opengl.GL46.GL_DYNAMIC_STORAGE_BIT; +import static org.lwjgl.opengl.GL46.GL_MAP_FLUSH_EXPLICIT_BIT; +import static org.lwjgl.opengl.GL46.GL_MAP_PERSISTENT_BIT; +import static org.lwjgl.opengl.GL46.GL_MAP_WRITE_BIT; +import static org.lwjgl.opengl.GL46.GL_SHADER_STORAGE_BUFFER; +import static org.lwjgl.opengl.GL46.glBindBuffer; +import static org.lwjgl.opengl.GL46.glCopyNamedBufferSubData; +import static org.lwjgl.opengl.GL46.glDeleteBuffers; +import static org.lwjgl.opengl.GL46.glFlushMappedNamedBufferRange; +import static org.lwjgl.opengl.GL46.glNamedBufferStorage; +import static org.lwjgl.opengl.GL46.nglBindBuffersRange; +import static org.lwjgl.opengl.GL46.nglCreateBuffers; +import static org.lwjgl.opengl.GL46.nglDeleteBuffers; +import static org.lwjgl.opengl.GL46.nglMapNamedBufferRange; +import static org.lwjgl.opengl.GL46.nglNamedBufferSubData; + +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.Pointer; + +import com.jozufozu.flywheel.backend.memory.FlwMemoryTracker; +import com.jozufozu.flywheel.backend.memory.MemoryBlock; + +public class IndirectBuffers { + public static final int BUFFER_COUNT = 4; + public static final long INT_SIZE = Integer.BYTES; + public static final long PTR_SIZE = Pointer.POINTER_SIZE; + + // DRAW COMMAND + public static final long DRAW_COMMAND_STRIDE = 36; + public static final long DRAW_COMMAND_OFFSET = 0; + + // BITS + private static final int SUB_DATA_BITS = GL_DYNAMIC_STORAGE_BIT; + private static final int PERSISTENT_BITS = GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT; + private static final int MAP_BITS = PERSISTENT_BITS | GL_MAP_FLUSH_EXPLICIT_BIT; + private static final int GPU_ONLY_BITS = 0; + + // OFFSETS + private static final long OFFSET_OFFSET = BUFFER_COUNT * INT_SIZE; + private static final long SIZE_OFFSET = OFFSET_OFFSET + BUFFER_COUNT * PTR_SIZE; + private static final long BUFFERS_SIZE_BYTES = SIZE_OFFSET + BUFFER_COUNT * PTR_SIZE; + + private static final long OBJECT_SIZE_OFFSET = SIZE_OFFSET; + private static final long TARGET_SIZE_OFFSET = OBJECT_SIZE_OFFSET + PTR_SIZE; + private static final long BATCH_SIZE_OFFSET = TARGET_SIZE_OFFSET + PTR_SIZE; + private static final long DRAW_SIZE_OFFSET = BATCH_SIZE_OFFSET + PTR_SIZE; + + final MemoryBlock buffers; + final long objectStride; + int object; + int target; + int batch; + int draw; + + long objectPtr; + long batchPtr; + long drawPtr; + + int maxObjectCount = 0; + int maxDrawCount = 0; + + float objectGrowthFactor = 2f; + float drawGrowthFactor = 2f; + + IndirectBuffers(long objectStride) { + this.objectStride = objectStride; + this.buffers = MemoryBlock.calloc(BUFFERS_SIZE_BYTES, 1); + } + + void createBuffers() { + final long ptr = buffers.ptr(); + nglCreateBuffers(4, ptr); + object = MemoryUtil.memGetInt(ptr); + target = MemoryUtil.memGetInt(ptr + 4); + batch = MemoryUtil.memGetInt(ptr + 8); + draw = MemoryUtil.memGetInt(ptr + 12); + } + + void updateCounts(int objectCount, int drawCount) { + + if (objectCount > maxObjectCount) { + var newObjectCount = maxObjectCount; + while (newObjectCount <= objectCount) { + newObjectCount *= objectGrowthFactor; + } + createObjectStorage(newObjectCount); + } + if (drawCount > maxDrawCount) { + var newDrawCount = maxDrawCount; + while (newDrawCount <= drawCount) { + newDrawCount *= drawGrowthFactor; + } + createDrawStorage(newDrawCount); + } + + final long objectSize = objectStride * objectCount; + final long targetSize = INT_SIZE * objectCount; + final long drawSize = DRAW_COMMAND_STRIDE * drawCount; + + final long ptr = buffers.ptr(); + MemoryUtil.memPutAddress(ptr + OBJECT_SIZE_OFFSET, objectSize); + MemoryUtil.memPutAddress(ptr + TARGET_SIZE_OFFSET, targetSize); + MemoryUtil.memPutAddress(ptr + BATCH_SIZE_OFFSET, targetSize); + MemoryUtil.memPutAddress(ptr + DRAW_SIZE_OFFSET, drawSize); + } + + void createObjectStorage(int objectCount) { + freeObjectStogare(); + var objectSize = objectStride * objectCount; + var targetSize = INT_SIZE * objectCount; + + if (maxObjectCount > 0) { + var ptr = buffers.ptr(); + nglCreateBuffers(3, ptr); + + int objectNew = MemoryUtil.memGetInt(ptr); + int targetNew = MemoryUtil.memGetInt(ptr + 4); + int batchNew = MemoryUtil.memGetInt(ptr + 8); + + glNamedBufferStorage(objectNew, objectSize, PERSISTENT_BITS); + glNamedBufferStorage(targetNew, targetSize, GPU_ONLY_BITS); + glNamedBufferStorage(batchNew, targetSize, PERSISTENT_BITS); + + glCopyNamedBufferSubData(object, objectNew, 0, 0, objectStride * maxObjectCount); + glCopyNamedBufferSubData(target, targetNew, 0, 0, INT_SIZE * maxObjectCount); + glCopyNamedBufferSubData(batch, batchNew, 0, 0, INT_SIZE * maxObjectCount); + + glDeleteBuffers(object); + glDeleteBuffers(target); + glDeleteBuffers(batch); + + object = objectNew; + target = targetNew; + batch = batchNew; + } else { + glNamedBufferStorage(object, objectSize, PERSISTENT_BITS); + glNamedBufferStorage(target, targetSize, GPU_ONLY_BITS); + glNamedBufferStorage(batch, targetSize, PERSISTENT_BITS); + } + + objectPtr = nglMapNamedBufferRange(object, 0, objectSize, MAP_BITS); + batchPtr = nglMapNamedBufferRange(batch, 0, targetSize, MAP_BITS); + maxObjectCount = objectCount; + + FlwMemoryTracker._allocGPUMemory(maxObjectCount * objectStride + maxObjectCount * INT_SIZE); + } + + void createDrawStorage(int drawCount) { + freeDrawStorage(); + + var drawSize = DRAW_COMMAND_STRIDE * drawCount; + if (maxDrawCount > 0) { + int drawNew = glCreateBuffers(); + + glNamedBufferStorage(drawNew, drawSize, SUB_DATA_BITS); + + glDeleteBuffers(draw); + + MemoryUtil.memPutInt(buffers.ptr() + INT_SIZE * 3, drawNew); + draw = drawNew; + drawPtr = MemoryUtil.nmemRealloc(drawPtr, drawSize); + } else { + + glNamedBufferStorage(draw, drawSize, SUB_DATA_BITS); + drawPtr = MemoryUtil.nmemAlloc(drawSize); + } + maxDrawCount = drawCount; + FlwMemoryTracker._allocGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE); + } + + private void freeObjectStogare() { + FlwMemoryTracker._freeGPUMemory(maxObjectCount * objectStride + maxObjectCount * INT_SIZE); + } + + private void freeDrawStorage() { + FlwMemoryTracker._freeGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE); + } + + public void bindAll() { + bindN(BUFFER_COUNT); + } + + public void bindObjectAndTarget() { + bindN(2); + } + + private void bindN(int bufferCount) { + if (bufferCount > BUFFER_COUNT) { + throw new IllegalArgumentException("Can't bind more than " + BUFFER_COUNT + " buffers"); + } + + final long ptr = buffers.ptr(); + nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, bufferCount, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET); + } + + void bindIndirectBuffer() { + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, draw); + } + + void flushBatchIDs(long length) { + glFlushMappedNamedBufferRange(batch, 0, length); + } + + void flushObjects(long length) { + glFlushMappedNamedBufferRange(object, 0, length); + } + + void flushDrawCommands(long length) { + nglNamedBufferSubData(draw, 0, length, drawPtr); + // glFlushMappedNamedBufferRange(this.draw, 0, length); + } + + public void delete() { + nglDeleteBuffers(BUFFER_COUNT, buffers.ptr()); + buffers.free(); + freeObjectStogare(); + freeDrawStorage(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java new file mode 100644 index 000000000..f1d21bdca --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java @@ -0,0 +1,197 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import static org.lwjgl.opengl.GL42.GL_COMMAND_BARRIER_BIT; +import static org.lwjgl.opengl.GL42.glMemoryBarrier; +import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT; +import static org.lwjgl.opengl.GL46.glBindVertexArray; +import static org.lwjgl.opengl.GL46.glCreateVertexArrays; +import static org.lwjgl.opengl.GL46.glDeleteVertexArrays; +import static org.lwjgl.opengl.GL46.glDispatchCompute; +import static org.lwjgl.opengl.GL46.glEnableVertexArrayAttrib; +import static org.lwjgl.opengl.GL46.glVertexArrayElementBuffer; +import static org.lwjgl.opengl.GL46.glVertexArrayVertexBuffer; + +import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.struct.StorageBufferWriter; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; +import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.Materials; +import com.jozufozu.flywheel.core.QuadConverter; +import com.jozufozu.flywheel.core.uniform.UniformBuffer; + +public class IndirectCullingGroup { + + private static final int BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT; + + final StorageBufferWriter storageBufferWriter; + final GlProgram compute; + final GlProgram draw; + private final VertexType vertexType; + private final long objectStride; + + final IndirectBuffers buffers; + + final IndirectMeshPool meshPool; + private final int elementBuffer; + + int vertexArray; + + final IndirectDrawSet drawSet = new IndirectDrawSet<>(); + + private boolean hasCulledThisFrame; + private boolean needsMemoryBarrier; + private int instanceCountThisFrame; + + IndirectCullingGroup(StructType structType, VertexType vertexType) { + this.vertexType = vertexType; + storageBufferWriter = structType.getStorageBufferWriter(); + + objectStride = storageBufferWriter.getAlignment(); + buffers = new IndirectBuffers(objectStride); + buffers.createBuffers(); + buffers.createObjectStorage(128); + buffers.createDrawStorage(2); + + meshPool = new IndirectMeshPool(vertexType, 1024); + + vertexArray = glCreateVertexArrays(); + + elementBuffer = QuadConverter.getInstance() + .quads2Tris(2048).buffer.handle(); + setupVertexArray(); + + var indirectShader = structType.getIndirectShader(); + compute = ComputeCullerCompiler.INSTANCE.get(indirectShader); + draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.SHULKER, indirectShader, Components.WORLD, Components.INDIRECT)); + } + + private void setupVertexArray() { + glVertexArrayElementBuffer(vertexArray, elementBuffer); + + var meshLayout = vertexType.getLayout(); + var meshAttribs = meshLayout.getAttributeCount(); + + var attributes = meshLayout.getAttributes(); + + long offset = 0; + for (int i = 0; i < meshAttribs; i++) { + var attribute = attributes.get(i); + glEnableVertexArrayAttrib(vertexArray, i); + glVertexArrayVertexBuffer(vertexArray, i, meshPool.vbo, offset, meshLayout.getStride()); + attribute.format(vertexArray, i); + offset += attribute.getByteWidth(); + } + } + + void beginFrame() { + hasCulledThisFrame = false; + needsMemoryBarrier = true; + instanceCountThisFrame = calculateTotalInstanceCountAndPrepareBatches(); + } + + void submit(RenderStage stage) { + if (drawSet.isEmpty()) { + return; + } + + if (instanceCountThisFrame == 0) { + return; + } + + cull(); + dispatchDraw(stage); + } + + private void cull() { + if (hasCulledThisFrame) { + return; + } + + buffers.updateCounts(instanceCountThisFrame, drawSet.size()); + meshPool.uploadAll(); + uploadInstanceData(); + uploadIndirectCommands(); + + UniformBuffer.getInstance() + .sync(); + + compute.bind(); + buffers.bindAll(); + + var groupCount = (instanceCountThisFrame + 31) >> 5; // ceil(instanceCount / 32) + glDispatchCompute(groupCount, 1, 1); + hasCulledThisFrame = true; + } + + private void dispatchDraw(RenderStage stage) { + if (!drawSet.contains(stage)) { + return; + } + + draw.bind(); + glBindVertexArray(vertexArray); + buffers.bindObjectAndTarget(); + buffers.bindIndirectBuffer(); + + UniformBuffer.getInstance() + .sync(); + + memoryBarrier(); + + drawSet.submit(stage); + glBindVertexArray(0); + } + + private void memoryBarrier() { + if (needsMemoryBarrier) { + glMemoryBarrier(BARRIER_BITS); + needsMemoryBarrier = false; + } + } + + private void uploadInstanceData() { + long objectPtr = buffers.objectPtr; + long batchIDPtr = buffers.batchPtr; + + for (int i = 0, batchesSize = drawSet.indirectDraws.size(); i < batchesSize; i++) { + var batch = drawSet.indirectDraws.get(i); + var instanceCount = batch.instancer.getInstanceCount(); + batch.writeObjects(objectPtr, batchIDPtr, i); + + objectPtr += instanceCount * objectStride; + batchIDPtr += instanceCount * IndirectBuffers.INT_SIZE; + } + + buffers.flushObjects(objectPtr - buffers.objectPtr); + buffers.flushBatchIDs(batchIDPtr - buffers.batchPtr); + } + + private void uploadIndirectCommands() { + long writePtr = buffers.drawPtr; + for (var batch : drawSet.indirectDraws) { + batch.writeIndirectCommand(writePtr); + writePtr += IndirectBuffers.DRAW_COMMAND_STRIDE; + } + buffers.flushDrawCommands(writePtr - buffers.drawPtr); + } + + private int calculateTotalInstanceCountAndPrepareBatches() { + int baseInstance = 0; + for (var batch : drawSet.indirectDraws) { + batch.prepare(baseInstance); + baseInstance += batch.instancer.instanceCount; + } + return baseInstance; + } + + public void delete() { + glDeleteVertexArrays(vertexArray); + buffers.delete(); + meshPool.delete(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java new file mode 100644 index 000000000..c4d3cbaea --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java @@ -0,0 +1,52 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; + +public final class IndirectDraw { + final IndirectInstancer instancer; + final IndirectMeshPool.BufferedMesh mesh; + final Material material; + int baseInstance = -1; + + boolean needsFullWrite = true; + + IndirectDraw(IndirectInstancer instancer, Material material, IndirectMeshPool.BufferedMesh mesh) { + this.instancer = instancer; + this.material = material; + this.mesh = mesh; + } + + public void prepare(int baseInstance) { + instancer.update(); + if (baseInstance == this.baseInstance) { + needsFullWrite = false; + return; + } + this.baseInstance = baseInstance; + needsFullWrite = true; + } + + void writeObjects(long objectPtr, long batchIDPtr, int batchID) { + if (needsFullWrite) { + instancer.writeFull(objectPtr, batchIDPtr, batchID); + } else if (instancer.anyToUpdate) { + instancer.writeSparse(objectPtr, batchIDPtr, batchID); + } + instancer.anyToUpdate = false; + } + + public void writeIndirectCommand(long ptr) { + var boundingSphere = mesh.mesh.getBoundingSphere(); + + MemoryUtil.memPutInt(ptr, mesh.getIndexCount()); // count + MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be incremented by the compute shader + MemoryUtil.memPutInt(ptr + 8, 0); // firstIndex - all models share the same index buffer + MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex + MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance + + boundingSphere.getToAddress(ptr + 20); // boundingSphere + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawManager.java new file mode 100644 index 000000000..956694f59 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawManager.java @@ -0,0 +1,23 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import java.util.HashMap; +import java.util.Map; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.core.model.Mesh; +import com.jozufozu.flywheel.util.Pair; + +public class IndirectDrawManager { + + public final Map, VertexType>, IndirectCullingGroup> lists = new HashMap<>(); + + @SuppressWarnings("unchecked") + public void add(IndirectInstancer instancer, Material material, Mesh mesh) { + var indirectList = (IndirectCullingGroup) lists.computeIfAbsent(Pair.of(instancer.type, mesh.getVertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second())); + + indirectList.drawSet.add(instancer, material, indirectList.meshPool.alloc(mesh)); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java new file mode 100644 index 000000000..5bf8c18fc --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java @@ -0,0 +1,54 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import static org.lwjgl.opengl.GL11.GL_TRIANGLES; +import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT; +import static org.lwjgl.opengl.GL43.glMultiDrawElementsIndirect; + +import java.util.ArrayList; +import java.util.List; + +import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; + +public class IndirectDrawSet { + + final List> indirectDraws = new ArrayList<>(); + + public boolean isEmpty() { + return indirectDraws.isEmpty(); + } + + public int size() { + return indirectDraws.size(); + } + + public void add(IndirectInstancer instancer, Material material, IndirectMeshPool.BufferedMesh bufferedMesh) { + indirectDraws.add(new IndirectDraw<>(instancer, material, bufferedMesh)); + } + + public void submit(RenderStage stage) { + final int stride = (int) IndirectBuffers.DRAW_COMMAND_STRIDE; + for (int i = 0, indirectDrawsSize = indirectDraws.size(); i < indirectDrawsSize; i++) { + var batch = indirectDraws.get(i); + var material = batch.material; + + if (material.getRenderStage() != stage) { + continue; + } + material.setup(); + glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, i * stride, 1, stride); + material.clear(); + } + } + + public boolean contains(RenderStage stage) { + for (var draw : indirectDraws) { + if (draw.material.getRenderStage() == stage) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java new file mode 100644 index 000000000..f6585868f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -0,0 +1,147 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jetbrains.annotations.NotNull; +import org.lwjgl.opengl.GL32; + +import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.context.ContextShader; +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.gl.GlTextureUnit; +import com.jozufozu.flywheel.backend.instancing.Engine; +import com.jozufozu.flywheel.backend.instancing.InstanceManager; +import com.jozufozu.flywheel.backend.instancing.TaskEngine; +import com.jozufozu.flywheel.core.RenderContext; +import com.jozufozu.flywheel.util.WeakHashSet; +import com.mojang.blaze3d.systems.RenderSystem; + +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; + +public class IndirectEngine implements Engine { + + public static int MAX_ORIGIN_DISTANCE = 100; + + protected BlockPos originCoordinate = BlockPos.ZERO; + + protected final ContextShader context; + + protected final Map, IndirectFactory> factories = new HashMap<>(); + + protected final List> uninitializedModels = new ArrayList<>(); + protected final IndirectDrawManager indirectDrawManager = new IndirectDrawManager(); + + /** + * The set of instance managers that are attached to this engine. + */ + private final WeakHashSet> instanceManagers; + + public IndirectEngine(ContextShader context) { + this.context = context; + + this.instanceManagers = new WeakHashSet<>(); + } + + @SuppressWarnings("unchecked") + @NotNull + @Override + public IndirectFactory factory(StructType type) { + return (IndirectFactory) factories.computeIfAbsent(type, this::createFactory); + } + + @NotNull + private IndirectFactory createFactory(StructType type) { + return new IndirectFactory<>(type, uninitializedModels::add); + } + + @Override + public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) { + setup(); + + for (var list : indirectDrawManager.lists.values()) { + list.submit(stage); + } + } + + private void setup() { + GlTextureUnit.T2.makeActive(); + Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer(); + + RenderSystem.depthMask(true); + RenderSystem.colorMask(true, true, true, true); + RenderSystem.enableDepthTest(); + RenderSystem.depthFunc(GL32.GL_LEQUAL); + RenderSystem.enableCull(); + } + + @Override + public void delete() { + factories.values() + .forEach(IndirectFactory::delete); + + indirectDrawManager.lists.values() + .forEach(IndirectCullingGroup::delete); + + factories.clear(); + } + + @Override + public Vec3i getOriginCoordinate() { + return originCoordinate; + } + + @Override + public void attachManagers(InstanceManager... listener) { + instanceManagers.addAll(List.of(listener)); + } + + @Override + public boolean maintainOriginCoordinate(Camera camera) { + Vec3 cameraPos = camera.getPosition(); + + double distanceSqr = Vec3.atLowerCornerOf(originCoordinate) + .subtract(cameraPos) + .lengthSqr(); + + if (distanceSqr > MAX_ORIGIN_DISTANCE * MAX_ORIGIN_DISTANCE) { + shiftListeners(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y), Mth.floor(cameraPos.z)); + return true; + } + return false; + } + + @Override + public void beginFrame(TaskEngine taskEngine, RenderContext context) { + for (var model : uninitializedModels) { + model.init(indirectDrawManager); + } + uninitializedModels.clear(); + + for (IndirectCullingGroup value : indirectDrawManager.lists.values()) { + value.beginFrame(); + } + } + + private void shiftListeners(int cX, int cY, int cZ) { + originCoordinate = new BlockPos(cX, cY, cZ); + + factories.values().forEach(IndirectFactory::clear); + + instanceManagers.forEach(InstanceManager::onOriginShift); + } + + @Override + public void addDebugInfo(List info) { + info.add("GL46 Indirect"); + info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ()); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectFactory.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectFactory.java new file mode 100644 index 000000000..d0bfaa3b8 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectFactory.java @@ -0,0 +1,50 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.instancer.Instancer; +import com.jozufozu.flywheel.api.instancer.InstancerFactory; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; +import com.jozufozu.flywheel.core.model.Model; + +public class IndirectFactory implements InstancerFactory { + + protected final Map> models = new HashMap<>(); + protected final StructType type; + private final Consumer> creationListener; + + public IndirectFactory(StructType type, Consumer> creationListener) { + this.type = type; + this.creationListener = creationListener; + } + + @Override + public Instancer model(Model modelKey) { + return models.computeIfAbsent(modelKey, this::createInstancer).getInstancer(); + } + + public void delete() { + models.values().forEach(IndirectModel::delete); + models.clear(); + } + + /** + * Clear all instance data without freeing resources. + */ + public void clear() { + models.values() + .stream() + .map(IndirectModel::getInstancer) + .forEach(AbstractInstancer::clear); + } + + private IndirectModel createInstancer(Model model) { + var instancer = new IndirectModel<>(type, model); + this.creationListener.accept(instancer); + return instancer; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java new file mode 100644 index 000000000..c1c24ba46 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectInstancer.java @@ -0,0 +1,74 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; +import com.jozufozu.flywheel.core.layout.BufferLayout; + +public class IndirectInstancer extends AbstractInstancer { + + public final BufferLayout instanceFormat; + public final IndirectModel parent; + int instanceCount = 0; + + boolean anyToUpdate; + + public IndirectInstancer(IndirectModel parent, StructType type) { + super(type); + this.parent = parent; + this.instanceFormat = type.getLayout(); + } + + @Override + public void notifyDirty() { + anyToUpdate = true; + } + + public boolean isEmpty() { + return !anyToUpdate && !anyToRemove && instanceCount == 0; + } + + void update() { + if (anyToRemove) { + removeDeletedInstances(); + } + + instanceCount = data.size(); + + anyToRemove = false; + } + + public void writeSparse(long objectPtr, long batchIDPtr, int batchID) { + var storageBufferWriter = this.type.getStorageBufferWriter(); + long objectStride = storageBufferWriter.getAlignment(); + for (int i = 0, size = data.size(); i < size; i++) { + final var element = data.get(i); + if (element.checkDirtyAndClear()) { + storageBufferWriter.write(objectPtr + i * objectStride, element); + + MemoryUtil.memPutInt(batchIDPtr + i * IndirectBuffers.INT_SIZE, batchID); + } + } + } + + public void writeFull(long objectPtr, long batchIDPtr, int batchID) { + var storageBufferWriter = this.type.getStorageBufferWriter(); + var objectStride = storageBufferWriter.getAlignment(); + for (var object : data) { + // write object + storageBufferWriter.write(objectPtr, object); + objectPtr += objectStride; + + // write batchID + MemoryUtil.memPutInt(batchIDPtr, batchID); + batchIDPtr += IndirectBuffers.INT_SIZE; + } + } + + @Override + public void delete() { + // noop + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java new file mode 100644 index 000000000..367e4ffdb --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectMeshPool.java @@ -0,0 +1,127 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import static org.lwjgl.opengl.GL46.*; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer; +import com.jozufozu.flywheel.backend.memory.MemoryBlock; +import com.jozufozu.flywheel.core.model.Mesh; + +public class IndirectMeshPool { + + private final Map meshes = new HashMap<>(); + private final List meshList = new ArrayList<>(); + + final VertexType vertexType; + + final int vbo; + private final MemoryBlock clientStorage; + + private boolean dirty; + + /** + * Create a new mesh pool. + */ + public IndirectMeshPool(VertexType type, int vertexCapacity) { + vertexType = type; + vbo = glCreateBuffers(); + var byteCapacity = type.byteOffset(vertexCapacity); + glNamedBufferStorage(vbo, byteCapacity, GL_DYNAMIC_STORAGE_BIT); + clientStorage = MemoryBlock.malloc(byteCapacity); + } + + /** + * Allocate a model in the arena. + * + * @param mesh The model to allocate. + * @return A handle to the allocated model. + */ + public BufferedMesh alloc(Mesh mesh) { + return meshes.computeIfAbsent(mesh, m -> { + BufferedMesh bufferedModel = new BufferedMesh(m); + meshList.add(bufferedModel); + + dirty = true; + return bufferedModel; + }); + } + + @Nullable + public BufferedMesh get(Mesh mesh) { + return meshes.get(mesh); + } + + void uploadAll() { + if (!dirty) { + return; + } + dirty = false; + + final long ptr = clientStorage.ptr(); + + int byteIndex = 0; + int baseVertex = 0; + for (BufferedMesh model : meshList) { + model.byteIndex = byteIndex; + model.baseVertex = baseVertex; + + model.buffer(ptr); + + byteIndex += model.getByteSize(); + baseVertex += model.mesh.getVertexCount(); + } + + nglNamedBufferSubData(vbo, 0, byteIndex, ptr); + } + + public void delete() { + clientStorage.free(); + glDeleteBuffers(vbo); + meshes.clear(); + meshList.clear(); + } + + public class BufferedMesh { + + public final Mesh mesh; + private final int vertexCount; + private long byteIndex; + private int baseVertex; + + public BufferedMesh(Mesh mesh) { + this.mesh = mesh; + + vertexCount = mesh.getVertexCount(); + } + + private void buffer(long ptr) { + this.mesh.write(ptr + byteIndex); + } + + public int getByteSize() { + return IndirectMeshPool.this.vertexType.getLayout().getStride() * this.vertexCount; + } + + public int getBaseVertex() { + return baseVertex; + } + + public int getVertexCount() { + return this.vertexCount; + } + + public int getIndexCount() { + return this.vertexCount * 6 / 4; + } + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java new file mode 100644 index 000000000..29638638b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectModel.java @@ -0,0 +1,43 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import com.jozufozu.flywheel.api.instancer.InstancedPart; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.core.model.Model; + +public class IndirectModel { + + private final Model model; + private final IndirectInstancer instancer; + + public IndirectModel(StructType type, Model model) { + this.model = model; + this.instancer = new IndirectInstancer<>(this, type); + } + + public void init(IndirectDrawManager indirectDrawManager) { + var materialMeshMap = this.model.getMeshes(); + for (var entry : materialMeshMap.entrySet()) { + var material = entry.getKey(); + var mesh = entry.getValue(); + indirectDrawManager.add(instancer, material, mesh); + + return; // TODO: support multiple meshes per model + } + } + + public IndirectInstancer getInstancer() { + return instancer; + } + + public Model getModel() { + return model; + } + + public int getVertexCount() { + return model.getVertexCount() * instancer.instanceCount; + } + + public void delete() { + + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ShaderState.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ShaderState.java new file mode 100644 index 000000000..7f1844eb2 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ShaderState.java @@ -0,0 +1,8 @@ +package com.jozufozu.flywheel.backend.instancing.indirect; + +import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; + +public record ShaderState(Material material, VertexType vertex, StructType instance) { +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/package-info.java new file mode 100644 index 000000000..6ec4f9bda --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.instancing.indirect; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index 0417d9124..23f5ba6df 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -16,10 +16,11 @@ import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.gl.GlTextureUnit; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.InstanceManager; +import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; import com.jozufozu.flywheel.backend.instancing.TaskEngine; +import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.RenderContext; -import com.jozufozu.flywheel.core.compile.ContextShader; -import com.jozufozu.flywheel.core.compile.ProgramCompiler; +import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.uniform.UniformBuffer; import com.jozufozu.flywheel.util.WeakHashSet; @@ -122,9 +123,9 @@ public class InstancingEngine implements Engine { .getInstanceShader(); Material material = desc.material(); - var ctx = new ProgramCompiler.Context(vertexType, material, instanceShader, context); + var ctx = new PipelineCompiler.Context(vertexType, material, instanceShader, context, Components.INSTANCED_ARRAYS); - ProgramCompiler.INSTANCE.getProgram(ctx) + PipelineCompiler.INSTANCE.getProgram(ctx) .bind(); UniformBuffer.getInstance().sync(); } diff --git a/src/main/java/com/jozufozu/flywheel/config/BackendType.java b/src/main/java/com/jozufozu/flywheel/config/BackendType.java index 822c28973..a64da9ddd 100644 --- a/src/main/java/com/jozufozu/flywheel/config/BackendType.java +++ b/src/main/java/com/jozufozu/flywheel/config/BackendType.java @@ -19,6 +19,11 @@ public enum BackendType { * Use GPU instancing to render everything. */ INSTANCING("GL33 Instanced Arrays"), + + /** + * Use Compute shaders to cull instances. + */ + INDIRECT("GL46 Compute Culling"), ; private static final Map lookup; diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index c9365b451..5fb8a5032 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -5,6 +5,7 @@ import java.util.function.BiConsumer; import org.jetbrains.annotations.NotNull; import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.core.uniform.FrustumProvider; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.IntegerArgumentType; @@ -109,6 +110,23 @@ public class FlwCommands { return 1; })))); + commandBuilder.command.then(Commands.literal("debugFrustum") + .then(Commands.literal("pause") + .executes(context -> { + FrustumProvider.PAUSED = true; + return 1; + })) + .then(Commands.literal("unpause") + .executes(context -> { + FrustumProvider.PAUSED = false; + return 1; + })) + .then(Commands.literal("capture") + .executes(context -> { + FrustumProvider.CAPTURE = true; + return 1; + }))); + commandBuilder.build(event.getDispatcher()); } @@ -141,6 +159,7 @@ public class FlwCommands { case OFF -> new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED); case INSTANCING -> new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN); case BATCHING -> new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN); + case INDIRECT -> new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN); }; } diff --git a/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java b/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java index 6200c4679..c44251baf 100644 --- a/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java +++ b/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java @@ -11,7 +11,7 @@ import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.uniform.UniformProvider; import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.core.compile.ContextShader; +import com.jozufozu.flywheel.api.context.ContextShader; import net.minecraft.resources.ResourceLocation; diff --git a/src/main/java/com/jozufozu/flywheel/core/Components.java b/src/main/java/com/jozufozu/flywheel/core/Components.java index 3edc8496d..ab85c20d4 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Components.java +++ b/src/main/java/com/jozufozu/flywheel/core/Components.java @@ -3,7 +3,9 @@ package com.jozufozu.flywheel.core; import java.util.function.BiConsumer; import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.core.compile.ContextShader; +import com.jozufozu.flywheel.api.context.ContextShader; +import com.jozufozu.flywheel.api.pipeline.PipelineShader; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; import com.jozufozu.flywheel.core.crumbling.CrumblingProgram; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.SourceChecks; @@ -11,6 +13,7 @@ import com.jozufozu.flywheel.core.source.SourceFile; import com.jozufozu.flywheel.core.source.error.ErrorReporter; import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.uniform.FogProvider; +import com.jozufozu.flywheel.core.uniform.FrustumProvider; import com.jozufozu.flywheel.core.uniform.ViewProvider; import com.jozufozu.flywheel.core.vertex.Formats; import com.jozufozu.flywheel.util.ResourceUtil; @@ -22,9 +25,13 @@ public class Components { public static final ViewProvider VIEW_PROVIDER = ComponentRegistry.register(new ViewProvider()); public static final FogProvider FOG_PROVIDER = ComponentRegistry.register(new FogProvider()); + public static final FrustumProvider FRUSTUM_PROVIDER = ComponentRegistry.register(new FrustumProvider()); public static final ContextShader WORLD = ComponentRegistry.register(new ContextShader(WorldProgram::new, Files.WORLD_VERTEX, Files.WORLD_FRAGMENT)); public static final ContextShader CRUMBLING = ComponentRegistry.register(new ContextShader(CrumblingProgram::new, Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT)); + public static final PipelineShader INSTANCED_ARRAYS = new PipelineShader(GLSLVersion.V420, Pipeline.INSTANCED_ARRAYS_DRAW, Pipeline.DRAW_FRAGMENT); + public static final PipelineShader INDIRECT = new PipelineShader(GLSLVersion.V460, Pipeline.INDIRECT_DRAW, Pipeline.DRAW_FRAGMENT); + public static void init() { Files.init(); Formats.init(); @@ -32,14 +39,29 @@ public class Components { Materials.init(); } + public static class Pipeline { + public static final FileResolution DRAW_FRAGMENT = pipeline("pipeline/draw.frag"); + public static final FileResolution INSTANCED_ARRAYS_DRAW = pipeline("pipeline/instanced_arrays_draw.vert"); + public static final FileResolution INDIRECT_DRAW = pipeline("pipeline/indirect_draw.vert"); + public static final FileResolution INDIRECT_CULL = pipeline("pipeline/indirect_cull.glsl"); + + private static FileResolution pipeline(String name) { + return FileResolution.get(Flywheel.rl(name)) + .validateWith(Checks.PIPELINE); + } + } + public static class Files { public static final FileResolution VIEW_UNIFORMS = uniform(Flywheel.rl("uniform/view.glsl")); public static final FileResolution FOG_UNIFORMS = uniform(Flywheel.rl("uniform/fog.glsl")); + public static final FileResolution FRUSTUM_UNIFORMS = uniform(Flywheel.rl("uniform/frustum.glsl")); public static final FileResolution BLOCK_LAYOUT = layoutVertex(ResourceUtil.subPath(Names.BLOCK, ".vert")); public static final FileResolution POS_TEX_NORMAL_LAYOUT = layoutVertex(ResourceUtil.subPath(Names.POS_TEX_NORMAL, ".vert")); public static final FileResolution TRANSFORMED = instanceVertex(ResourceUtil.subPath(Names.TRANSFORMED, ".vert")); + public static final FileResolution TRANSFORMED_INDIRECT = instanceVertex(ResourceUtil.subPath(Names.TRANSFORMED, "_indirect.glsl")); public static final FileResolution ORIENTED = instanceVertex(ResourceUtil.subPath(Names.ORIENTED, ".vert")); + public static final FileResolution ORIENTED_INDIRECT = instanceVertex(ResourceUtil.subPath(Names.ORIENTED, "_indirect.glsl")); public static final FileResolution DEFAULT_VERTEX = materialVertex(ResourceUtil.subPath(Names.DEFAULT, ".vert")); public static final FileResolution SHADED_VERTEX = materialVertex(ResourceUtil.subPath(Names.SHADED, ".vert")); public static final FileResolution DEFAULT_FRAGMENT = materialFragment(ResourceUtil.subPath(Names.DEFAULT, ".frag")); @@ -49,6 +71,10 @@ public class Components { public static final FileResolution CRUMBLING_VERTEX = contextVertex(ResourceUtil.subPath(Names.CRUMBLING, ".vert")); public static final FileResolution CRUMBLING_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.CRUMBLING, ".frag")); + private static FileResolution compute(ResourceLocation rl) { + return FileResolution.get(rl); + } + private static FileResolution uniform(ResourceLocation location) { return FileResolution.get(location); } @@ -59,8 +85,7 @@ public class Components { } private static FileResolution instanceVertex(ResourceLocation location) { - return FileResolution.get(location) - .validateWith(Checks.INSTANCE_VERTEX); + return FileResolution.get(location); // .validateWith(Checks.INSTANCE_VERTEX); } private static FileResolution materialVertex(ResourceLocation location) { @@ -90,12 +115,16 @@ public class Components { public static class Checks { - public static final BiConsumer LAYOUT_VERTEX = SourceChecks.checkFunctionArity("flw_layoutVertex", 0); - public static final BiConsumer INSTANCE_VERTEX = SourceChecks.checkFunctionArity("flw_instanceVertex", 0); + public static final BiConsumer LAYOUT_VERTEX = SourceChecks.checkFunctionArity("flw_layoutVertex", 0) + .andThen(SourceChecks.checkDefine("FLW_INSTANCE_BASE_INDEX")); + public static final BiConsumer INSTANCE_VERTEX = SourceChecks.checkFunctionParameterTypeExists("flw_instanceVertex", 1, 0) + .andThen(SourceChecks.checkDefine("FLW_INSTANCE_STRUCT")); public static final BiConsumer MATERIAL_VERTEX = SourceChecks.checkFunctionArity("flw_materialVertex", 0); public static final BiConsumer MATERIAL_FRAGMENT = SourceChecks.checkFunctionArity("flw_materialFragment", 0); public static final BiConsumer CONTEXT_VERTEX = SourceChecks.checkFunctionArity("flw_contextVertex", 0); public static final BiConsumer CONTEXT_FRAGMENT = SourceChecks.checkFunctionArity("flw_contextFragment", 0).andThen(SourceChecks.checkFunctionArity("flw_initFragment", 0)); + + public static final BiConsumer PIPELINE = SourceChecks.checkFunctionArity("main", 0); } public static class Names { @@ -110,5 +139,6 @@ public class Components { public static final ResourceLocation SHADED = Flywheel.rl("material/shaded"); public static final ResourceLocation WORLD = Flywheel.rl("context/world"); public static final ResourceLocation CRUMBLING = Flywheel.rl("context/crumbling"); + public static final ResourceLocation DRAW_INDIRECT = Flywheel.rl("compute/draw_instances"); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/DebugRender.java b/src/main/java/com/jozufozu/flywheel/core/DebugRender.java new file mode 100644 index 000000000..10c8e414c --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/DebugRender.java @@ -0,0 +1,104 @@ +package com.jozufozu.flywheel.core; + +import org.lwjgl.opengl.GL46; +import org.lwjgl.system.MemoryStack; + +import com.jozufozu.flywheel.Flywheel; +import com.jozufozu.flywheel.backend.gl.GlStateTracker; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.core.compile.DebugCompiler; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.util.Lazy; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; +import com.mojang.blaze3d.systems.RenderSystem; + +public class DebugRender { + + private static final Lazy SHADER = Lazy.of(() -> DebugCompiler.INSTANCE.get(new DebugCompiler.Context(Files.VERTEX, Files.FRAGMENT))); + + private static final Lazy FRUSTUM_VBO = Lazy.of(Frustum::new); + + public static void init() { + Files.init(); + } + + public static void updateFrustum(FrustumIntersection culler) { + FRUSTUM_VBO.get() + .upload(culler); + } + + public static void drawFrustum() { + if (!FRUSTUM_VBO.isInitialized()) { + return; + } + + RenderSystem.disableCull(); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + + try (var ignored = GlStateTracker.getRestoreState()) { + SHADER.get() + .bind(); + FRUSTUM_VBO.get() + .draw(); + } + } + + public static class Files { + public static final FileResolution VERTEX = FileResolution.get(Flywheel.rl("debug/debug.vert")); + public static final FileResolution FRAGMENT = FileResolution.get(Flywheel.rl("debug/debug.frag")); + + public static void init() { + + } + } + + // FIXME: This never worked (and the thing it was meant to debug is already fixed), + // but it should be a quick turnaround + private static class Frustum { + private static final int[] indices = new int[]{ + 0, 2, 3, 0, 3, 1, + 2, 6, 7, 2, 7, 3, + 6, 4, 5, 6, 5, 7, + 4, 0, 1, 4, 1, 5, + 0, 4, 6, 0, 6, 2, + 1, 5, 7, 1, 7, 3, + }; + + private static final int elementCount = indices.length; + private static final int indicesSize = elementCount * 4; + private static final int verticesSize = 3 * 8 * 4; + private final int buffer; + private final int vao; + + public Frustum() { + // holy moly DSA is nice + buffer = GL46.glCreateBuffers(); + GL46.glNamedBufferStorage(buffer, verticesSize + indicesSize, GL46.GL_DYNAMIC_STORAGE_BIT); + GL46.glNamedBufferSubData(buffer, 0, indices); + + vao = GL46.glCreateVertexArrays(); + GL46.glEnableVertexArrayAttrib(vao, 0); + GL46.glVertexArrayElementBuffer(vao, buffer); + GL46.glVertexArrayVertexBuffer(vao, 0, buffer, indicesSize, 3 * 4); + GL46.glVertexArrayAttribFormat(vao, 0, 3, GL46.GL_FLOAT, false, 0); + } + + public void upload(FrustumIntersection culler) { + try (var stack = MemoryStack.stackPush()) { + var buf = stack.malloc(3 * 8 * 4); + + culler.getCorners(buf); + + GL46.glNamedBufferSubData(buffer, indicesSize, buf); + } + } + + public void draw() { + GL46.glEnableVertexArrayAttrib(vao, 0); + GL46.glVertexArrayElementBuffer(vao, buffer); + GL46.glBindVertexArray(vao); + GL46.glDrawElements(GL46.GL_TRIANGLES, elementCount, GL46.GL_UNSIGNED_INT, 0); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/RenderContext.java b/src/main/java/com/jozufozu/flywheel/core/RenderContext.java index 6fd3effbb..4664a052a 100644 --- a/src/main/java/com/jozufozu/flywheel/core/RenderContext.java +++ b/src/main/java/com/jozufozu/flywheel/core/RenderContext.java @@ -2,11 +2,10 @@ package com.jozufozu.flywheel.core; import org.jetbrains.annotations.NotNull; -import com.jozufozu.flywheel.util.transform.TransformStack; +import com.jozufozu.flywheel.extension.Matrix4fExtension; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; -import com.mojang.math.Quaternion; import net.minecraft.client.Camera; import net.minecraft.client.multiplayer.ClientLevel; @@ -14,44 +13,7 @@ import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.RenderBuffers; public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack stack, Matrix4f viewProjection, - Matrix4f projection, RenderBuffers buffers, Camera camera) implements TransformStack { - - @Override - public TransformStack multiply(Quaternion quaternion) { - return TransformStack.cast(stack).multiply(quaternion); - } - - @Override - public TransformStack scale(float factorX, float factorY, float factorZ) { - return TransformStack.cast(stack).scale(factorX, factorY, factorZ); - } - - @Override - public TransformStack pushPose() { - stack.pushPose(); - return TransformStack.cast(stack); - } - - @Override - public TransformStack popPose() { - stack.popPose(); - return TransformStack.cast(stack); - } - - @Override - public TransformStack mulPose(Matrix4f pose) { - return TransformStack.cast(stack).mulPose(pose); - } - - @Override - public TransformStack mulNormal(Matrix3f normal) { - return TransformStack.cast(stack).mulNormal(normal); - } - - @Override - public TransformStack translate(double x, double y, double z) { - return TransformStack.cast(stack).translate(x, y, z); - } + Matrix4f projection, RenderBuffers buffers, Camera camera, FrustumIntersection culler) { @NotNull public static Matrix4f createViewProjection(PoseStack view, Matrix4f projection) { @@ -59,4 +21,12 @@ public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack viewProjection.multiply(view.last().pose()); return viewProjection; } + + public static FrustumIntersection createCuller(Matrix4f viewProjection, float camX, float camY, float camZ) { + com.jozufozu.flywheel.util.joml.Matrix4f proj = Matrix4fExtension.clone(viewProjection); + + proj.translate(camX, camY, camZ); + + return new FrustumIntersection(proj); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java b/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java index 19784f7fb..d4641970e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java @@ -16,10 +16,8 @@ public class CompileUtil { public static final Pattern vecType = Pattern.compile("^[biud]?vec([234])$"); public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$"); - protected static String generateHeader(GLSLVersion version, ShaderType type) { - return "#version " + version + '\n' - + "#extension GL_ARB_explicit_attrib_location : enable\n" - + "#extension GL_ARB_conservative_depth : enable\n" + public static String generateHeader(GLSLVersion version, ShaderType type) { + return version.getVersionLine() + type.getDefineStatement() + '\n'; } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/DebugCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/DebugCompiler.java new file mode 100644 index 000000000..319d2dc11 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/DebugCompiler.java @@ -0,0 +1,89 @@ +package com.jozufozu.flywheel.core.compile; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.source.CompilationContext; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.event.ReloadRenderersEvent; + +/** + * Simple shader compiler that pulls no excessive tricks.

+ * Useful for writing experimental shaders or + */ +public class DebugCompiler extends Memoizer { + + public static final DebugCompiler INSTANCE = new DebugCompiler(); + + private final ShaderCompiler shaderCompiler; + + private DebugCompiler() { + this.shaderCompiler = new ShaderCompiler(); + } + + @Override + public void invalidate() { + super.invalidate(); + shaderCompiler.invalidate(); + } + + @Override + protected GlProgram _create(DebugCompiler.Context ctx) { + + return new ProgramAssembler(ctx.vertex.getFileLoc()) + .attachShader(shaderCompiler.vertex(ctx.vertex)) + .attachShader(shaderCompiler.fragment(ctx.fragment)) + .link() + .build(GlProgram::new); + } + + @Override + protected void _destroy(GlProgram value) { + value.delete(); + } + + public static void invalidateAll(ReloadRenderersEvent ignored) { + INSTANCE.invalidate(); + } + + public record Context(FileResolution vertex, FileResolution fragment) { + } + + /** + * Handles compilation and deletion of vertex shaders. + */ + private static class ShaderCompiler extends Memoizer { + + public GlShader vertex(FileResolution source) { + return get(new Context(source, ShaderType.VERTEX)); + } + + public GlShader fragment(FileResolution source) { + return get(new Context(source, ShaderType.FRAGMENT)); + } + + @Override + protected GlShader _create(Context ctx) { + var index = new CompilationContext(); + + String source = CompileUtil.generateHeader(GLSLVersion.V420, ctx.type) + ctx.source.getFile() + .generateFinalSource(index); + + try { + return new GlShader(source, ctx.type, ImmutableList.of(ctx.source.getFileLoc())); + } catch (ShaderCompilationException e) { + throw e.withErrorLog(index); + } + } + + @Override + protected void _destroy(GlShader value) { + value.delete(); + } + + public record Context(FileResolution source, ShaderType type) { + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java deleted file mode 100644 index 2923de834..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.jozufozu.flywheel.core.compile; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.gl.GLSLVersion; -import com.jozufozu.flywheel.backend.gl.shader.GlShader; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.core.source.CompilationContext; -import com.jozufozu.flywheel.core.source.SourceFile; - -/** - * Handles compilation and deletion of fragment shaders. - */ -public class FragmentCompiler extends Memoizer { - - public FragmentCompiler() { - } - - @Override - protected GlShader _create(Context key) { - StringBuilder finalSource = new StringBuilder(); - - finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.FRAGMENT)); - - var ctx = new CompilationContext(); - - // MATERIAL - - SourceFile materialShader = key.materialShader; - finalSource.append(materialShader.generateFinalSource(ctx)); - - // CONTEXT - - SourceFile contextShaderSource = key.contextShader; - finalSource.append(contextShaderSource.generateFinalSource(ctx)); - - // MAIN - - finalSource.append(generateFooter()); - - try { - return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name)); - } catch (ShaderCompilationException e) { - throw e.withErrorLog(ctx); - } - } - - protected String generateFooter() { - return """ - void main() { - flw_initFragment(); - - flw_materialFragment(); - - flw_contextFragment(); - } - """; - } - - @Override - protected void _destroy(GlShader value) { - value.delete(); - } - - /** - * Represents the conditions under which a shader is compiled. - * - * @param materialShader The fragment material shader source. - */ - public record Context(SourceFile materialShader, SourceFile contextShader) { - - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java deleted file mode 100644 index 9aee2efc8..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.jozufozu.flywheel.core.compile; - -import com.jozufozu.flywheel.api.material.Material; -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.core.source.FileResolution; -import com.jozufozu.flywheel.event.ReloadRenderersEvent; - -/** - * A caching compiler. - * - *

- * This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of - * compiled programs, and will only compile a program if it is not already in the cache. - *

- *

- * A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload. - *

- */ -public class ProgramCompiler extends Memoizer { - - public static final ProgramCompiler INSTANCE = new ProgramCompiler(); - - private final VertexCompiler vertexCompiler; - private final FragmentCompiler fragmentCompiler; - - private ProgramCompiler() { - this.vertexCompiler = new VertexCompiler(); - this.fragmentCompiler = new FragmentCompiler(); - } - - /** - * Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec. - * - * @param ctx The context of compilation. - * @return A compiled GlProgram. - */ - public GlProgram getProgram(ProgramCompiler.Context ctx) { - return super.get(ctx); - } - - @Override - public void invalidate() { - super.invalidate(); - vertexCompiler.invalidate(); - fragmentCompiler.invalidate(); - } - - @Override - protected GlProgram _create(ProgramCompiler.Context ctx) { - // TODO: try-catch here to prevent crashing if shaders failed to compile - Material material = ctx.material; - FileResolution instanceShader = ctx.instanceShader(); - ContextShader contextShader = ctx.contextShader; - - var vertex = new VertexCompiler.Context(ctx.vertexType(), instanceShader.getFile(), material.getVertexShader().getFile(), - contextShader.getVertexShader()); - - var fragment = new FragmentCompiler.Context(material.getFragmentShader().getFile(), contextShader.getFragmentShader()); - - return new ProgramAssembler(instanceShader.getFileLoc()) - .attachShader(vertexCompiler.get(vertex)) - .attachShader(fragmentCompiler.get(fragment)) - .link() - .build(contextShader.factory()); - } - - @Override - protected void _destroy(GlProgram value) { - value.delete(); - } - - public static void onReloadRenderers(ReloadRenderersEvent event) { - INSTANCE.invalidate(); - } - - /** - * Represents the entire context of a program's usage. - * - * @param vertexType The vertexType the program should be adapted for. - * @param material The material shader to use. - * @param instanceShader The instance shader to use. - * @param contextShader The context shader to use. - */ - public record Context(VertexType vertexType, Material material, FileResolution instanceShader, - ContextShader contextShader) { - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java deleted file mode 100644 index 50aba6150..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.jozufozu.flywheel.core.compile; - -import java.util.ArrayList; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.gl.GLSLVersion; -import com.jozufozu.flywheel.backend.gl.shader.GlShader; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.core.source.CompilationContext; -import com.jozufozu.flywheel.core.source.SourceFile; -import com.jozufozu.flywheel.core.source.parse.ShaderField; -import com.jozufozu.flywheel.core.source.span.Span; -import com.jozufozu.flywheel.util.Pair; - -/** - * Handles compilation and deletion of vertex shaders. - */ -public class VertexCompiler extends Memoizer { - - public VertexCompiler() { - } - - @Override - protected GlShader _create(Context key) { - StringBuilder finalSource = new StringBuilder(); - - finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX)); - - var index = new CompilationContext(); - - // LAYOUT - - var layoutShader = key.vertexType.getLayoutShader().getFile(); - finalSource.append(layoutShader.generateFinalSource(index)); - - // INSTANCE - - int attributeBaseIndex = key.vertexType.getLayout() - .getAttributeCount(); - - var instanceShader = key.instanceShader; - var replacements = new ArrayList>(); - for (ShaderField field : instanceShader.fields.values()) { - if (field.decoration != ShaderField.Decoration.IN) { - continue; - } - - int location = Integer.parseInt(field.location.get()); - int newLocation = location + attributeBaseIndex; - replacements.add(Pair.of(field.location, Integer.toString(newLocation))); - } - finalSource.append(instanceShader.generateFinalSource(index, replacements)); - - // MATERIAL - - var materialShader = key.materialShader; - finalSource.append(materialShader.generateFinalSource(index)); - - // CONTEXT - - var contextShaderSource = key.contextShader; - finalSource.append(contextShaderSource.generateFinalSource(index)); - - // MAIN - - finalSource.append(""" - void main() { - flw_layoutVertex(); - - flw_instanceVertex(); - - flw_materialVertex(); - - flw_contextVertex(); - } - """); - - try { - return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name)); - } catch (ShaderCompilationException e) { - throw e.withErrorLog(index); - } - } - - @Override - protected void _destroy(GlShader value) { - value.delete(); - } - - /** - * @param vertexType The vertex type to use. - * @param instanceShader The instance shader source. - * @param materialShader The vertex material shader source. - * @param contextShader The context shader source. - */ - public record Context(VertexType vertexType, SourceFile instanceShader, SourceFile materialShader, SourceFile contextShader) { - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java deleted file mode 100644 index c58b5984d..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingInstanceManager.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.jozufozu.flywheel.core.crumbling; - -import com.jozufozu.flywheel.api.instance.DynamicInstance; -import com.jozufozu.flywheel.api.instancer.InstancerManager; -import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager; - -public class CrumblingInstanceManager extends BlockEntityInstanceManager { - - public CrumblingInstanceManager(InstancerManager instancerManager) { - super(instancerManager); - } - - @Override - protected void updateInstance(DynamicInstance dyn, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) { - dyn.beginFrame(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java index 4dc301213..b381d859b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java +++ b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java @@ -6,25 +6,23 @@ import com.jozufozu.flywheel.api.vertex.MutableVertexList; import com.jozufozu.flywheel.api.vertex.ReusableVertexList; import com.jozufozu.flywheel.backend.memory.MemoryBlock; import com.jozufozu.flywheel.core.model.Mesh; +import com.jozufozu.flywheel.core.model.ModelUtil; import com.jozufozu.flywheel.core.vertex.Formats; import com.jozufozu.flywheel.core.vertex.PosTexNormalVertex; +import com.jozufozu.flywheel.util.joml.Vector4f; +import com.jozufozu.flywheel.util.joml.Vector4fc; public class ModelPart implements Mesh { private final int vertexCount; private final MemoryBlock contents; private final ReusableVertexList vertexList; private final String name; + private final Vector4f boundingSphere; public ModelPart(List cuboids, String name) { this.name = name; - { - int vertices = 0; - for (PartBuilder.CuboidBuilder cuboid : cuboids) { - vertices += cuboid.vertices(); - } - this.vertexCount = vertices; - } + this.vertexCount = countVertices(cuboids); contents = MemoryBlock.malloc(size()); long ptr = contents.ptr(); @@ -36,6 +34,8 @@ public class ModelPart implements Mesh { vertexList = getVertexType().createVertexList(); vertexList.ptr(ptr); vertexList.setVertexCount(vertexCount); + + boundingSphere = ModelUtil.computeBoundingSphere(vertexList); } public static PartBuilder builder(String name, int sizeU, int sizeV) { @@ -71,4 +71,17 @@ public class ModelPart implements Mesh { public String name() { return name; } + + @Override + public Vector4fc getBoundingSphere() { + return boundingSphere; + } + + private static int countVertices(List cuboids) { + int vertices = 0; + for (PartBuilder.CuboidBuilder cuboid : cuboids) { + vertices += cuboid.vertices(); + } + return vertices; + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java b/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java index 74f958d6a..8f28d9f2d 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java @@ -35,7 +35,7 @@ public class BufferLayout { this.stride = calculateStride(this.attributes) + padding; } - public Collection getAttributes() { + public List getAttributes() { return attributes; } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java b/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java index ecf138451..9a120f6b6 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java @@ -4,6 +4,7 @@ import com.jozufozu.flywheel.api.vertex.MutableVertexList; import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer; import com.jozufozu.flywheel.core.QuadConverter; +import com.jozufozu.flywheel.util.joml.Vector4fc; /** * A holder for arbitrary vertex data that can be written to memory or a vertex list. @@ -12,6 +13,8 @@ public interface Mesh { VertexType getVertexType(); + Vector4fc getBoundingSphere(); + /** * @return The number of vertices this mesh has. */ diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java index 3163b0298..edfbf2ca2 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java @@ -3,17 +3,21 @@ package com.jozufozu.flywheel.core.model; import java.lang.reflect.Field; import java.nio.ByteBuffer; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryUtil; +import com.dreizak.miniball.highdim.Miniball; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.vertex.ReusableVertexList; +import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexListProvider; import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.memory.MemoryBlock; import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.vertex.Formats; +import com.jozufozu.flywheel.util.joml.Vector4f; import com.mojang.blaze3d.vertex.BufferBuilder.DrawState; import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.datafixers.util.Pair; @@ -89,4 +93,12 @@ public class ModelUtil { } return null; } + + @NotNull + public static Vector4f computeBoundingSphere(VertexList reader) { + var miniball = new Miniball(reader); + double[] center = miniball.center(); + double radius = miniball.radius(); + return new Vector4f((float) center[0], (float) center[1], (float) center[2], (float) radius); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/SimpleMesh.java b/src/main/java/com/jozufozu/flywheel/core/model/SimpleMesh.java index d184a05d6..95ce7d8bf 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/SimpleMesh.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/SimpleMesh.java @@ -4,6 +4,8 @@ import com.jozufozu.flywheel.api.vertex.MutableVertexList; import com.jozufozu.flywheel.api.vertex.ReusableVertexList; import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.memory.MemoryBlock; +import com.jozufozu.flywheel.util.joml.Vector4f; +import com.jozufozu.flywheel.util.joml.Vector4fc; public class SimpleMesh implements Mesh { private final VertexType vertexType; @@ -11,6 +13,7 @@ public class SimpleMesh implements Mesh { private final MemoryBlock contents; private final ReusableVertexList vertexList; private final String name; + private final Vector4f boundingSphere; public SimpleMesh(VertexType vertexType, MemoryBlock contents, String name) { this.vertexType = vertexType; @@ -27,6 +30,8 @@ public class SimpleMesh implements Mesh { vertexList = getVertexType().createVertexList(); vertexList.ptr(contents.ptr()); vertexList.setVertexCount(vertexCount); + + boundingSphere = ModelUtil.computeBoundingSphere(vertexList); } @Override @@ -59,6 +64,11 @@ public class SimpleMesh implements Mesh { return name; } + @Override + public Vector4fc getBoundingSphere() { + return boundingSphere; + } + @Override public String toString() { return "SimpleMesh{" + "name='" + name + "',vertexType='" + vertexType + "}"; diff --git a/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java b/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java index e2d74269f..ea4ca0d43 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java @@ -114,7 +114,7 @@ public class FileResolution { ErrorBuilder builder = errorReporter.error(String.format("could not find source for file %s", fileLoc)); for (Span location : neededAt) { builder.pointAtFile(location.getSourceFile()) - .pointAt(location, 1); + .pointAt(location); } } @@ -162,4 +162,25 @@ public class FileResolution { public String toString() { return "FileResolution[" + fileLoc + "]"; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + FileResolution that = (FileResolution) o; + + return fileLoc.equals(that.fileLoc); + } + + @Override + public int hashCode() { + // FileResolutions are interned and therefore can be hashed based on object identity. + // Overriding this to make it explicit. + return System.identityHashCode(this); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/SourceChecks.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceChecks.java index a8247a33e..1d4023705 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/SourceChecks.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceChecks.java @@ -54,4 +54,12 @@ public class SourceChecks { return func; } + + public static BiConsumer checkDefine(String define) { + return (errorReporter, file) -> { +// if (!file.hasDefine(define)) { +// errorReporter.generateMissingDefine(file, define, "\"" + define + "\" define not defined"); +// } + }; + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java index 4e63ef409..faeb46acb 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java @@ -184,24 +184,21 @@ public class SourceFile { return "#use " + '"' + name + '"'; } - public String generateFinalSource(CompilationContext env) { - return generateFinalSource(env, Collections.emptyList()); - } - - public String generateFinalSource(CompilationContext env, List> replacements) { + public String generateFinalSource(CompilationContext context) { + List> replacements = Collections.emptyList(); var out = new StringBuilder(); for (Import include : flattenedImports) { SourceFile file = include.getFile(); - if (file == null || env.contains(file)) { + if (file == null || context.contains(file)) { continue; } - out.append(file.generateLineHeader(env)) + out.append(file.generateLineHeader(context)) .append(file.replaceAndElide(replacements)); } - out.append(this.generateLineHeader(env)) + out.append(this.generateLineHeader(context)) .append(this.replaceAndElide(replacements)); return out.toString(); diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorBuilder.java b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorBuilder.java index 93ab86e84..1a4390e33 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorBuilder.java @@ -88,6 +88,10 @@ public class ErrorBuilder { .pointAt(span, 0); } + public ErrorBuilder pointAt(Span span) { + return pointAt(span, 0); + } + public ErrorBuilder pointAt(Span span, int ctxLines) { if (span.lines() == 1) { @@ -123,7 +127,9 @@ public class ErrorBuilder { for (ErrorLine line : lines) { int length = line.neededMargin(); - if (length > maxLength) maxLength = length; + if (length > maxLength) { + maxLength = length; + } } StringBuilder builder = new StringBuilder(); diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java index 859ec71e2..90224f0e2 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java @@ -3,9 +3,11 @@ package com.jozufozu.flywheel.core.source.error; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.core.source.ShaderLoadingException; import com.jozufozu.flywheel.core.source.SourceFile; import com.jozufozu.flywheel.core.source.parse.ShaderFunction; import com.jozufozu.flywheel.core.source.parse.ShaderStruct; @@ -82,10 +84,12 @@ public class ErrorReporter { return !reportedErrors.isEmpty(); } - public void dump() { - for (var error : reportedErrors) { - Backend.LOGGER.error(error.build()); - } + public ShaderLoadingException dump() { + var allErrors = reportedErrors.stream() + .map(ErrorBuilder::build) + .collect(Collectors.joining()); + + return new ShaderLoadingException(allErrors); } public static void printLines(CharSequence source) { diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java new file mode 100644 index 000000000..17183e8cc --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedStorageWriter.java @@ -0,0 +1,43 @@ +package com.jozufozu.flywheel.core.structs.oriented; + + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.struct.StorageBufferWriter; + +public class OrientedStorageWriter implements StorageBufferWriter { + + public static final OrientedStorageWriter INSTANCE = new OrientedStorageWriter(); + + private OrientedStorageWriter() { + } + + @Override + public void write(final long ptr, OrientedPart d) { + MemoryUtil.memPutFloat(ptr, d.qX); + MemoryUtil.memPutFloat(ptr + 4, d.qY); + MemoryUtil.memPutFloat(ptr + 8, d.qZ); + MemoryUtil.memPutFloat(ptr + 12, d.qW); + + MemoryUtil.memPutFloat(ptr + 16, d.posX); + MemoryUtil.memPutFloat(ptr + 20, d.posY); + MemoryUtil.memPutFloat(ptr + 24, d.posZ); + + MemoryUtil.memPutFloat(ptr + 28, d.pivotX); + MemoryUtil.memPutFloat(ptr + 32, d.pivotY); + MemoryUtil.memPutFloat(ptr + 36, d.pivotZ); + + MemoryUtil.memPutShort(ptr + 40, d.skyLight); + MemoryUtil.memPutShort(ptr + 42, d.blockLight); + + MemoryUtil.memPutByte(ptr + 44, d.r); + MemoryUtil.memPutByte(ptr + 45, d.g); + MemoryUtil.memPutByte(ptr + 46, d.b); + MemoryUtil.memPutByte(ptr + 47, d.a); + } + + @Override + public int getAlignment() { + return 48; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java index 66f2653f5..039fdd1e5 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java @@ -34,11 +34,21 @@ public class OrientedType implements StructType { return OrientedWriter.INSTANCE; } + @Override + public OrientedStorageWriter getStorageBufferWriter() { + return OrientedStorageWriter.INSTANCE; + } + @Override public FileResolution getInstanceShader() { return Components.Files.ORIENTED; } + @Override + public FileResolution getIndirectShader() { + return Components.Files.ORIENTED_INDIRECT; + } + @Override public VertexTransformer getVertexTransformer() { return (vertexList, struct, level) -> { diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java b/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java new file mode 100644 index 000000000..a9f370442 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedStorageWriter.java @@ -0,0 +1,31 @@ +package com.jozufozu.flywheel.core.structs.transformed; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.struct.StorageBufferWriter; +import com.jozufozu.flywheel.extension.MatrixWrite; + +public class TransformedStorageWriter implements StorageBufferWriter { + + public static final TransformedStorageWriter INSTANCE = new TransformedStorageWriter(); + + private TransformedStorageWriter() { + } + + @Override + public void write(long ptr, TransformedPart instance) { + MatrixWrite.writeUnsafe(instance.model, ptr); + MatrixWrite.writeUnsafe(instance.normal, ptr + 64); + MemoryUtil.memPutByte(ptr + 100, instance.r); + MemoryUtil.memPutByte(ptr + 101, instance.g); + MemoryUtil.memPutByte(ptr + 102, instance.b); + MemoryUtil.memPutByte(ptr + 103, instance.a); + MemoryUtil.memPutShort(ptr + 104, instance.skyLight); + MemoryUtil.memPutShort(ptr + 106, instance.blockLight); + } + + @Override + public int getAlignment() { + return 108; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedType.java b/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedType.java index 8eb36eeb6..aa6ba75d9 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedType.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedType.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.core.structs.transformed; +import com.jozufozu.flywheel.api.struct.StorageBufferWriter; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.core.Components; @@ -31,11 +32,21 @@ public class TransformedType implements StructType { return TransformedWriter.INSTANCE; } + @Override + public StorageBufferWriter getStorageBufferWriter() { + return TransformedStorageWriter.INSTANCE; + } + @Override public FileResolution getInstanceShader() { return Components.Files.TRANSFORMED; } + @Override + public FileResolution getIndirectShader() { + return Components.Files.TRANSFORMED_INDIRECT; + } + @Override public VertexTransformer getVertexTransformer() { return (vertexList, struct, level) -> { diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedWriterUnsafe.java new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/FogProvider.java b/src/main/java/com/jozufozu/flywheel/core/uniform/FogProvider.java index 29873074b..05cfdc8b3 100644 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/FogProvider.java +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/FogProvider.java @@ -10,7 +10,7 @@ import com.mojang.blaze3d.systems.RenderSystem; public class FogProvider extends UniformProvider { @Override - public int getSize() { + public int getActualByteSize() { return 16 + 8 + 4; } diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java b/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java new file mode 100644 index 000000000..0f8346cad --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java @@ -0,0 +1,59 @@ +package com.jozufozu.flywheel.core.uniform; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.uniform.UniformProvider; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; +import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.RenderContext; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.event.BeginFrameEvent; + +import net.minecraft.core.Vec3i; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.MinecraftForge; + +public class FrustumProvider extends UniformProvider { + + public static boolean PAUSED = false; + public static boolean CAPTURE = false; + + public FrustumProvider() { + MinecraftForge.EVENT_BUS.addListener(this::beginFrame); + } + + @Override + public int getActualByteSize() { + return 96; + } + + @Override + public FileResolution getUniformShader() { + return Components.Files.FRUSTUM_UNIFORMS; + } + + public void beginFrame(BeginFrameEvent event) { + update(event.getContext()); + } + + public void update(RenderContext context) { + if (ptr == MemoryUtil.NULL || (PAUSED && !CAPTURE)) { + return; + } + + Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(context.level()); + Vec3 camera = context.camera() + .getPosition(); + + var camX = (float) (camera.x - originCoordinate.getX()); + var camY = (float) (camera.y - originCoordinate.getY()); + var camZ = (float) (camera.z - originCoordinate.getZ()); + + var shiftedCuller = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ); + + shiftedCuller.getJozuPackedPlanes(ptr); + + notifier.signalChanged(); + CAPTURE = false; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/UniformBuffer.java b/src/main/java/com/jozufozu/flywheel/core/uniform/UniformBuffer.java index 39ce69a94..e3ecee4ab 100644 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/UniformBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/UniformBuffer.java @@ -45,11 +45,11 @@ public class UniformBuffer { int totalBytes = 0; int index = 0; for (UniformProvider provider : providers) { - int size = provider.getSize(); + int size = alignPo2(provider.getActualByteSize(), 16); builder.add(new Allocated(provider, totalBytes, size, index)); - totalBytes = align(totalBytes + size); + totalBytes = alignUniformBuffer(totalBytes + size); index++; } @@ -80,7 +80,7 @@ public class UniformBuffer { } // https://stackoverflow.com/questions/3407012/rounding-up-to-the-nearest-multiple-of-a-number - private static int align(int numToRound) { + private static int alignUniformBuffer(int numToRound) { if (PO2_ALIGNMENT) { return (numToRound + OFFSET_ALIGNMENT - 1) & -OFFSET_ALIGNMENT; } else { @@ -88,6 +88,10 @@ public class UniformBuffer { } } + private static int alignPo2(int numToRound, int alignment) { + return (numToRound + alignment - 1) & -alignment; + } + private class Allocated implements UniformProvider.Notifier { private final UniformProvider provider; private final int offset; diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java b/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java index a1d844de1..89175710e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java @@ -26,7 +26,7 @@ public class ViewProvider extends UniformProvider { } @Override - public int getSize() { + public int getActualByteSize() { return 4 * 16 + 16 + 4; } @@ -56,7 +56,7 @@ public class ViewProvider extends UniformProvider { MemoryUtil.memPutFloat(ptr + 64, camX); MemoryUtil.memPutFloat(ptr + 68, camY); MemoryUtil.memPutFloat(ptr + 72, camZ); - MemoryUtil.memPutInt(ptr + 80, constantAmbientLight); + MemoryUtil.memPutInt(ptr + 76, constantAmbientLight); notifier.signalChanged(); } diff --git a/src/main/java/com/jozufozu/flywheel/extension/Matrix3fExtension.java b/src/main/java/com/jozufozu/flywheel/extension/Matrix3fExtension.java new file mode 100644 index 000000000..ef8ce3cc6 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/extension/Matrix3fExtension.java @@ -0,0 +1,16 @@ +package com.jozufozu.flywheel.extension; + +import com.jozufozu.flywheel.util.joml.Matrix3f; + +public interface Matrix3fExtension { + + Matrix3f flywheel$store(Matrix3f matrix); + + static Matrix3f clone(com.mojang.math.Matrix3f moj) { + return ((Matrix3fExtension)(Object) moj).flywheel$store(new Matrix3f()); + } + + static void store(com.mojang.math.Matrix3f moj, Matrix3f joml) { + ((Matrix3fExtension)(Object) moj).flywheel$store(joml); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/extension/Matrix4fExtension.java b/src/main/java/com/jozufozu/flywheel/extension/Matrix4fExtension.java new file mode 100644 index 000000000..ea77ca703 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/extension/Matrix4fExtension.java @@ -0,0 +1,16 @@ +package com.jozufozu.flywheel.extension; + +import com.jozufozu.flywheel.util.joml.Matrix4f; + +public interface Matrix4fExtension { + + Matrix4f flywheel$store(Matrix4f matrix); + + static Matrix4f clone(com.mojang.math.Matrix4f moj) { + return ((Matrix4fExtension)(Object) moj).flywheel$store(new Matrix4f()); + } + + static void store(com.mojang.math.Matrix4f moj, Matrix4f joml) { + ((Matrix4fExtension)(Object) moj).flywheel$store(joml); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/extension/MatrixWrite.java b/src/main/java/com/jozufozu/flywheel/extension/MatrixWrite.java index 90747af22..15dead550 100644 --- a/src/main/java/com/jozufozu/flywheel/extension/MatrixWrite.java +++ b/src/main/java/com/jozufozu/flywheel/extension/MatrixWrite.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.extension; import java.nio.ByteBuffer; +import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; /** @@ -24,4 +25,8 @@ public interface MatrixWrite { static void writeUnsafe(Matrix4f matrix, long ptr) { ((MatrixWrite) (Object) matrix).flywheel$writeUnsafe(ptr); } + + static void writeUnsafe(Matrix3f matrix, long ptr) { + ((MatrixWrite) (Object) matrix).flywheel$writeUnsafe(ptr); + } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/ClientMainMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/ClientMainMixin.java new file mode 100644 index 000000000..49f7d0abe --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/ClientMainMixin.java @@ -0,0 +1,27 @@ +package com.jozufozu.flywheel.mixin; + +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 net.minecraft.client.main.Main; + +@Mixin(Main.class) +public class ClientMainMixin { + @Inject(method = "main", at = @At("HEAD")) + private static void injectRenderDoc(CallbackInfo ci) { + // Only try to load RenderDoc if a system property is set. + if (System.getProperty("flw.loadRenderDoc") == null) { + return; + } + + try { + System.loadLibrary("renderdoc"); + } catch (Throwable ignored) { + // Oh well, we tried. + // On Windows, RenderDoc installs to "C:\Program Files\RenderDoc\" + System.err.println("Is RenderDoc in your PATH?"); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java index 5e843dd60..e248e1f59 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java @@ -26,6 +26,7 @@ import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.RenderBuffers; +import net.minecraft.world.phys.Vec3; import net.minecraftforge.common.MinecraftForge; @Mixin(value = LevelRenderer.class, priority = 1001) // Higher priority to go after Sodium @@ -42,7 +43,10 @@ public class LevelRendererMixin { @Inject(at = @At("HEAD"), method = "renderLevel") private void flywheel$beginRender(PoseStack pPoseStack, float pPartialTick, long pFinishNanoTime, boolean pRenderBlockOutline, Camera pCamera, GameRenderer pGameRenderer, LightTexture pLightTexture, Matrix4f pProjectionMatrix, CallbackInfo ci) { - flywheel$renderContext = new RenderContext((LevelRenderer) (Object) this, level, pPoseStack, RenderContext.createViewProjection(pPoseStack, pProjectionMatrix), pProjectionMatrix, renderBuffers, pCamera); + var viewProjection = RenderContext.createViewProjection(pPoseStack, pProjectionMatrix); + var cameraPos = pCamera.getPosition(); + var culler = RenderContext.createCuller(viewProjection, (float) -cameraPos.x, (float) -cameraPos.y, (float) -cameraPos.z); + flywheel$renderContext = new RenderContext((LevelRenderer) (Object) this, level, pPoseStack, viewProjection, pProjectionMatrix, renderBuffers, pCamera, culler); try (var restoreState = GlStateTracker.getRestoreState()) { MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(flywheel$renderContext)); diff --git a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java index f986dee92..2690b8f99 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java @@ -6,11 +6,12 @@ import org.lwjgl.system.MemoryUtil; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import com.jozufozu.flywheel.extension.Matrix3fExtension; import com.jozufozu.flywheel.extension.MatrixWrite; import com.mojang.math.Matrix3f; @Mixin(Matrix3f.class) -public abstract class Matrix3fMixin implements MatrixWrite { +public abstract class Matrix3fMixin implements MatrixWrite, Matrix3fExtension { @Shadow protected float m00; @Shadow protected float m01; @Shadow protected float m02; @@ -46,4 +47,9 @@ public abstract class Matrix3fMixin implements MatrixWrite { buffer.putFloat(m12); buffer.putFloat(m22); } + + @Override + public com.jozufozu.flywheel.util.joml.Matrix3f flywheel$store(com.jozufozu.flywheel.util.joml.Matrix3f matrix) { + return matrix.set(m00, m10, m20, m01, m11, m21, m02, m12, m22); + } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java index 6a5f62459..542e71929 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java @@ -6,11 +6,12 @@ import org.lwjgl.system.MemoryUtil; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import com.jozufozu.flywheel.extension.Matrix4fExtension; import com.jozufozu.flywheel.extension.MatrixWrite; import com.mojang.math.Matrix4f; @Mixin(Matrix4f.class) -public abstract class Matrix4fMixin implements MatrixWrite { +public abstract class Matrix4fMixin implements MatrixWrite, Matrix4fExtension { @Shadow protected float m00; @Shadow protected float m01; @Shadow protected float m02; @@ -67,4 +68,13 @@ public abstract class Matrix4fMixin implements MatrixWrite { buf.putFloat(m23); buf.putFloat(m33); } + + @Override + public com.jozufozu.flywheel.util.joml.Matrix4f flywheel$store(com.jozufozu.flywheel.util.joml.Matrix4f matrix) { + return matrix.set( + m00, m10, m20, m30, + m01, m11, m21, m31, + m02, m12, m22, m32, + m03, m13, m23, m33); + } } diff --git a/src/main/java/com/jozufozu/flywheel/util/Lazy.java b/src/main/java/com/jozufozu/flywheel/util/Lazy.java index a1fdd660e..2945da562 100644 --- a/src/main/java/com/jozufozu/flywheel/util/Lazy.java +++ b/src/main/java/com/jozufozu/flywheel/util/Lazy.java @@ -26,6 +26,10 @@ public class Lazy implements Supplier { return value; } + public boolean isInitialized() { + return value != null; + } + public Lazy lazyMap(Function func) { return new Lazy<>(() -> func.apply(get())); } diff --git a/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java b/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java index a85b90676..4e4881aaf 100644 --- a/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java +++ b/src/main/java/com/jozufozu/flywheel/util/joml/FrustumIntersection.java @@ -23,6 +23,9 @@ */ package com.jozufozu.flywheel.util.joml; +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; /** * Efficiently performs frustum intersection tests by caching the frustum planes of an arbitrary transformation {@link Matrix4fc matrix}. *

@@ -950,4 +953,94 @@ public class FrustumIntersection { return da >= 0.0f || db >= 0.0f; } + public void getCorners(ByteBuffer buffer) { + + Vector3f scratch = new Vector3f(); + Vector3f result = new Vector3f(); + + long addr = MemoryUtil.memAddress(buffer); + planeIntersect(planes[0], planes[2], planes[4], result, scratch); result.getToAddress(addr); + planeIntersect(planes[0], planes[2], planes[5], result, scratch); result.getToAddress(addr + 12); + planeIntersect(planes[0], planes[3], planes[4], result, scratch); result.getToAddress(addr + 24); + planeIntersect(planes[0], planes[3], planes[5], result, scratch); result.getToAddress(addr + 36); + planeIntersect(planes[1], planes[2], planes[4], result, scratch); result.getToAddress(addr + 48); + planeIntersect(planes[1], planes[2], planes[5], result, scratch); result.getToAddress(addr + 60); + planeIntersect(planes[1], planes[3], planes[4], result, scratch); result.getToAddress(addr + 72); + planeIntersect(planes[1], planes[3], planes[5], result, scratch); result.getToAddress(addr + 84); + } + + private Vector3f planeIntersect(Vector4f a, Vector4f b, Vector4f c, Vector3f result, Vector3f scratch) { + // Formula used + // d1 ( N2 * N3 ) + d2 ( N3 * N1 ) + d3 ( N1 * N2 ) + //P = --------------------------------------------------------------------- + // N1 . ( N2 * N3 ) + // + // Note: N refers to the normal, d refers to the displacement. '.' means dot product. '*' means cross product + + float f = result.set(b.x, b.y, b.z).cross(c.x, c.y, c.z).dot(a.x, a.y, a.z); + + result.set(0); + scratch.set(b.x, b.y, b.z).cross(c.x, c.y, c.z).mul(a.z); + result.add(scratch); + scratch.set(c.x, c.y, c.z).cross(a.x, a.y, a.z).mul(b.z); + result.add(scratch); + scratch.set(a.x, a.y, a.z).cross(b.x, b.y, b.z).mul(c.z); + result.add(scratch); + + return result.div(f); + } + + /** + * Writes the planes of this frustum to the given buffer.

+ * Uses a different format that is friendly towards an optimized instruction-parallel + * implementation of sphere-frustum intersection.

+ * The format is as follows:

+ * {@code vec4(nxX, pxX, nyX, pyX)}
+ * {@code vec4(nxY, pxY, nyY, pyY)}
+ * {@code vec4(nxZ, pxZ, nyZ, pyZ)}
+ * {@code vec4(nxW, pxW, nyW, pyW)}
+ * {@code vec2(nzX, pzX)}
+ * {@code vec2(nzY, pzY)}
+ * {@code vec2(nzZ, pzZ)}
+ * {@code vec2(nzW, pzW)}
+ * + * @param addr The buffer to write the planes to. + */ + public void getJozuPackedPlanes(long addr) { + MemoryUtil.memPutFloat(addr, nxX); + MemoryUtil.memPutFloat(addr + 4, pxX); + MemoryUtil.memPutFloat(addr + 8, nyX); + MemoryUtil.memPutFloat(addr + 12, pyX); + MemoryUtil.memPutFloat(addr + 16, nxY); + MemoryUtil.memPutFloat(addr + 20, pxY); + MemoryUtil.memPutFloat(addr + 24, nyY); + MemoryUtil.memPutFloat(addr + 28, pyY); + MemoryUtil.memPutFloat(addr + 32, nxZ); + MemoryUtil.memPutFloat(addr + 36, pxZ); + MemoryUtil.memPutFloat(addr + 40, nyZ); + MemoryUtil.memPutFloat(addr + 44, pyZ); + MemoryUtil.memPutFloat(addr + 48, nxW); + MemoryUtil.memPutFloat(addr + 52, pxW); + MemoryUtil.memPutFloat(addr + 56, nyW); + MemoryUtil.memPutFloat(addr + 60, pyW); + MemoryUtil.memPutFloat(addr + 64, nzX); + MemoryUtil.memPutFloat(addr + 68, pzX); + MemoryUtil.memPutFloat(addr + 72, nzY); + MemoryUtil.memPutFloat(addr + 76, pzY); + MemoryUtil.memPutFloat(addr + 80, nzZ); + MemoryUtil.memPutFloat(addr + 84, pzZ); + MemoryUtil.memPutFloat(addr + 88, nzW); + MemoryUtil.memPutFloat(addr + 92, pzW); + } + + public void getPlanes(ByteBuffer buffer) { + long addr = MemoryUtil.memAddress(buffer); + + planes[0].getToAddress(addr); + planes[1].getToAddress(addr + 16); + planes[2].getToAddress(addr + 32); + planes[3].getToAddress(addr + 48); + planes[4].getToAddress(addr + 64); + planes[5].getToAddress(addr + 80); + } } diff --git a/src/main/java/com/jozufozu/flywheel/util/joml/Quaternionf.java b/src/main/java/com/jozufozu/flywheel/util/joml/Quaternionf.java index f783ca20e..ad2ef13cd 100644 --- a/src/main/java/com/jozufozu/flywheel/util/joml/Quaternionf.java +++ b/src/main/java/com/jozufozu/flywheel/util/joml/Quaternionf.java @@ -32,6 +32,8 @@ import java.nio.FloatBuffer; import java.text.DecimalFormat; import java.text.NumberFormat; +import com.mojang.math.Quaternion; + /** * Quaternion of 4 single-precision floats which can represent rotation and uniform scaling. * @@ -130,6 +132,13 @@ public class Quaternionf implements Externalizable, Cloneable, Quaternionfc { w = cos; } + public Quaternionf(Quaternion moj) { + x = moj.i(); + y = moj.j(); + z = moj.k(); + w = moj.r(); + } + /** * @return the first component of the vector part */ diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/effect/ExampleEffect.java b/src/main/java/com/jozufozu/flywheel/vanilla/effect/ExampleEffect.java index c0adede2e..60cfa49fa 100644 --- a/src/main/java/com/jozufozu/flywheel/vanilla/effect/ExampleEffect.java +++ b/src/main/java/com/jozufozu/flywheel/vanilla/effect/ExampleEffect.java @@ -18,6 +18,7 @@ import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.ImmutableBox; +import com.jozufozu.flywheel.util.joml.FrustumIntersection; import com.jozufozu.flywheel.util.joml.Vector3f; import net.minecraft.client.Minecraft; @@ -297,5 +298,10 @@ public class ExampleEffect implements Effect { public boolean decreaseFramerateWithDistance() { return false; } + + @Override + public boolean checkFrustum(FrustumIntersection frustum) { + return true; + } } } diff --git a/src/main/resources/assets/flywheel/flywheel/api/cull.glsl b/src/main/resources/assets/flywheel/flywheel/api/cull.glsl new file mode 100644 index 000000000..d89bebbf3 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/api/cull.glsl @@ -0,0 +1,4 @@ +#ifdef COMPUTE_SHADER +uint flw_objectID; +uint flw_batchID; +#endif diff --git a/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl b/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl index 735f7a84e..11be92a1e 100644 --- a/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl +++ b/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl @@ -1,3 +1,4 @@ +#ifdef FRAGMENT_SHADER in vec4 flw_vertexPos; in vec4 flw_vertexColor; in vec2 flw_vertexTexCoord; @@ -32,3 +33,4 @@ vec4 flw_fogFilter(vec4 color); * Guard calls with FLW_DISCARD */ bool flw_discardPredicate(vec4 finalColor); +#endif diff --git a/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl b/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl index c5d670ec3..c00b99cbd 100644 --- a/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl +++ b/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl @@ -1,3 +1,4 @@ +#ifdef VERTEX_SHADER out vec4 flw_vertexPos; out vec4 flw_vertexColor; out vec2 flw_vertexTexCoord; @@ -11,3 +12,4 @@ out vec4 flw_var0; out vec4 flw_var1; out vec4 flw_var2; out vec4 flw_var3; +#endif diff --git a/src/main/resources/assets/flywheel/flywheel/compute/sphere_cull_frustum_experiment.glsl b/src/main/resources/assets/flywheel/flywheel/compute/sphere_cull_frustum_experiment.glsl new file mode 100644 index 000000000..43ef57f8b --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/compute/sphere_cull_frustum_experiment.glsl @@ -0,0 +1,72 @@ +#version 450 +#define FLW_SUBGROUP_SIZE 32 + +layout(local_size_x = FLW_SUBGROUP_SIZE) in; + + +// in uvec3 gl_NumWorkGroups; +// in uvec3 gl_WorkGroupID; +// in uvec3 gl_LocalInvocationID; +// in uvec3 gl_GlobalInvocationID; +// in uint gl_LocalInvocationIndex; + +layout(std430, binding = 0) buffer Frustum1 { + vec4 a1; // vec4(nx.x, px.x, ny.x, py.x) + vec4 a2; // vec4(nx.y, px.y, ny.y, py.y) + vec4 a3; // vec4(nx.z, px.z, ny.z, py.z) + vec4 a4; // vec4(nx.w, px.w, ny.w, py.w) + vec2 b1; // vec2(nz.x, pz.x) + vec2 b2; // vec2(nz.y, pz.y) + vec2 b3; // vec2(nz.z, pz.z) + vec2 b4; // vec2(nz.w, pz.w) +} frustum1; + +layout(binding = 1) buffer Frustum2 { + vec4 nx; + vec4 px; + vec4 ny; + vec4 py; + vec4 nz; + vec4 pz; +} frustum2; + +layout(binding = 2) buffer Result { + bool res1; + bool res2; + bool res3; +} result; + +// 83 - 27 = 56 spirv instruction results +bool testSphere1(vec4 sphere) { + return + all(lessThanEqual(fma(frustum1.a1, sphere.xxxx, fma(frustum1.a2, sphere.yyyy, fma(frustum1.a3, sphere.zzzz, frustum1.a4))), -sphere.wwww)) && + all(lessThanEqual(fma(frustum1.b1, sphere.xx, fma(frustum1.b2, sphere.yy, fma(frustum1.b3, sphere.zz, frustum1.b4))), -sphere.ww)); +} + +// 236 - 92 = 144 spirv instruction results +bool testSphere2(vec4 sphere) { + return + fma(frustum2.nx.x, sphere.x, fma(frustum2.nx.y, sphere.y, fma(frustum2.nx.z, sphere.z, frustum2.nx.w))) >= -sphere.w && + fma(frustum2.px.x, sphere.x, fma(frustum2.px.y, sphere.y, fma(frustum2.px.z, sphere.z, frustum2.px.w))) >= -sphere.w && + fma(frustum2.ny.x, sphere.x, fma(frustum2.ny.y, sphere.y, fma(frustum2.ny.z, sphere.z, frustum2.ny.w))) >= -sphere.w && + fma(frustum2.py.x, sphere.x, fma(frustum2.py.y, sphere.y, fma(frustum2.py.z, sphere.z, frustum2.py.w))) >= -sphere.w && + fma(frustum2.nz.x, sphere.x, fma(frustum2.nz.y, sphere.y, fma(frustum2.nz.z, sphere.z, frustum2.nz.w))) >= -sphere.w && + fma(frustum2.pz.x, sphere.x, fma(frustum2.pz.y, sphere.y, fma(frustum2.pz.z, sphere.z, frustum2.pz.w))) >= -sphere.w; +} + +// 322 - 240 = 82 spirv instruction results +bool testSphere3(vec4 sphere) { + return + (dot(frustum2.nx.xyz, sphere.xyz) + frustum2.nx.w) >= -sphere.w && + (dot(frustum2.px.xyz, sphere.xyz) + frustum2.px.w) >= -sphere.w && + (dot(frustum2.ny.xyz, sphere.xyz) + frustum2.ny.w) >= -sphere.w && + (dot(frustum2.py.xyz, sphere.xyz) + frustum2.py.w) >= -sphere.w && + (dot(frustum2.nz.xyz, sphere.xyz) + frustum2.nz.w) >= -sphere.w && + (dot(frustum2.pz.xyz, sphere.xyz) + frustum2.pz.w) >= -sphere.w; +} + +void main() { + result.res1 = testSphere1(vec4(0., 1., 0., 1.)); + result.res2 = testSphere2(vec4(0., 1., 0., 1.)); + result.res3 = testSphere3(vec4(0., 1., 0., 1.)); +} diff --git a/src/main/resources/assets/flywheel/flywheel/debug/debug.frag b/src/main/resources/assets/flywheel/flywheel/debug/debug.frag new file mode 100644 index 000000000..ab41dc91f --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/debug/debug.frag @@ -0,0 +1,5 @@ +out vec4 fragColor; + +void main() { + fragColor = vec4(1.0, 1.0, 1.0, 0.2); +} diff --git a/src/main/resources/assets/flywheel/flywheel/debug/debug.vert b/src/main/resources/assets/flywheel/flywheel/debug/debug.vert new file mode 100644 index 000000000..6a5d60ca1 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/debug/debug.vert @@ -0,0 +1,7 @@ +#use "flywheel:uniform/view.glsl" + +layout(location = 0) in vec3 worldPos; + +void main() { + gl_Position = flw_viewProjection * vec4(worldPos, 1.0); +} diff --git a/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert b/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert index 93a50ddee..880d55de4 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert +++ b/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert @@ -1,11 +1,11 @@ #use "flywheel:api/vertex.glsl" #use "flywheel:util/quaternion.glsl" -layout(location = 0) in ivec2 oriented_light; -layout(location = 1) in vec4 oriented_color; -layout(location = 2) in vec3 oriented_pos; -layout(location = 3) in vec3 oriented_pivot; -layout(location = 4) in vec4 oriented_rotation; +layout(location = FLW_INSTANCE_BASE_INDEX + 0) in ivec2 oriented_light; +layout(location = FLW_INSTANCE_BASE_INDEX + 1) in vec4 oriented_color; +layout(location = FLW_INSTANCE_BASE_INDEX + 2) in vec3 oriented_pos; +layout(location = FLW_INSTANCE_BASE_INDEX + 3) in vec3 oriented_pivot; +layout(location = FLW_INSTANCE_BASE_INDEX + 4) in vec4 oriented_rotation; void flw_instanceVertex() { flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - oriented_pivot, oriented_rotation) + oriented_pivot + oriented_pos, 1.0); diff --git a/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl b/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl new file mode 100644 index 000000000..cd0f90fb2 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/instance/oriented_indirect.glsl @@ -0,0 +1,32 @@ +#use "flywheel:api/vertex.glsl" +#use "flywheel:util/quaternion.glsl" +#use "flywheel:util/types.glsl" + +#define FLW_INSTANCE_STRUCT Instance +struct Instance { + Vec4F rotation; + Vec3F pos; + Vec3F pivot; + uint light; + uint color; +}; + +void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) { + vec4 rotation = unpackVec4F(i.rotation); + vec3 pivot = unpackVec3F(i.pivot); + vec3 pos = unpackVec3F(i.pos); + + center = rotateVertexByQuat(center - pivot, rotation) + pivot + pos; +} + +#ifdef VERTEX_SHADER +void flw_instanceVertex(Instance i) { + vec4 rotation = unpackVec4F(i.rotation); + vec3 pivot = unpackVec3F(i.pivot); + vec3 pos = unpackVec3F(i.pos); + flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - pivot, rotation) + pivot + pos, 1.0); + flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, rotation); + flw_vertexColor = unpackUnorm4x8(i.color); + flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0; +} + #endif diff --git a/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert b/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert index e2ea23ef4..2c6161d18 100644 --- a/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert +++ b/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert @@ -1,9 +1,9 @@ #use "flywheel:api/vertex.glsl" -layout(location = 0) in ivec2 transformed_light; -layout(location = 1) in vec4 transformed_color; -layout(location = 2) in mat4 transformed_pose; -layout(location = 6) in mat3 transformed_normal; +layout(location = FLW_INSTANCE_BASE_INDEX + 0) in ivec2 transformed_light; +layout(location = FLW_INSTANCE_BASE_INDEX + 1) in vec4 transformed_color; +layout(location = FLW_INSTANCE_BASE_INDEX + 2) in mat4 transformed_pose; +layout(location = FLW_INSTANCE_BASE_INDEX + 6) in mat3 transformed_normal; void flw_instanceVertex() { flw_vertexPos = transformed_pose * flw_vertexPos; diff --git a/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl new file mode 100644 index 000000000..f0067c4b9 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/instance/transformed_indirect.glsl @@ -0,0 +1,27 @@ +#use "flywheel:api/vertex.glsl" +#use "flywheel:util/types.glsl" + +#define FLW_INSTANCE_STRUCT Instance +struct Instance { + Mat4F pose; + Mat3F normal; + uint color; + uint light; +}; + +void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) { + mat4 pose = unpackMat4F(i.pose); + center = (pose * vec4(center, 1.0)).xyz; + + float scale = max(length(pose[0].xyz), max(length(pose[1].xyz), length(pose[2].xyz))); + radius *= scale; +} + + #ifdef VERTEX_SHADER +void flw_instanceVertex(Instance i) { + flw_vertexPos = unpackMat4F(i.pose) * flw_vertexPos; + flw_vertexNormal = unpackMat3F(i.normal) * flw_vertexNormal; + flw_vertexColor = unpackUnorm4x8(i.color); + flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0; +} + #endif diff --git a/src/main/resources/assets/flywheel/flywheel/layout/block.vert b/src/main/resources/assets/flywheel/flywheel/layout/block.vert index a3af3b16d..4123724aa 100644 --- a/src/main/resources/assets/flywheel/flywheel/layout/block.vert +++ b/src/main/resources/assets/flywheel/flywheel/layout/block.vert @@ -5,6 +5,7 @@ layout(location = 1) in vec4 _flw_v_color; layout(location = 2) in vec2 _flw_v_texCoord; layout(location = 3) in ivec2 _flw_v_light; layout(location = 4) in vec3 _flw_v_normal; +#define FLW_INSTANCE_BASE_INDEX 5 void flw_layoutVertex() { flw_vertexPos = vec4(_flw_v_pos, 1.0); diff --git a/src/main/resources/assets/flywheel/flywheel/layout/pos_tex_normal.vert b/src/main/resources/assets/flywheel/flywheel/layout/pos_tex_normal.vert index 4ecfb2c3c..80b02f8bc 100644 --- a/src/main/resources/assets/flywheel/flywheel/layout/pos_tex_normal.vert +++ b/src/main/resources/assets/flywheel/flywheel/layout/pos_tex_normal.vert @@ -3,6 +3,7 @@ layout(location = 0) in vec3 _flw_v_pos; layout(location = 1) in vec2 _flw_v_texCoord; layout(location = 2) in vec3 _flw_v_normal; +#define FLW_INSTANCE_BASE_INDEX 3 void flw_layoutVertex() { flw_vertexPos = vec4(_flw_v_pos, 1.0); diff --git a/src/main/resources/assets/flywheel/flywheel/pipeline/draw.frag b/src/main/resources/assets/flywheel/flywheel/pipeline/draw.frag new file mode 100644 index 000000000..f57dd9a87 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/draw.frag @@ -0,0 +1,7 @@ +#use "flywheel:api/fragment.glsl" + +void main() { + flw_initFragment(); + flw_materialFragment(); + flw_contextFragment(); +} diff --git a/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl new file mode 100644 index 000000000..b1706edf0 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl @@ -0,0 +1,68 @@ +#define FLW_SUBGROUP_SIZE 32 +layout(local_size_x = FLW_SUBGROUP_SIZE) in; +#use "flywheel:api/cull.glsl" +#use "flywheel:uniform/frustum.glsl" +#use "flywheel:util/types.glsl" + +struct MeshDrawCommand { + uint indexCount; + uint instanceCount; + uint firstIndex; + uint vertexOffset; + uint baseInstance; + + BoundingSphere boundingSphere; +}; + +// populated by instancers +layout(std430, binding = 0) restrict readonly buffer ObjectBuffer { + FLW_INSTANCE_STRUCT objects[]; +}; + +layout(std430, binding = 1) restrict writeonly buffer TargetBuffer { + uint objectIDs[]; +}; + +layout(std430, binding = 2) restrict readonly buffer BatchBuffer { + uint batchIDs[]; +}; + +layout(std430, binding = 3) restrict buffer DrawCommands { + MeshDrawCommand drawCommands[]; +}; + +// 83 - 27 = 56 spirv instruction results +bool testSphere(vec3 center, float radius) { + bvec4 xyInside = greaterThanEqual(fma(flw_planes.xyX, center.xxxx, fma(flw_planes.xyY, center.yyyy, fma(flw_planes.xyZ, center.zzzz, flw_planes.xyW))), -radius.xxxx); + bvec2 zInside = greaterThanEqual(fma(flw_planes.zX, center.xx, fma(flw_planes.zY, center.yy, fma(flw_planes.zZ, center.zz, flw_planes.zW))), -radius.xx); + + return all(xyInside) && all(zInside); +} + +bool isVisible() { + BoundingSphere sphere = drawCommands[flw_batchID].boundingSphere; + + vec3 center; + float radius; + unpackBoundingSphere(sphere, center, radius); + flw_transformBoundingSphere(objects[flw_objectID], center, radius); + + return testSphere(center, radius); +} + +void main() { + flw_objectID = gl_GlobalInvocationID.x; + + if (flw_objectID >= objects.length()) { + return; + } + + flw_batchID = batchIDs[flw_objectID]; + + if (isVisible()) { + uint batchIndex = atomicAdd(drawCommands[flw_batchID].instanceCount, 1); + uint globalIndex = drawCommands[flw_batchID].baseInstance + batchIndex; + + objectIDs[globalIndex] = flw_objectID; + } +} diff --git a/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw.vert b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw.vert new file mode 100644 index 000000000..7583ebbd6 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw.vert @@ -0,0 +1,18 @@ +#use "flywheel:api/vertex.glsl" + +layout(std430, binding = 0) restrict readonly buffer ObjectBuffer { + FLW_INSTANCE_STRUCT objects[]; +}; + +layout(std430, binding = 1) restrict readonly buffer TargetBuffer { + uint objectIDs[]; +}; + +void main() { + uint instanceIndex = objectIDs[gl_BaseInstance + gl_InstanceID]; + flw_layoutVertex(); + FLW_INSTANCE_STRUCT i = objects[instanceIndex]; + flw_instanceVertex(i); + flw_materialVertex(); + flw_contextVertex(); +} diff --git a/src/main/resources/assets/flywheel/flywheel/pipeline/instanced_arrays_draw.vert b/src/main/resources/assets/flywheel/flywheel/pipeline/instanced_arrays_draw.vert new file mode 100644 index 000000000..0b75edde3 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/instanced_arrays_draw.vert @@ -0,0 +1,8 @@ +#use "flywheel:api/vertex.glsl" + +void main() { + flw_layoutVertex(); + flw_instanceVertex(); + flw_materialVertex(); + flw_contextVertex(); +} diff --git a/src/main/resources/assets/flywheel/flywheel/uniform/frustum.glsl b/src/main/resources/assets/flywheel/flywheel/uniform/frustum.glsl new file mode 100644 index 000000000..39866a922 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/uniform/frustum.glsl @@ -0,0 +1,14 @@ +struct FLWPackedPlanes { + vec4 xyX; // + vec4 xyY; // + vec4 xyZ; // + vec4 xyW; // + vec2 zX; // + vec2 zY; // + vec2 zZ; // + vec2 zW; // +}; + +layout(std140, binding = 2) uniform FLWFrustum { + FLWPackedPlanes flw_planes; +}; diff --git a/src/main/resources/assets/flywheel/flywheel/util/types.glsl b/src/main/resources/assets/flywheel/flywheel/util/types.glsl new file mode 100644 index 000000000..3a93dbda4 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/util/types.glsl @@ -0,0 +1,63 @@ +// Types intended for use is SSBOs to achieve tighter data packing. + +struct Vec3F { + float x; + float y; + float z; +}; + +struct Vec4F { + float x; + float y; + float z; + float w; +}; + +struct Mat4F { + Vec4F c0; + Vec4F c1; + Vec4F c2; + Vec4F c3; +}; + +struct Mat3F { + Vec3F c0; + Vec3F c1; + Vec3F c2; +}; + +// 4-aligned instead of a 16-aligned vec4 +struct BoundingSphere { + Vec3F center; + float radius; +}; + +vec3 unpackVec3F(in Vec3F v) { + return vec3(v.x, v.y, v.z); +} + +vec4 unpackVec4F(in Vec4F v) { + return vec4(v.x, v.y, v.z, v.w); +} + +mat4 unpackMat4F(in Mat4F m) { + return mat4( + unpackVec4F(m.c0), + unpackVec4F(m.c1), + unpackVec4F(m.c2), + unpackVec4F(m.c3) + ); +} + +mat3 unpackMat3F(in Mat3F m) { + return mat3( + unpackVec3F(m.c0), + unpackVec3F(m.c1), + unpackVec3F(m.c2) + ); +} + +void unpackBoundingSphere(in BoundingSphere sphere, out vec3 center, out float radius) { + center = unpackVec3F(sphere.center); + radius = sphere.radius; +} diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index f0cf17998..1d26183e7 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -11,6 +11,7 @@ "BufferUploaderMixin", "ChunkRebuildHooksMixin", "ClientLevelMixin", + "ClientMainMixin", "EntityTypeMixin", "FixFabulousDepthMixin", "FogUpdateMixin",