diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java index 28366c73b..5c0e809c7 100644 --- a/src/main/java/com/jozufozu/flywheel/Flywheel.java +++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java @@ -7,14 +7,12 @@ 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.BackendTypes; 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; @@ -81,7 +79,6 @@ public class Flywheel { forgeEventBus.addListener(FlwCommands::registerClientCommands); forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onReloadRenderers); - forgeEventBus.addListener(PipelineCompiler::onReloadRenderers); forgeEventBus.addListener(Models::onReloadRenderers); forgeEventBus.addListener(DrawBuffer::onReloadRenderers); @@ -108,7 +105,6 @@ public class Flywheel { // forgeEventBus.addListener(ExampleEffect::onReload); Components.init(); - DebugRender.init(); VanillaInstances.init(); diff --git a/src/main/java/com/jozufozu/flywheel/api/context/Context.java b/src/main/java/com/jozufozu/flywheel/api/context/Context.java new file mode 100644 index 000000000..0504e7fbf --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/api/context/Context.java @@ -0,0 +1,13 @@ +package com.jozufozu.flywheel.api.context; + +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; + +import net.minecraft.resources.ResourceLocation; + +public interface Context { + void onProgramLink(GlProgram program); + + ResourceLocation vertexShader(); + + ResourceLocation fragmentShader(); +} diff --git a/src/main/java/com/jozufozu/flywheel/api/context/ContextShader.java b/src/main/java/com/jozufozu/flywheel/api/context/ContextShader.java deleted file mode 100644 index 37e9320ed..000000000 --- a/src/main/java/com/jozufozu/flywheel/api/context/ContextShader.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.jozufozu.flywheel.api.context; - -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.core.source.FileResolution; -import com.jozufozu.flywheel.core.source.SourceFile; - -public record ContextShader(GlProgram.Factory factory, FileResolution vertexShader, FileResolution fragmentShader) { - - public SourceFile getVertexShader() { - return vertexShader.getFile(); - } - - public SourceFile getFragmentShader() { - return fragmentShader.getFile(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/api/material/Material.java b/src/main/java/com/jozufozu/flywheel/api/material/Material.java index a44b314ef..f7462c7df 100644 --- a/src/main/java/com/jozufozu/flywheel/api/material/Material.java +++ b/src/main/java/com/jozufozu/flywheel/api/material/Material.java @@ -1,15 +1,15 @@ package com.jozufozu.flywheel.api.material; import com.jozufozu.flywheel.api.vertex.MutableVertexList; -import com.jozufozu.flywheel.core.source.FileResolution; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; public interface Material { - FileResolution getVertexShader(); + ResourceLocation vertexShader(); - FileResolution getFragmentShader(); + ResourceLocation fragmentShader(); void setup(); diff --git a/src/main/java/com/jozufozu/flywheel/api/pipeline/Pipeline.java b/src/main/java/com/jozufozu/flywheel/api/pipeline/Pipeline.java new file mode 100644 index 000000000..ce4761393 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/api/pipeline/Pipeline.java @@ -0,0 +1,28 @@ +package com.jozufozu.flywheel.api.pipeline; + +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.core.SourceComponent; +import com.jozufozu.flywheel.core.source.ShaderSources; + +import net.minecraft.resources.ResourceLocation; + +public interface Pipeline { + + GLSLVersion glslVersion(); + + ResourceLocation vertexShader(); + + ResourceLocation fragmentShader(); + + /** + * Generate the source component necessary to convert a packed {@link StructType} into its shader representation. + * + * @return A source component defining functions that unpack a representation of the given struct type. + */ + SourceComponent assemble(InstanceAssemblerContext context); + + record InstanceAssemblerContext(ShaderSources sources, VertexType vertexType, StructType structType) { + } +} diff --git a/src/main/java/com/jozufozu/flywheel/api/pipeline/PipelineShader.java b/src/main/java/com/jozufozu/flywheel/api/pipeline/PipelineShader.java deleted file mode 100644 index f33c3b006..000000000 --- a/src/main/java/com/jozufozu/flywheel/api/pipeline/PipelineShader.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.jozufozu.flywheel.api.pipeline; - -import java.util.function.BiFunction; - -import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.gl.GLSLVersion; -import com.jozufozu.flywheel.core.SourceComponent; -import com.jozufozu.flywheel.core.source.FileResolution; - -public record PipelineShader(GLSLVersion glslVersion, FileResolution vertex, FileResolution fragment, - InstanceAssemblerFactory factory) { - - /** - * Generate the source component necessary to convert a packed {@link StructType} into its shader representation. - * - * @param structType The struct type to convert. - * @return A source component defining functions that unpack a representation of the given struct type. - */ - public SourceComponent assemble(VertexType vertexType, StructType structType) { - return factory.apply(vertexType, structType); - } - - public interface InstanceAssemblerFactory extends BiFunction, SourceComponent> { - - } -} 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..86689b885 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java @@ -3,9 +3,9 @@ package com.jozufozu.flywheel.api.struct; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.vertex.MutableVertexList; import com.jozufozu.flywheel.core.layout.BufferLayout; -import com.jozufozu.flywheel.core.source.FileResolution; import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.resources.ResourceLocation; /** * A StructType contains metadata for a specific instance struct that Flywheel can interface with. @@ -25,7 +25,7 @@ public interface StructType { StructWriter getWriter(); - FileResolution getInstanceShader(); + ResourceLocation instanceShader(); VertexTransformer getVertexTransformer(); diff --git a/src/main/java/com/jozufozu/flywheel/api/uniform/ShaderUniforms.java b/src/main/java/com/jozufozu/flywheel/api/uniform/ShaderUniforms.java new file mode 100644 index 000000000..535c775a8 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/api/uniform/ShaderUniforms.java @@ -0,0 +1,29 @@ +package com.jozufozu.flywheel.api.uniform; + +import net.minecraft.resources.ResourceLocation; + +public interface ShaderUniforms { + + Provider activate(long ptr); + + ResourceLocation uniformShader(); + + int byteSize(); + + interface Provider { + /** + * Delete this provider.

+ *

+ * Do not free the ptr passed to {@link #activate(long)}.
+ * Clean up other resources, and unsubscribe from events. + */ + void delete(); + + /** + * Poll the provider for changes. + * + * @return {@code true} if the provider updated its backing store. + */ + boolean poll(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/api/uniform/UniformProvider.java b/src/main/java/com/jozufozu/flywheel/api/uniform/UniformProvider.java deleted file mode 100644 index 06716f2bc..000000000 --- a/src/main/java/com/jozufozu/flywheel/api/uniform/UniformProvider.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.jozufozu.flywheel.api.uniform; - -import com.jozufozu.flywheel.core.source.FileResolution; - -public abstract class UniformProvider { - - protected long ptr; - protected Notifier notifier; - - public abstract int getActualByteSize(); - - public void updatePtr(long ptr, Notifier notifier) { - this.ptr = ptr; - this.notifier = notifier; - } - - public abstract FileResolution getUniformShader(); - - public interface Notifier { - void signalChanged(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java index 596ff8aba..cde253249 100644 --- a/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java +++ b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java @@ -1,7 +1,8 @@ package com.jozufozu.flywheel.api.vertex; import com.jozufozu.flywheel.core.layout.BufferLayout; -import com.jozufozu.flywheel.core.source.FileResolution; + +import net.minecraft.resources.ResourceLocation; /** * A vertex type containing metadata about a specific vertex layout. @@ -13,5 +14,13 @@ public interface VertexType extends VertexListProvider { */ BufferLayout getLayout(); - FileResolution getLayoutShader(); + ResourceLocation layoutShader(); + + default int getStride() { + return getLayout().getStride(); + } + + default int byteOffset(int vertexIndex) { + return getStride() * vertexIndex; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index 4f0a523ed..056979f0a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.backend; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -60,6 +61,7 @@ public class Backend { return TYPE != BackendTypes.OFF; } + @Contract("null -> false") public static boolean canUseInstancing(@Nullable Level level) { return isOn() && isFlywheelLevel(level); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/BackendType.java b/src/main/java/com/jozufozu/flywheel/backend/BackendType.java index d7124a3d9..0762e3596 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/BackendType.java +++ b/src/main/java/com/jozufozu/flywheel/backend/BackendType.java @@ -1,6 +1,9 @@ package com.jozufozu.flywheel.backend; +import org.jetbrains.annotations.Nullable; + import com.jozufozu.flywheel.backend.instancing.Engine; +import com.jozufozu.flywheel.core.pipeline.SimplePipeline; import net.minecraft.network.chat.Component; @@ -17,4 +20,6 @@ public interface BackendType { BackendType findFallback(); boolean supported(); + + @Nullable SimplePipeline pipelineShader(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index 2524be426..07c940c10 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -1,23 +1,9 @@ package com.jozufozu.flywheel.backend; -import java.util.List; - -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.struct.StructType; -import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; -import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; -import com.jozufozu.flywheel.backend.instancing.indirect.ComputeCullerCompiler; -import com.jozufozu.flywheel.core.ComponentRegistry; -import com.jozufozu.flywheel.core.Components; -import com.jozufozu.flywheel.core.compile.ShaderCompilationException; -import com.jozufozu.flywheel.core.source.FileResolution; -import com.jozufozu.flywheel.core.source.ShaderLoadingException; +import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler; import com.jozufozu.flywheel.core.source.ShaderSources; import com.jozufozu.flywheel.core.source.error.ErrorReporter; -import com.jozufozu.flywheel.util.StringUtil; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; @@ -37,11 +23,12 @@ public class Loader implements ResourceManagerReloadListener { Loader() { // Can be null when running datagenerators due to the unfortunate time we call this Minecraft minecraft = Minecraft.getInstance(); - if (minecraft != null) { - ResourceManager manager = minecraft.getResourceManager(); - if (manager instanceof ReloadableResourceManager) { - ((ReloadableResourceManager) manager).registerReloadListener(this); - } + if (minecraft == null) { + return; + } + + if (minecraft.getResourceManager() instanceof ReloadableResourceManager reloadable) { + reloadable.registerReloadListener(this); } } @@ -52,62 +39,11 @@ public class Loader implements ResourceManagerReloadListener { var errorReporter = new ErrorReporter(); ShaderSources sources = new ShaderSources(errorReporter, manager); - Backend.LOGGER.info("Loaded all shader sources in " + sources.getLoadTime()); - - FileResolution.run(errorReporter, sources); - - if (errorReporter.hasErrored()) { - throw errorReporter.dump(); + if (FlwCompiler.INSTANCE != null) { + FlwCompiler.INSTANCE.delete(); } - sources.postResolve(); - - Backend.LOGGER.info("Successfully resolved all source files."); - - FileResolution.checkAll(errorReporter); - - Backend.LOGGER.info("All shaders passed checks."); - - long compileStart = System.nanoTime(); - int programCounter = 0; - boolean crash = false; - - for (Material material : ComponentRegistry.materials) { - for (StructType structType : ComponentRegistry.structTypes) { - for (VertexType vertexType : ComponentRegistry.vertexTypes) { - for (ContextShader contextShader : ComponentRegistry.contextShaders) { - for (PipelineShader pipelineShader : List.of(Components.INSTANCED_ARRAYS, Components.INDIRECT)) { - var ctx = new PipelineCompiler.Context(vertexType, material, structType, contextShader, pipelineShader); - try { - PipelineCompiler.INSTANCE.getProgram(ctx); - } catch (ShaderCompilationException e) { - Backend.LOGGER.error(e.errors); - crash = true; - } - programCounter++; - } - } - } - } - } - - for (StructType structType : ComponentRegistry.structTypes) { - try { - ComputeCullerCompiler.INSTANCE.get(structType); - } catch (ShaderCompilationException e) { - Backend.LOGGER.error(e.errors); - crash = true; - } - programCounter++; - } - - long compileEnd = System.nanoTime(); - - Backend.LOGGER.info("Compiled " + programCounter + " programs in " + StringUtil.formatTime(compileEnd - compileStart)); - - if (crash) { - throw new ShaderLoadingException("Compilation failed"); - } + FlwCompiler.INSTANCE = new FlwCompiler(sources); ClientLevel level = Minecraft.getInstance().level; if (Backend.canUseInstancing(level)) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/SimpleBackendType.java b/src/main/java/com/jozufozu/flywheel/backend/SimpleBackendType.java index 1269c08dd..9a4b90056 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/SimpleBackendType.java +++ b/src/main/java/com/jozufozu/flywheel/backend/SimpleBackendType.java @@ -3,8 +3,11 @@ package com.jozufozu.flywheel.backend; import java.util.function.BooleanSupplier; import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; + import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.core.BackendTypes; +import com.jozufozu.flywheel.core.pipeline.SimplePipeline; import net.minecraft.network.chat.Component; @@ -17,14 +20,16 @@ public class SimpleBackendType implements BackendType { private final Supplier engineSupplier; private final Supplier fallback; private final BooleanSupplier isSupported; + private final SimplePipeline pipelineShader; - public SimpleBackendType(String properName, String shortName, Component engineMessage, Supplier engineSupplier, Supplier fallback, BooleanSupplier isSupported) { + public SimpleBackendType(String properName, String shortName, Component engineMessage, Supplier engineSupplier, Supplier fallback, BooleanSupplier isSupported, @Nullable SimplePipeline pipelineShader) { this.properName = properName; this.shortName = shortName; this.engineMessage = engineMessage; this.engineSupplier = engineSupplier; this.fallback = fallback; this.isSupported = isSupported; + this.pipelineShader = pipelineShader; } public static Builder builder() { @@ -66,6 +71,11 @@ public class SimpleBackendType implements BackendType { return isSupported.getAsBoolean(); } + @Override + public @Nullable SimplePipeline pipelineShader() { + return pipelineShader; + } + public static class Builder { private String properName; private String shortName; @@ -73,6 +83,7 @@ public class SimpleBackendType implements BackendType { private Supplier engineSupplier; private Supplier fallback; private BooleanSupplier isSupported; + private SimplePipeline pipelineShader; public Builder properName(String properName) { this.properName = properName; @@ -104,8 +115,13 @@ public class SimpleBackendType implements BackendType { return this; } + public Builder pipelineShader(SimplePipeline pipelineShader) { + this.pipelineShader = pipelineShader; + return this; + } + public BackendType register() { - return BackendTypes.register(new SimpleBackendType(properName, shortName, engineMessage, engineSupplier, fallback, isSupported)); + return BackendTypes.register(new SimpleBackendType(properName, shortName, engineMessage, engineSupplier, fallback, isSupported, pipelineShader)); } } } 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 ea80d4cf5..edc21b543 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 @@ -1,28 +1,22 @@ package com.jozufozu.flywheel.backend.gl.shader; -import static org.lwjgl.opengl.GL20.glDeleteProgram; -import static org.lwjgl.opengl.GL20.glGetUniformLocation; -import static org.lwjgl.opengl.GL20.glUniform1i; -import static org.lwjgl.opengl.GL20.glUniformMatrix4fv; - -import org.jetbrains.annotations.NotNull; +import static org.lwjgl.opengl.GL20.*; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlObject; +import com.jozufozu.flywheel.core.uniform.UniformBuffer; import com.mojang.blaze3d.shaders.ProgramManager; -import net.minecraft.resources.ResourceLocation; - public class GlProgram extends GlObject { - public final ResourceLocation name; - - public GlProgram(ResourceLocation name, int handle) { - this.name = name; + public GlProgram(int handle) { setHandle(handle); } public void bind() { + // TODO: bind textures? + UniformBuffer.getInstance() + .sync(); ProgramManager.glUseProgram(handle()); } @@ -40,7 +34,7 @@ public class GlProgram extends GlObject { int index = glGetUniformLocation(this.handle(), uniform); if (index < 0) { - Backend.LOGGER.debug("No active uniform '{}' exists in program '{}'. Could be unused.", uniform, this.name); + Backend.LOGGER.debug("No active uniform '{}' exists. Could be unused.", uniform); } return index; @@ -51,17 +45,13 @@ public class GlProgram extends GlObject { * * @param name The name of the sampler uniform. * @param binding The index of the texture unit. - * @return The sampler uniform's index. - * @throws NullPointerException If no uniform exists with the given name. */ - public int setSamplerBinding(String name, int binding) { + public void setSamplerBinding(String name, int binding) { int samplerUniform = getUniformLocation(name); if (samplerUniform >= 0) { glUniform1i(samplerUniform, binding); } - - return samplerUniform; } @Override @@ -69,17 +59,4 @@ public class GlProgram extends GlObject { glDeleteProgram(handle); } - @Override - public String toString() { - return "program " + name; - } - - /** - * A factory interface to create a {@link GlProgram}. - */ - public interface Factory { - - @NotNull - GlProgram create(ResourceLocation name, int handle); - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java index a93ec6046..a637b8298 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java @@ -1,38 +1,17 @@ package com.jozufozu.flywheel.backend.gl.shader; -import java.io.File; -import java.io.FileWriter; -import java.util.List; -import java.util.stream.Collectors; - import org.lwjgl.opengl.GL20; -import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlObject; -import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; -import com.jozufozu.flywheel.core.compile.ShaderCompilationException; - -import net.minecraft.client.Minecraft; -import net.minecraft.resources.ResourceLocation; public class GlShader extends GlObject { public final ShaderType type; - private final List parts; + private final String name; - public GlShader(String source, ShaderType type, List parts) throws ShaderCompilationException { - this.parts = parts; + public GlShader(int handle, ShaderType type, String name) { this.type = type; - int handle = GL20.glCreateShader(type.glEnum); - - GlCompat.safeShaderSource(handle, source); - GL20.glCompileShader(handle); - - dumpSource(source, type); - - if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) { - throw new ShaderCompilationException("Could not compile " + getName(), handle); - } + this.name = name; setHandle(handle); } @@ -42,26 +21,9 @@ public class GlShader extends GlObject { GL20.glDeleteShader(handle); } - public String getName() { - return parts.stream() - .map(ResourceLocation::toString) - .map(s -> s.replaceAll("/", "_") - .replaceAll(":", "\\$")) - .collect(Collectors.joining(";")); + @Override + public String toString() { + return "GlShader{" + type.name + handle() + " " + name + "}"; } - private void dumpSource(String source, ShaderType type) { - if (!Backend.DUMP_SHADER_SOURCE) { - return; - } - - File dir = new File(Minecraft.getInstance().gameDirectory, "flywheel_sources"); - dir.mkdirs(); - File file = new File(dir, type.getFileName(getName())); - try (FileWriter writer = new FileWriter(file)) { - writer.write(source); - } catch (Exception e) { - e.printStackTrace(); - } - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/PipelineCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/PipelineCompiler.java deleted file mode 100644 index 5df7ae5b6..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/PipelineCompiler.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing; - -import java.util.LinkedHashSet; -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.struct.StructType; -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.SourceComponent; -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.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.structType.getInstanceShader() - .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 structType The instance shader to use. - * @param contextShader The context shader to use. - */ - public record Context(VertexType vertexType, Material material, StructType structType, - ContextShader contextShader, PipelineShader pipelineShader) { - - ImmutableList getVertexComponents() { - var layout = vertexType.getLayoutShader() - .getFile(); - var instanceAssembly = pipelineShader.assemble(vertexType, structType); - var instance = structType.getInstanceShader() - .getFile(); - var material = this.material.getVertexShader() - .getFile(); - var context = contextShader.getVertexShader(); - var pipeline = pipelineShader.vertex() - .getFile(); - return ImmutableList.of(layout, instanceAssembly, instance, material, context, pipeline); - } - - ImmutableList getFragmentComponents() { - var material = this.material.getFragmentShader() - .getFile(); - var context = contextShader.getFragmentShader(); - var pipeline = pipelineShader.fragment() - .getFile(); - return ImmutableList.of(material, context, pipeline); - } - } - - /** - * 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(key.generateHeader()); - finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n"); - finalSource.append("#extension GL_ARB_conservative_depth : enable\n"); - - var ctx = new CompilationContext(); - - var names = ImmutableList.builder(); - var included = new LinkedHashSet(); // linked to preserve order - for (var component : key.sourceComponents) { - included.addAll(component.included()); - names.add(component.name()); - } - - for (var include : included) { - finalSource.append(include.source(ctx)); - } - - for (var component : key.sourceComponents) { - finalSource.append(component.source(ctx)); - } - - 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 sourceComponents A list of shader components to stitch together, in order. - */ - public record Context(GLSLVersion glslVersion, ShaderType shaderType, List sourceComponents) { - - public String generateHeader() { - return CompileUtil.generateHeader(glslVersion, shaderType); - } - } - - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/Compilation.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/Compilation.java new file mode 100644 index 000000000..f8253f8f5 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/Compilation.java @@ -0,0 +1,150 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.io.File; +import java.io.FileWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.NotNull; +import org.lwjgl.opengl.GL20; + +import com.jozufozu.flywheel.backend.Backend; +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.backend.gl.versioned.GlCompat; +import com.jozufozu.flywheel.core.SourceComponent; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.util.StringUtil; + +import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceLocation; + +/** + * Builder style class for compiling shaders. + *

+ * Keeps track of the source files and components used to compile a shader, + * and interprets/pretty prints any errors that occur. + */ +public class Compilation { + private final List files = new ArrayList<>(); + private final List componentNames = new ArrayList<>(); + private final StringBuilder generatedSource; + private final StringBuilder fullSource; + private final GLSLVersion glslVersion; + private final ShaderType shaderType; + private int generatedLines = 0; + + public Compilation(GLSLVersion glslVersion, ShaderType shaderType) { + this.generatedSource = new StringBuilder(); + this.fullSource = new StringBuilder(CompileUtil.generateHeader(glslVersion, shaderType)); + this.glslVersion = glslVersion; + this.shaderType = shaderType; + } + + @NotNull + public CompilationResult compile() { + int handle = GL20.glCreateShader(shaderType.glEnum); + var source = fullSource.toString(); + + GlCompat.safeShaderSource(handle, source); + GL20.glCompileShader(handle); + + var shaderName = buildShaderName(); + dumpSource(source, shaderType.getFileName(shaderName)); + + if (compiledSuccessfully(handle)) { + return CompilationResult.success(new GlShader(handle, shaderType, shaderName)); + } + + var errorLog = GL20.glGetShaderInfoLog(handle); + GL20.glDeleteShader(handle); + return CompilationResult.failure(new FailedCompilation(shaderName, files, generatedSource.toString(), errorLog)); + } + + public void enableExtension(String ext) { + fullSource.append("#extension ") + .append(ext) + .append(" : enable\n"); + } + + public void addComponentName(ResourceLocation name) { + componentNames.add(name); + } + + public void appendComponent(SourceComponent component) { + var source = component.source(); + + if (component instanceof SourceFile file) { + fullSource.append(sourceHeader(file)); + } else { + fullSource.append(generatedHeader(source, component.name() + .toString())); + } + + fullSource.append(source) + .append('\n'); + } + + private String sourceHeader(SourceFile sourceFile) { + return "#line " + 0 + ' ' + getOrCreateFileID(sourceFile) + " // " + sourceFile.name + '\n'; + } + + private String generatedHeader(String generatedCode, String comment) { + generatedSource.append(generatedCode); + int lines = StringUtil.countLines(generatedCode); + + // all generated code is put in file 0, + var out = "#line " + generatedLines + ' ' + 0; + + generatedLines += lines; + + return out + " // (generated) " + comment + '\n'; + } + + /** + * Returns an arbitrary file ID for use this compilation context, or generates one if missing. + * + * @param sourceFile The file to retrieve the ID for. + * @return A file ID unique to the given sourceFile. + */ + private int getOrCreateFileID(SourceFile sourceFile) { + int i = files.indexOf(sourceFile); + if (i != -1) { + return i + 1; + } + + files.add(sourceFile); + return files.size(); + } + + @NotNull + private String buildShaderName() { + var components = componentNames.stream() + .map(ResourceLocation::toString) + .map(s -> s.replaceAll("/", "_") + .replaceAll(":", "\\$")) + .collect(Collectors.joining(";")); + return shaderType.name + glslVersion + ';' + components; + } + + private static void dumpSource(String source, String fileName) { + if (!Backend.DUMP_SHADER_SOURCE) { + return; + } + + File dir = new File(Minecraft.getInstance().gameDirectory, "flywheel_sources"); + dir.mkdirs(); + File file = new File(dir, fileName); + try (FileWriter writer = new FileWriter(file)) { + writer.write(source); + } catch (Exception e) { + Backend.LOGGER.error("Could not dump source.", e); + } + } + + private static boolean compiledSuccessfully(int handle) { + return GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) == GL20.GL_TRUE; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CompilationResult.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CompilationResult.java new file mode 100644 index 000000000..a5d352f73 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CompilationResult.java @@ -0,0 +1,29 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import org.jetbrains.annotations.Nullable; + +import com.jozufozu.flywheel.backend.gl.shader.GlShader; + +public sealed interface CompilationResult { + @Nullable + default GlShader unwrap() { + if (this instanceof Success s) { + return s.shader(); + } + return null; + } + + record Success(GlShader shader) implements CompilationResult { + } + + record Failure(FailedCompilation failure) implements CompilationResult { + } + + static CompilationResult success(GlShader program) { + return new Success(program); + } + + static CompilationResult failure(FailedCompilation failure) { + return new Failure(failure); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CompileUtil.java similarity index 51% rename from src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java rename to src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CompileUtil.java index 545055281..14a4631a4 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CompileUtil.java @@ -1,4 +1,10 @@ -package com.jozufozu.flywheel.core.compile; +package com.jozufozu.flywheel.backend.instancing.compile; + +import static org.lwjgl.opengl.GL20.GL_LINK_STATUS; +import static org.lwjgl.opengl.GL20.GL_TRUE; +import static org.lwjgl.opengl.GL20.glGetProgramInfoLog; +import static org.lwjgl.opengl.GL20.glGetProgrami; +import static org.lwjgl.opengl.GL20.glLinkProgram; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -7,6 +13,7 @@ import java.util.stream.Stream; import org.jetbrains.annotations.NotNull; +import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GLSLVersion; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.core.source.SourceFile; @@ -17,9 +24,7 @@ public class CompileUtil { public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$"); public static String generateHeader(GLSLVersion version, ShaderType type) { - return version.getVersionLine() - + type.getDefineStatement() - + '\n'; + return version.getVersionLine() + type.getDefineStatement() + '\n'; } public static int getElementCount(String type) { @@ -53,10 +58,31 @@ public class CompileUtil { return 1; } - @NotNull - public static String generateDebugName(SourceFile... stages) { - return Stream.of(stages) - .map(SourceFile::toString) - .collect(Collectors.joining(" -> ")); - } + @NotNull + public static String generateDebugName(SourceFile... stages) { + return Stream.of(stages) + .map(SourceFile::toString) + .collect(Collectors.joining(" -> ")); + } + + /** + * Check the program info log for errors. + * + * @param handle The handle of the program to check. + */ + public static void checkLinkLog(int handle) { + glLinkProgram(handle); + + String log = glGetProgramInfoLog(handle); + + if (!log.isEmpty()) { + Backend.LOGGER.debug("Program link log: " + log); + } + + int result = glGetProgrami(handle, GL_LINK_STATUS); + + if (result != GL_TRUE) { + throw new RuntimeException("Shader program linking failed, see log for details"); + } + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CullingContext.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CullingContext.java new file mode 100644 index 000000000..80464f50f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CullingContext.java @@ -0,0 +1,6 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import com.jozufozu.flywheel.api.struct.StructType; + +public record CullingContext(StructType structType) { +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CullingContextSet.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CullingContextSet.java new file mode 100644 index 000000000..bd6981c4a --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CullingContextSet.java @@ -0,0 +1,38 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.core.ComponentRegistry; + +public class CullingContextSet { + static CullingContextSet create() { + var builder = new CullingContextSet(); + for (StructType structType : ComponentRegistry.structTypes) { + builder.add(structType); + } + return builder; + } + + private final List contexts = new ArrayList<>(); + private final List contextView = Collections.unmodifiableList(contexts); + + CullingContextSet() { + } + + public List all() { + return contextView; + } + + public int size() { + return contexts.size(); + } + + private void add(StructType structType) { + var ctx = new CullingContext(structType); + + contexts.add(ctx); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FailedCompilation.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FailedCompilation.java new file mode 100644 index 000000000..fc44515bb --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FailedCompilation.java @@ -0,0 +1,83 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.jetbrains.annotations.NotNull; + +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.core.source.SourceLines; +import com.jozufozu.flywheel.core.source.error.ErrorBuilder; +import com.jozufozu.flywheel.core.source.span.Span; +import com.jozufozu.flywheel.util.ConsoleColors; +import com.jozufozu.flywheel.util.StringUtil; + +public class FailedCompilation { + private static final Pattern ERROR_LINE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)"); + private final List files; + private final SourceLines generatedSource; + private final String errorLog; + private final String shaderName; + + public FailedCompilation(String shaderName, List files, String generatedSource, String errorLog) { + this.shaderName = shaderName; + this.files = files; + this.generatedSource = new SourceLines(generatedSource); + this.errorLog = errorLog; + } + + public String getMessage() { + return ConsoleColors.RED_BOLD_BRIGHT + "Failed to compile " + shaderName + ":\n" + errorString(); + } + + public String errorString() { + return errorStream().map(ErrorBuilder::build) + .collect(Collectors.joining("\n")); + } + + @NotNull + private Stream errorStream() { + return errorLog.lines() + .map(this::interpretErrorLine); + } + + private ErrorBuilder interpretErrorLine(String s) { + Matcher matcher = ERROR_LINE.matcher(s); + + if (matcher.find()) { + int fileId = Integer.parseInt(matcher.group(1)); + int lineNo = Integer.parseInt(matcher.group(2)); + var msg = StringUtil.trimPrefix(matcher.group(3), "error") + .stripLeading(); + + if (fileId == 0) { + return interpretGeneratedError(lineNo, msg); + } else { + return interpretSourceError(fileId, lineNo, msg); + } + } + return ErrorBuilder.create() + .error(s); + } + + private ErrorBuilder interpretSourceError(int fileId, int lineNo, String msg) { + var sourceFile = files.get(fileId - 1); + Span span = sourceFile.getLineSpanNoWhitespace(lineNo); + + return ErrorBuilder.create() + .error(msg) + .pointAtFile(sourceFile) + .pointAt(span, 1); + } + + private ErrorBuilder interpretGeneratedError(int lineNo, String msg) { + return ErrorBuilder.create() + .error(msg) + .pointAtFile("[in generated source]") + .pointAtLine(generatedSource, lineNo, 1) + .note("This generally indicates a bug in Flywheel, not your shader code."); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FlwCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FlwCompiler.java new file mode 100644 index 000000000..42635d642 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FlwCompiler.java @@ -0,0 +1,207 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import static org.lwjgl.opengl.GL20.glAttachShader; +import static org.lwjgl.opengl.GL20.glCreateProgram; +import static org.lwjgl.opengl.GL20.glLinkProgram; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.Flywheel; +import com.jozufozu.flywheel.api.pipeline.Pipeline; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.uniform.ShaderUniforms; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.backend.instancing.indirect.IndirectComponent; +import com.jozufozu.flywheel.core.ComponentRegistry; +import com.jozufozu.flywheel.core.Pipelines; +import com.jozufozu.flywheel.core.SourceComponent; +import com.jozufozu.flywheel.core.context.SimpleContext; +import com.jozufozu.flywheel.core.pipeline.SimplePipeline; +import com.jozufozu.flywheel.core.source.ShaderLoadingException; +import com.jozufozu.flywheel.core.source.ShaderSources; +import com.jozufozu.flywheel.core.source.generate.FnSignature; +import com.jozufozu.flywheel.core.source.generate.GlslExpr; +import com.jozufozu.flywheel.util.StringUtil; + +public class FlwCompiler { + + public static FlwCompiler INSTANCE; + + final long compileStart = System.nanoTime(); + private final ShaderSources sources; + private final UniformComponent uniformComponent; + private final MaterialAdapterComponent vertexMaterialComponent; + private final MaterialAdapterComponent fragmentMaterialComponent; + + private final PipelineContextSet pipelineContexts; + private final CullingContextSet cullingContexts; + + final ShaderCompiler shaderCompiler; + final Map pipelinePrograms = new HashMap<>(); + final Map, GlProgram> cullingPrograms = new HashMap<>(); + final List errors = new ArrayList<>(); + + public FlwCompiler(ShaderSources sources) { + this.shaderCompiler = ShaderCompiler.builder() + .errorConsumer(errors::add) + .build(); + + this.sources = sources; + this.vertexMaterialComponent = MaterialAdapterComponent.builder(Flywheel.rl("vertex_material_adapter")) + .materialSources(ComponentRegistry.materials.vertexSources()) + .adapt(FnSignature.ofVoid("flw_materialVertex")) + .switchOn(GlslExpr.variable("flw_materialVertexID")) + .build(sources); + this.fragmentMaterialComponent = MaterialAdapterComponent.builder(Flywheel.rl("fragment_material_adapter")) + .materialSources(ComponentRegistry.materials.fragmentSources()) + .adapt(FnSignature.ofVoid("flw_materialFragment")) + .adapt(FnSignature.create() + .returnType("bool") + .name("flw_discardPredicate") + .arg("vec4", "color") + .build(), GlslExpr.literal(false)) + .adapt(FnSignature.create() + .returnType("vec4") + .name("flw_fogFilter") + .arg("vec4", "color") + .build(), GlslExpr.variable("color")) + .switchOn(GlslExpr.variable("flw_materialFragmentID")) + .build(sources); + this.uniformComponent = UniformComponent.builder(Flywheel.rl("uniforms")) + .sources(ComponentRegistry.getAllUniformProviders() + .stream() + .map(ShaderUniforms::uniformShader) + .toList()) + .build(sources); + + this.pipelineContexts = PipelineContextSet.create(); + this.cullingContexts = CullingContextSet.create(); + + doCompilation(); + + finish(); + } + + private void doCompilation() { + for (var ctx : pipelineContexts.all()) { + compilePipelineContext(ctx); + } + + for (var ctx : cullingContexts.all()) { + compileComputeCuller(ctx); + } + } + + private void finish() { + long compileEnd = System.nanoTime(); + int programCount = pipelineContexts.size() + ComponentRegistry.structTypes.size(); + int shaderCount = shaderCompiler.shaderCount(); + int errorCount = errors.size(); + var elapsed = StringUtil.formatTime(compileEnd - compileStart); + + Backend.LOGGER.info("Compiled " + programCount + " programs and " + shaderCount + " shaders in " + elapsed + " with " + errorCount + " errors."); + + if (errorCount > 0) { + var details = errors.stream() + .map(FailedCompilation::getMessage) + .collect(Collectors.joining("\n")); + throw new ShaderLoadingException("Compilation failed.\n" + details); + } + } + + public void delete() { + pipelinePrograms.values() + .forEach(GlProgram::delete); + cullingPrograms.values() + .forEach(GlProgram::delete); + shaderCompiler.delete(); + } + + public GlProgram getPipelineProgram(VertexType vertexType, StructType structType, SimpleContext contextShader, SimplePipeline pipelineShader) { + return pipelinePrograms.get(new PipelineContext(vertexType, structType, contextShader, pipelineShader)); + } + + public GlProgram getCullingProgram(StructType structType) { + return cullingPrograms.get(structType); + } + + private void compilePipelineContext(PipelineContext ctx) { + var glslVersion = ctx.pipelineShader() + .glslVersion(); + + var vertex = shaderCompiler.compile(glslVersion, ShaderType.VERTEX, getVertexComponents(ctx)); + var fragment = shaderCompiler.compile(glslVersion, ShaderType.FRAGMENT, getFragmentComponents(ctx)); + + if (vertex == null || fragment == null) { + return; + } + + var glProgram = link(vertex.handle(), fragment.handle()); + ctx.contextShader() + .onProgramLink(glProgram); + pipelinePrograms.put(ctx, glProgram); + } + + private void compileComputeCuller(CullingContext ctx) { + var computeComponents = getComputeComponents(ctx.structType()); + var result = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, computeComponents); + + if (result == null) { + return; + } + + cullingPrograms.put(ctx.structType(), link(result.handle())); + } + + private GlProgram link(int... shaders) { + var handle = glCreateProgram(); + for (var shader : shaders) { + glAttachShader(handle, shader); + } + glLinkProgram(handle); + CompileUtil.checkLinkLog(handle); + return new GlProgram(handle); + } + + private ImmutableList getVertexComponents(PipelineContext ctx) { + var instanceAssembly = ctx.pipelineShader() + .assemble(new Pipeline.InstanceAssemblerContext(sources, ctx.vertexType(), ctx.structType())); + + var layout = sources.find(ctx.vertexType() + .layoutShader()); + var instance = sources.find(ctx.structType() + .instanceShader()); + var context = sources.find(ctx.contextShader() + .vertexShader()); + var pipeline = sources.find(ctx.pipelineShader() + .vertexShader()); + + return ImmutableList.of(uniformComponent, vertexMaterialComponent, instanceAssembly, layout, instance, context, pipeline); + } + + private ImmutableList getFragmentComponents(PipelineContext ctx) { + var context = sources.find(ctx.contextShader() + .fragmentShader()); + var pipeline = sources.find(ctx.pipelineShader() + .fragmentShader()); + return ImmutableList.of(uniformComponent, fragmentMaterialComponent, context, pipeline); + } + + private ImmutableList getComputeComponents(StructType structType) { + var instanceAssembly = new IndirectComponent(sources, structType); + var instance = sources.find(structType.instanceShader()); + var pipeline = sources.find(Pipelines.Files.INDIRECT_CULL); + + return ImmutableList.of(uniformComponent, instanceAssembly, instance, pipeline); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/Includer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/Includer.java new file mode 100644 index 000000000..b25b03920 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/Includer.java @@ -0,0 +1,21 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.util.function.Consumer; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.core.SourceComponent; + +/** + * A component of a ShaderCompiler, responsible for expanding root sources into the complete set of included sources. + */ +public interface Includer { + + /** + * Expand the given root sources into the complete set of included sources. + *

Each unique source will be seen exactly once. + * + * @param rootSources The root sources to expand. + * @param out A consumer to which all sources should be passed in the order they should be included. + */ + void expand(ImmutableList rootSources, Consumer out); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/MaterialAdapterComponent.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/MaterialAdapterComponent.java new file mode 100644 index 000000000..f3e575982 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/MaterialAdapterComponent.java @@ -0,0 +1,173 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; + +import javax.annotation.Nonnull; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.core.SourceComponent; +import com.jozufozu.flywheel.core.source.ShaderSources; +import com.jozufozu.flywheel.core.source.generate.FnSignature; +import com.jozufozu.flywheel.core.source.generate.GlslBlock; +import com.jozufozu.flywheel.core.source.generate.GlslBuilder; +import com.jozufozu.flywheel.core.source.generate.GlslExpr; +import com.jozufozu.flywheel.core.source.generate.GlslSwitch; +import com.jozufozu.flywheel.util.ResourceUtil; + +import net.minecraft.resources.ResourceLocation; + +public class MaterialAdapterComponent implements SourceComponent { + + // TODO: material id handling in pipeline shader + private final ResourceLocation name; + private final GlslExpr switchArg; + private final List functionsToAdapt; + private final List adaptedComponents; + + public MaterialAdapterComponent(ResourceLocation name, GlslExpr switchArg, List functionsToAdapt, List adaptedComponents) { + this.name = name; + this.switchArg = switchArg; + this.functionsToAdapt = functionsToAdapt; + this.adaptedComponents = adaptedComponents; + } + + public static Builder builder(ResourceLocation name) { + return new Builder(name); + } + + @Override + public ResourceLocation name() { + return name; + } + + @Override + public Collection included() { + return adaptedComponents; + } + + @Override + public String source() { + var builder = new GlslBuilder(); + + for (var adaptedFunction : functionsToAdapt) { + builder.function() + .signature(adaptedFunction.signature()) + .body(body -> generateAdapter(body, adaptedFunction)); + } + + return builder.build(); + } + + private void generateAdapter(GlslBlock body, AdaptedFn adaptedFunction) { + var sw = GlslSwitch.on(switchArg); + var fnSignature = adaptedFunction.signature(); + var fnName = fnSignature.name(); + var isVoid = fnSignature.isVoid(); + var fnArgs = fnSignature.createArgExpressions(); + + for (int i = 0; i < adaptedComponents.size(); i++) { + var component = adaptedComponents.get(i); + + if (!component.replaces(fnName)) { + continue; + } + + var adaptedCall = GlslExpr.call(component.remapFnName(fnName), fnArgs); + + var block = GlslBlock.create(); + if (isVoid) { + block.eval(adaptedCall) + .breakStmt(); + } else { + block.ret(adaptedCall); + } + + sw.intCase(i, block); + } + + if (!isVoid) { + var defaultReturn = adaptedFunction.defaultReturn; + if (defaultReturn == null) { + throw new IllegalStateException("Function " + fnName + " is not void, but no default return value was provided"); + } + sw.defaultCase(GlslBlock.create() + .ret(defaultReturn)); + } + + body.add(sw); + } + + private record AdaptedFn(FnSignature signature, @Nullable GlslExpr defaultReturn) { + } + + public static class Builder { + private final ResourceLocation name; + private final List materialSources = new ArrayList<>(); + private final List adaptedFunctions = new ArrayList<>(); + private GlslExpr switchArg; + + public Builder(ResourceLocation name) { + this.name = name; + } + + public Builder materialSources(List sources) { + this.materialSources.addAll(sources); + return this; + } + + public Builder adapt(FnSignature function) { + adaptedFunctions.add(new AdaptedFn(function, null)); + return this; + } + + public Builder adapt(FnSignature function, @Nonnull GlslExpr defaultReturn) { + adaptedFunctions.add(new AdaptedFn(function, defaultReturn)); + return this; + } + + public Builder switchOn(GlslExpr expr) { + this.switchArg = expr; + return this; + } + + public MaterialAdapterComponent build(ShaderSources sources) { + if (switchArg == null) { + throw new NullPointerException("Switch argument must be set"); + } + + var transformed = ImmutableList.builder(); + + for (var rl : materialSources) { + var sourceFile = sources.find(rl); + var adapterMap = createAdapterMap(adaptedFunctions, getSuffix(rl)); + transformed.add(new StringSubstitutionSourceComponent(sourceFile, adapterMap)); + } + + return new MaterialAdapterComponent(name, switchArg, adaptedFunctions, transformed.build()); + } + } + + @NotNull + private static HashMap createAdapterMap(List adaptedFunctions, String suffix) { + HashMap out = new HashMap<>(); + + for (var adapted : adaptedFunctions) { + var fnName = adapted.signature() + .name(); + out.put(fnName, fnName + suffix); + } + + return out; + } + + @NotNull + private static String getSuffix(ResourceLocation rl) { + return '_' + ResourceUtil.toSafeString(rl); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/PipelineContext.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/PipelineContext.java new file mode 100644 index 000000000..179924cfa --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/PipelineContext.java @@ -0,0 +1,17 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import com.jozufozu.flywheel.api.context.Context; +import com.jozufozu.flywheel.api.pipeline.Pipeline; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; + +/** + * Represents the entire context of a program's usage. + * + * @param vertexType The vertexType the program should be adapted for. + * @param structType The instance shader to use. + * @param contextShader The context shader to use. + */ +public record PipelineContext(VertexType vertexType, StructType structType, Context contextShader, + Pipeline pipelineShader) { +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/PipelineContextSet.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/PipelineContextSet.java new file mode 100644 index 000000000..e103c9fff --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/PipelineContextSet.java @@ -0,0 +1,48 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.core.BackendTypes; +import com.jozufozu.flywheel.core.ComponentRegistry; +import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.context.SimpleContext; +import com.jozufozu.flywheel.core.pipeline.SimplePipeline; + +public class PipelineContextSet { + static PipelineContextSet create() { + var builder = new PipelineContextSet(); + for (SimplePipeline pipelineShader : BackendTypes.availablePipelineShaders()) { + for (StructType structType : ComponentRegistry.structTypes) { + for (VertexType vertexType : ComponentRegistry.vertexTypes) { + builder.add(vertexType, structType, Components.WORLD, pipelineShader); + } + } + } + return builder; + } + + private final List contexts = new ArrayList<>(); + private final List contextView = Collections.unmodifiableList(contexts); + + PipelineContextSet() { + } + + public List all() { + return contextView; + } + + public int size() { + return contexts.size(); + } + + private void add(VertexType vertexType, StructType structType, SimpleContext world, SimplePipeline pipelineShader) { + var ctx = new PipelineContext(vertexType, structType, world, pipelineShader); + + + contexts.add(ctx); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/RecursiveIncluder.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/RecursiveIncluder.java new file mode 100644 index 000000000..031371097 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/RecursiveIncluder.java @@ -0,0 +1,33 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.function.Consumer; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.core.SourceComponent; + +public class RecursiveIncluder implements Includer { + + public static final RecursiveIncluder INSTANCE = new RecursiveIncluder(); + + private RecursiveIncluder() { + } + + @Override + public void expand(ImmutableList rootSources, Consumer out) { + var included = new LinkedHashSet(); // use hash set to deduplicate. linked to preserve order + for (var component : rootSources) { + recursiveDepthFirstInclude(included, component); + included.add(component); + } + included.forEach(out); + } + + private static void recursiveDepthFirstInclude(Set included, SourceComponent component) { + for (var include : component.included()) { + recursiveDepthFirstInclude(included, include); + } + included.addAll(component.included()); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/ShaderCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/ShaderCompiler.java new file mode 100644 index 000000000..ca570b1e9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/ShaderCompiler.java @@ -0,0 +1,113 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +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.SourceComponent; +import com.jozufozu.flywheel.util.FlwUtil; + +public class ShaderCompiler { + private final Map shaderCache = new HashMap<>(); + private final Consumer errorConsumer; + private final CompilationFactory factory; + private final Includer includer; + + public ShaderCompiler(Consumer errorConsumer, CompilationFactory factory, Includer includer) { + this.errorConsumer = errorConsumer; + this.factory = factory; + this.includer = includer; + } + + public int shaderCount() { + return shaderCache.size(); + } + + @Nullable + public GlShader compile(GLSLVersion glslVersion, ShaderType shaderType, ImmutableList sourceComponents) { + var key = new ShaderKey(glslVersion, shaderType, sourceComponents); + var cached = shaderCache.get(key); + if (cached != null) { + return cached.unwrap(); + } + + CompilationResult out = compileUncached(factory.create(glslVersion, shaderType), sourceComponents); + shaderCache.put(key, out); + return unwrapAndReportError(out); + } + + public void delete() { + shaderCache.values() + .stream() + .map(CompilationResult::unwrap) + .filter(Objects::nonNull) + .forEach(GlShader::delete); + } + + @Nullable + private GlShader unwrapAndReportError(CompilationResult result) { + if (result instanceof CompilationResult.Success s) { + return s.shader(); + } else if (result instanceof CompilationResult.Failure f) { + errorConsumer.accept(f.failure()); + } + return null; + } + + @NotNull + private CompilationResult compileUncached(Compilation ctx, ImmutableList sourceComponents) { + ctx.enableExtension("GL_ARB_explicit_attrib_location"); + ctx.enableExtension("GL_ARB_conservative_depth"); + + includer.expand(sourceComponents, ctx::appendComponent); + + return ctx.compile(); + } + + private record ShaderKey(GLSLVersion glslVersion, ShaderType shaderType, + ImmutableList sourceComponents) { + + } + + public static Builder builder() { + return new Builder(); + } + + @FunctionalInterface + public interface CompilationFactory { + Compilation create(GLSLVersion version, ShaderType shaderType); + } + + public static class Builder { + private Consumer errorConsumer = FlwUtil::noop; + private CompilationFactory factory = Compilation::new; + private Includer includer = RecursiveIncluder.INSTANCE; + + public Builder errorConsumer(Consumer errorConsumer) { + this.errorConsumer = errorConsumer; + return this; + } + + public Builder compilationFactory(CompilationFactory factory) { + this.factory = factory; + return this; + } + + public Builder includer(Includer includer) { + this.includer = includer; + return this; + } + + public ShaderCompiler build() { + return new ShaderCompiler(errorConsumer, factory, includer); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/StringSubstitutionSourceComponent.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/StringSubstitutionSourceComponent.java new file mode 100644 index 000000000..52c341ea9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/StringSubstitutionSourceComponent.java @@ -0,0 +1,54 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.util.Collection; +import java.util.Map; + +import com.jozufozu.flywheel.core.SourceComponent; +import com.jozufozu.flywheel.util.ResourceUtil; + +import net.minecraft.resources.ResourceLocation; + +public final class StringSubstitutionSourceComponent implements SourceComponent { + private final SourceComponent source; + private final Map replacements; + private final String sourceString; + + public StringSubstitutionSourceComponent(SourceComponent source, String find, String replace) { + this(source, Map.of(find, replace)); + } + + public StringSubstitutionSourceComponent(SourceComponent source, Map replacements) { + this.source = source; + this.replacements = replacements; + this.sourceString = source.source(); + } + + public String remapFnName(String name) { + return replacements.getOrDefault(name, name); + } + + public boolean replaces(String name) { + return replacements.containsKey(name) && sourceString.contains(name); + } + + @Override + public String source() { + var source = sourceString; + + for (var entry : replacements.entrySet()) { + source = source.replace(entry.getKey(), entry.getValue()); + } + + return source; + } + + @Override + public ResourceLocation name() { + return ResourceUtil.subPath(source.name(), "_string_substitution"); + } + + @Override + public Collection included() { + return source.included(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/UniformComponent.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/UniformComponent.java new file mode 100644 index 000000000..4c334635d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/UniformComponent.java @@ -0,0 +1,76 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.core.SourceComponent; +import com.jozufozu.flywheel.core.source.ShaderSources; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.core.source.generate.GlslBuilder; + +import net.minecraft.resources.ResourceLocation; + +public class UniformComponent implements SourceComponent { + + private final ResourceLocation name; + private final ImmutableList uniformShaders; + + public static Builder builder(ResourceLocation uniforms) { + return new Builder(uniforms); + } + + private UniformComponent(ResourceLocation name, ImmutableList uniformShaders) { + this.name = name; + this.uniformShaders = uniformShaders; + } + + @Override + public Collection included() { + return uniformShaders; + } + + @Override + public String source() { + var builder = new GlslBuilder(); + + builder.uniformBlock() + .layout("std140") + .binding(0) + .name("FLWUniforms") + .member("flywheel_uniforms", "flywheel"); + + return builder.build(); + } + + @Override + public ResourceLocation name() { + return name; + } + + public static class Builder { + + private final ResourceLocation name; + private final List uniformShaders = new ArrayList<>(); + + public Builder(ResourceLocation name) { + this.name = name; + } + + public Builder sources(List sources) { + this.uniformShaders.addAll(sources); + return this; + } + + public UniformComponent build(ShaderSources sources) { + var out = ImmutableList.builder(); + + for (var fileResolution : uniformShaders) { + out.add(sources.find(fileResolution)); + } + + return new UniformComponent(name, out.build()); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/package-info.java new file mode 100644 index 000000000..ddcf60e2e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.instancing.compile; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault; 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 deleted file mode 100644 index 4efda5bc0..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/ComputeCullerCompiler.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.indirect; - -import java.util.LinkedHashSet; -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.api.struct.StructType; -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.SourceComponent; -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.event.ReloadRenderersEvent; - -import net.minecraft.resources.ResourceLocation; - -public class ComputeCullerCompiler extends Memoizer, GlProgram> { - - public static final ComputeCullerCompiler INSTANCE = new ComputeCullerCompiler(); - - private ComputeCullerCompiler() { - } - - @Override - protected GlProgram _create(StructType structType) { - var location = structType.getInstanceShader(); - - var finalSource = new StringBuilder(); - CompilationContext context = new CompilationContext(); - var components = List.of(new IndirectComponent(structType.getLayout().layoutItems), location.getFile(), Components.Pipeline.INDIRECT_CULL.getFile()); - - var names = ImmutableList.builder(); - var included = new LinkedHashSet(); // linked to preserve order - for (var component : components) { - included.addAll(component.included()); - names.add(component.name()); - } - - finalSource.append(CompileUtil.generateHeader(GLSLVersion.V460, ShaderType.COMPUTE)); - for (var include : included) { - finalSource.append(include.source(context)); - } - - for (var component : components) { - finalSource.append(component.source(context)); - } - - try { - var fileLoc = location.getFileLoc(); - var shader = new GlShader(finalSource.toString(), ShaderType.COMPUTE, ImmutableList.of(fileLoc)); - - return new ProgramAssembler(fileLoc).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 index f99a6d799..2c9f5f7d1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectBuffers.java @@ -1,22 +1,7 @@ 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 static org.lwjgl.opengl.GL46.*; import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.Pointer; @@ -30,7 +15,7 @@ public class IndirectBuffers { 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_STRIDE = 44; public static final long DRAW_COMMAND_OFFSET = 0; // BITS @@ -180,15 +165,16 @@ public class IndirectBuffers { FlwMemoryTracker._freeGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE); } - public void bindAll() { - bindN(BUFFER_COUNT); + public void bindForCompute() { + multiBind(BUFFER_COUNT); } - public void bindObjectAndTarget() { - bindN(2); + public void bindForDraw() { + multiBind(BUFFER_COUNT); + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, draw); } - private void bindN(int bufferCount) { + private void multiBind(int bufferCount) { if (bufferCount > BUFFER_COUNT) { throw new IllegalArgumentException("Can't bind more than " + BUFFER_COUNT + " buffers"); } @@ -197,10 +183,6 @@ public class IndirectBuffers { 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); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectComponent.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectComponent.java index 05f772b47..a9f9d4606 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectComponent.java @@ -2,13 +2,18 @@ package com.jozufozu.flywheel.backend.instancing.indirect; import java.util.Collection; import java.util.List; -import java.util.stream.Collectors; +import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.api.pipeline.Pipeline; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.core.Pipelines; import com.jozufozu.flywheel.core.SourceComponent; import com.jozufozu.flywheel.core.layout.LayoutItem; -import com.jozufozu.flywheel.core.source.CompilationContext; +import com.jozufozu.flywheel.core.source.ShaderSources; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.core.source.generate.FnSignature; +import com.jozufozu.flywheel.core.source.generate.GlslBlock; import com.jozufozu.flywheel.core.source.generate.GlslBuilder; import com.jozufozu.flywheel.core.source.generate.GlslExpr; @@ -18,16 +23,24 @@ public class IndirectComponent implements SourceComponent { private static final String UNPACK_ARG = "p"; private static final GlslExpr.Variable UNPACKING_VARIABLE = GlslExpr.variable(UNPACK_ARG); + private static final String STRUCT_NAME = "IndirectStruct"; + private static final String PACKED_STRUCT_NAME = STRUCT_NAME + "_packed"; private final List layoutItems; + private final ImmutableList included; - public IndirectComponent(List layoutItems) { - this.layoutItems = layoutItems; + public IndirectComponent(Pipeline.InstanceAssemblerContext ctx) { + this(ctx.sources(), ctx.structType()); + } + + public IndirectComponent(ShaderSources sources, StructType structType) { + this.layoutItems = structType.getLayout().layoutItems; + included = ImmutableList.of(sources.find(Pipelines.Files.UTIL_TYPES)); } @Override public Collection included() { - return List.of(Components.UTIL_TYPES.getFile()); + return included; } @Override @@ -36,22 +49,20 @@ public class IndirectComponent implements SourceComponent { } @Override - public String source(CompilationContext ctx) { - var generated = generateIndirect("IndirectStruct"); - return ctx.generatedHeader(generated, name().toString()) + generated; + public String source() { + return generateIndirect(); } - public String generateIndirect(String structName) { + public String generateIndirect() { var builder = new GlslBuilder(); - final var packedStructName = structName + "_packed"; - builder.define("FlwInstance", structName); - builder.define("FlwPackedInstance", packedStructName); + builder.define("FlwInstance", STRUCT_NAME); + builder.define("FlwPackedInstance", PACKED_STRUCT_NAME); var packed = builder.struct(); builder.blankLine(); var instance = builder.struct(); - packed.setName(packedStructName); - instance.setName(structName); + packed.setName(PACKED_STRUCT_NAME); + instance.setName(STRUCT_NAME); for (var field : layoutItems) { field.addPackedToStruct(packed); @@ -60,18 +71,21 @@ public class IndirectComponent implements SourceComponent { builder.blankLine(); - var func = builder.function() - .returnType(structName) - .name("flw_unpackInstance") - .argumentIn(packedStructName, UNPACK_ARG); - - var args = layoutItems.stream() - .map(layoutItem -> layoutItem.unpackField(UNPACKING_VARIABLE)) - .map(GlslExpr::minPrint) - .collect(Collectors.joining(", ")); - - func.statement("return " + structName + "(" + args + ");"); + builder.function() + .signature(FnSignature.create() + .returnType(STRUCT_NAME) + .name("flw_unpackInstance") + .arg(PACKED_STRUCT_NAME, UNPACK_ARG) + .build()) + .body(this::generateUnpackingBody); return builder.build(); } + + private void generateUnpackingBody(GlslBlock b) { + var unpackedFields = layoutItems.stream() + .map(layoutItem -> layoutItem.unpackField(UNPACKING_VARIABLE)) + .toList(); + b.ret(GlslExpr.call(STRUCT_NAME, unpackedFields)); + } } 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 index ae3f47c9b..40238c5c8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectCullingGroup.java @@ -3,31 +3,22 @@ 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 static org.lwjgl.opengl.GL46.*; import com.jozufozu.flywheel.api.RenderStage; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.api.struct.StructWriter; 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.backend.instancing.compile.FlwCompiler; import com.jozufozu.flywheel.core.Components; -import com.jozufozu.flywheel.core.Materials; +import com.jozufozu.flywheel.core.Pipelines; 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 StructWriter storageBufferWriter; final GlProgram compute; final GlProgram draw; private final VertexType vertexType; @@ -48,7 +39,6 @@ public class IndirectCullingGroup { IndirectCullingGroup(StructType structType, VertexType vertexType) { this.vertexType = vertexType; - storageBufferWriter = structType.getWriter(); objectStride = structType.getLayout() .getStride(); @@ -65,8 +55,8 @@ public class IndirectCullingGroup { .quads2Tris(2048).glBuffer; setupVertexArray(); - compute = ComputeCullerCompiler.INSTANCE.get(structType); - draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.SHULKER, structType, Components.WORLD, Components.INDIRECT)); + compute = FlwCompiler.INSTANCE.getCullingProgram(structType); + draw = FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, Components.WORLD, Pipelines.INDIRECT); } private void setupVertexArray() { @@ -116,11 +106,8 @@ public class IndirectCullingGroup { uploadInstanceData(); uploadIndirectCommands(); - UniformBuffer.getInstance() - .sync(); - compute.bind(); - buffers.bindAll(); + buffers.bindForCompute(); var groupCount = (instanceCountThisFrame + 31) >> 5; // ceil(instanceCount / 32) glDispatchCompute(groupCount, 1, 1); @@ -134,11 +121,7 @@ public class IndirectCullingGroup { draw.bind(); glBindVertexArray(vertexArray); - buffers.bindObjectAndTarget(); - buffers.bindIndirectBuffer(); - - UniformBuffer.getInstance() - .sync(); + buffers.bindForDraw(); memoryBarrier(); 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 index 72a7f7be7..6cb31ac81 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDraw.java @@ -5,6 +5,7 @@ import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.RenderStage; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.core.ComponentRegistry; public final class IndirectDraw { final IndirectInstancer instancer; @@ -13,6 +14,9 @@ public final class IndirectDraw { final RenderStage stage; int baseInstance = -1; + final int vertexMaterialID; + final int fragmentMaterialID; + boolean needsFullWrite = true; IndirectDraw(IndirectInstancer instancer, Material material, RenderStage stage, IndirectMeshPool.BufferedMesh mesh) { @@ -20,6 +24,9 @@ public final class IndirectDraw { this.material = material; this.stage = stage; this.mesh = mesh; + + this.vertexMaterialID = ComponentRegistry.materials.getVertexID(material); + this.fragmentMaterialID = ComponentRegistry.materials.getFragmentID(material); } public void prepare(int baseInstance) { @@ -51,5 +58,8 @@ public final class IndirectDraw { MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance boundingSphere.getToAddress(ptr + 20); // boundingSphere + MemoryUtil.memPutInt(ptr + 36, vertexMaterialID); // vertexMaterialID + MemoryUtil.memPutInt(ptr + 40, fragmentMaterialID); // fragmentMaterialID + } } 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 index 0f1a2a1ad..46f9d65cd 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectDrawSet.java @@ -10,6 +10,7 @@ import java.util.List; import com.jozufozu.flywheel.api.RenderStage; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.util.Textures; public class IndirectDrawSet { @@ -37,6 +38,7 @@ public class IndirectDrawSet { var material = batch.material; material.setup(); + Textures.bindActiveTextures(); glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, i * stride, 1, stride); material.clear(); } 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 index bca93b860..3b17cd3e4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java @@ -7,7 +7,6 @@ import java.util.Set; 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.instancer.Instancer; import com.jozufozu.flywheel.api.struct.StructType; @@ -19,6 +18,7 @@ import com.jozufozu.flywheel.backend.instancing.TaskExecutor; import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.util.FlwUtil; +import com.jozufozu.flywheel.core.context.SimpleContext; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.Camera; @@ -37,12 +37,12 @@ public class IndirectEngine implements Engine { */ private final Set> instanceManagers = FlwUtil.createWeakHashSet(); - protected final ContextShader context; + protected final SimpleContext context; protected final int sqrMaxOriginDistance; protected BlockPos originCoordinate = BlockPos.ZERO; - public IndirectEngine(ContextShader context, int sqrMaxOriginDistance) { + public IndirectEngine(SimpleContext context, int sqrMaxOriginDistance) { this.context = context; this.sqrMaxOriginDistance = sqrMaxOriginDistance; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysComponent.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysComponent.java index 2a7f28f8e..f894d7c63 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysComponent.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedArraysComponent.java @@ -3,12 +3,13 @@ package com.jozufozu.flywheel.backend.instancing.instancing; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; import com.jozufozu.flywheel.Flywheel; +import com.jozufozu.flywheel.api.pipeline.Pipeline; import com.jozufozu.flywheel.core.SourceComponent; import com.jozufozu.flywheel.core.layout.LayoutItem; -import com.jozufozu.flywheel.core.source.CompilationContext; +import com.jozufozu.flywheel.core.source.generate.FnSignature; +import com.jozufozu.flywheel.core.source.generate.GlslBlock; import com.jozufozu.flywheel.core.source.generate.GlslBuilder; import com.jozufozu.flywheel.core.source.generate.GlslExpr; @@ -16,13 +17,17 @@ import net.minecraft.resources.ResourceLocation; public class InstancedArraysComponent implements SourceComponent { private static final String ATTRIBUTE_SUFFIX = "_vertex_in"; + private static final String STRUCT_NAME = "Instance"; private final List layoutItems; private final int baseIndex; - public InstancedArraysComponent(List layoutItems, int baseIndex) { - this.layoutItems = layoutItems; - this.baseIndex = baseIndex; + public InstancedArraysComponent(Pipeline.InstanceAssemblerContext ctx) { + this.layoutItems = ctx.structType() + .getLayout().layoutItems; + this.baseIndex = ctx.vertexType() + .getLayout() + .getAttributeCount(); } @Override @@ -30,20 +35,15 @@ public class InstancedArraysComponent implements SourceComponent { return Collections.emptyList(); } - @Override - public String source(CompilationContext ctx) { - var generated = generateInstancedArrays("Instance"); - return ctx.generatedHeader(generated, name().toString()) + generated; - } - @Override public ResourceLocation name() { return Flywheel.rl("generated_instanced_arrays"); } - public String generateInstancedArrays(String structName) { + @Override + public String source() { var builder = new GlslBuilder(); - builder.define("FlwInstance", structName); + builder.define("FlwInstance", STRUCT_NAME); int i = baseIndex; for (var field : layoutItems) { @@ -60,7 +60,7 @@ public class InstancedArraysComponent implements SourceComponent { builder.blankLine(); var structBuilder = builder.struct(); - structBuilder.setName(structName); + structBuilder.setName(STRUCT_NAME); for (var field : layoutItems) { field.addToStruct(structBuilder); @@ -68,18 +68,18 @@ public class InstancedArraysComponent implements SourceComponent { builder.blankLine(); - var func = builder.function() - .returnType(structName) - .name("flw_unpackInstance"); - - var args = layoutItems.stream() - .map(it -> new GlslExpr.Variable(it.name() + ATTRIBUTE_SUFFIX)) - .map(GlslExpr::minPrint) - .collect(Collectors.joining(", ")); - - func.statement("return " + structName + "(" + args + ");"); + // unpacking function + builder.function() + .signature(FnSignature.of(STRUCT_NAME, "flw_unpackInstance")) + .body(this::generateUnpackingBody); return builder.build(); } + private void generateUnpackingBody(GlslBlock b) { + var fields = layoutItems.stream() + .map(it -> new GlslExpr.Variable(it.name() + ATTRIBUTE_SUFFIX)) + .toList(); + b.ret(GlslExpr.call(STRUCT_NAME, fields)); + } } 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 7eb5c16b6..267b64343 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 @@ -7,7 +7,6 @@ import java.util.Set; 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.instancer.Instancer; import com.jozufozu.flywheel.api.struct.StructType; @@ -15,12 +14,13 @@ import com.jozufozu.flywheel.backend.gl.GlStateTracker; 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.TaskExecutor; -import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler; +import com.jozufozu.flywheel.core.ComponentRegistry; +import com.jozufozu.flywheel.core.Pipelines; import com.jozufozu.flywheel.core.RenderContext; +import com.jozufozu.flywheel.core.context.SimpleContext; import com.jozufozu.flywheel.core.model.Model; -import com.jozufozu.flywheel.core.uniform.UniformBuffer; import com.jozufozu.flywheel.util.FlwUtil; import com.mojang.blaze3d.systems.RenderSystem; @@ -40,12 +40,12 @@ public class InstancingEngine implements Engine { */ private final Set> instanceManagers = FlwUtil.createWeakHashSet(); - protected final ContextShader context; + protected final SimpleContext context; protected final int sqrMaxOriginDistance; protected BlockPos originCoordinate = BlockPos.ZERO; - public InstancingEngine(ContextShader context, int sqrMaxOriginDistance) { + public InstancingEngine(SimpleContext context, int sqrMaxOriginDistance) { this.context = context; this.sqrMaxOriginDistance = sqrMaxOriginDistance; } @@ -116,12 +116,13 @@ public class InstancingEngine implements Engine { var structType = desc.instance(); var material = desc.material(); - var ctx = new PipelineCompiler.Context(vertexType, material, structType, context, Components.INSTANCED_ARRAYS); + var program = FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, context, Pipelines.INSTANCED_ARRAYS); + program.bind(); - PipelineCompiler.INSTANCE.getProgram(ctx) - .bind(); - UniformBuffer.getInstance() - .sync(); + var uniformLocation = program.getUniformLocation("_flw_materialID_instancing"); + var vertexID = ComponentRegistry.materials.getVertexID(material); + var fragmentID = ComponentRegistry.materials.getFragmentID(material); + GL32.glUniform2ui(uniformLocation, vertexID, fragmentID); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index 393a0872d..9b89fe2ce 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -8,7 +8,7 @@ import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.BackendType; import com.jozufozu.flywheel.backend.SimpleBackendType; import com.jozufozu.flywheel.core.BackendTypes; -import com.jozufozu.flywheel.core.uniform.FrustumProvider; +import com.jozufozu.flywheel.core.uniform.FlwShaderUniforms; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.IntegerArgumentType; @@ -111,17 +111,17 @@ public class FlwCommands { commandBuilder.command.then(Commands.literal("debugFrustum") .then(Commands.literal("pause") .executes(context -> { - FrustumProvider.PAUSED = true; + FlwShaderUniforms.FRUSTUM_PAUSED = true; return 1; })) .then(Commands.literal("unpause") .executes(context -> { - FrustumProvider.PAUSED = false; + FlwShaderUniforms.FRUSTUM_PAUSED = false; return 1; })) .then(Commands.literal("capture") .executes(context -> { - FrustumProvider.CAPTURE = true; + FlwShaderUniforms.FRUSTUM_CAPTURE = true; return 1; }))); diff --git a/src/main/java/com/jozufozu/flywheel/core/BackendTypes.java b/src/main/java/com/jozufozu/flywheel/core/BackendTypes.java index 5436279ad..22aa862c8 100644 --- a/src/main/java/com/jozufozu/flywheel/core/BackendTypes.java +++ b/src/main/java/com/jozufozu/flywheel/core/BackendTypes.java @@ -4,6 +4,8 @@ import java.util.Collection; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; import org.jetbrains.annotations.Nullable; @@ -14,6 +16,7 @@ import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine; import com.jozufozu.flywheel.backend.instancing.indirect.IndirectEngine; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; +import com.jozufozu.flywheel.core.pipeline.SimplePipeline; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.TextComponent; @@ -55,6 +58,7 @@ public class BackendTypes { .fallback(() -> BackendTypes.BATCHING) .supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance() .instancedArraysSupported()) + .pipelineShader(Pipelines.INSTANCED_ARRAYS) .register(); /** @@ -68,6 +72,7 @@ public class BackendTypes { .fallback(() -> BackendTypes.INSTANCING) .supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance() .supportsIndirect()) + .pipelineShader(Pipelines.INDIRECT) .register(); public static BackendType register(BackendType type) { @@ -94,4 +99,12 @@ public class BackendTypes { } + public static Collection availablePipelineShaders() { + return BACKEND_TYPES.values() + .stream() + .filter(BackendType::supported) + .map(BackendType::pipelineShader) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java b/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java index c44251baf..8a05cea34 100644 --- a/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java +++ b/src/main/java/com/jozufozu/flywheel/core/ComponentRegistry.java @@ -3,31 +3,34 @@ package com.jozufozu.flywheel.core; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import org.jetbrains.annotations.Nullable; + +import com.jozufozu.flywheel.api.context.Context; 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.uniform.ShaderUniforms; import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.api.context.ContextShader; import net.minecraft.resources.ResourceLocation; public class ComponentRegistry { - private static final Registry uniformProviders = new Registry<>(); + private static final Registry uniformProviders = new Registry<>(); - public static final Set materials = new HashSet<>(); + public static final MaterialRegistry materials = new MaterialRegistry(); public static final Set> structTypes = new HashSet<>(); public static final Set vertexTypes = new HashSet<>(); - public static final Set contextShaders = new HashSet<>(); + public static final Set contextShaders = new HashSet<>(); // TODO: fill out the rest of the registry public static T register(T material) { - materials.add(material); - return material; + return materials.add(material); } public static > T register(T type) { @@ -40,31 +43,90 @@ public class ComponentRegistry { return vertexType; } - public static ContextShader register(ContextShader contextShader) { + public static T register(T contextShader) { contextShaders.add(contextShader); return contextShader; } - public static T register(T provider) { - return uniformProviders.register(provider.getUniformShader() - .getFileLoc(), provider); + public static T register(T provider) { + return uniformProviders.register(provider.uniformShader(), provider); } - public static Collection getAllUniformProviders() { + public static Collection getAllUniformProviders() { return Collections.unmodifiableCollection(uniformProviders.objects); } + @Nullable + public static ShaderUniforms getUniformProvider(ResourceLocation loc) { + return uniformProviders.get(loc); + } + private static class Registry { - private final Set files = new HashSet<>(); + private final Map files = new HashMap<>(); private final List objects = new ArrayList<>(); public O register(ResourceLocation loc, O object) { - if (files.contains(loc)) { + if (files.containsKey(loc)) { throw new IllegalArgumentException("Shader file already registered: " + loc); } - files.add(loc); + files.put(loc, object); objects.add(object); return object; } + + @Nullable + public T get(ResourceLocation loc) { + return files.get(loc); + } + } + + public static class MaterialRegistry { + + private final Set materials = new HashSet<>(); + private final MaterialSources vertexSources = new MaterialSources(); + private final MaterialSources fragmentSources = new MaterialSources(); + + public T add(T material) { + materials.add(material); + + vertexSources.register(material.vertexShader()); + fragmentSources.register(material.fragmentShader()); + + return material; + } + + /** + * @return a list of vertex shader sources where the index in the list is the shader's ID. + */ + public List vertexSources() { + return vertexSources.sourceView; + } + + /** + * @return a list of fragment shader sources where the index in the list is the shader's ID. + */ + public List fragmentSources() { + return fragmentSources.sourceView; + } + + public int getVertexID(Material material) { + return vertexSources.orderedSources.indexOf(material.vertexShader()); + } + + public int getFragmentID(Material material) { + return fragmentSources.orderedSources.indexOf(material.fragmentShader()); + } + + private static class MaterialSources { + private final Set registered = new HashSet<>(); + private final List orderedSources = new ArrayList<>(); + private final List sourceView = Collections.unmodifiableList(orderedSources); + + public void register(ResourceLocation vertexShader) { + if (registered.add(vertexShader)) { + orderedSources.add(vertexShader); + } + } + } } } diff --git a/src/main/java/com/jozufozu/flywheel/core/Components.java b/src/main/java/com/jozufozu/flywheel/core/Components.java index c9231ce09..a4502e3a7 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Components.java +++ b/src/main/java/com/jozufozu/flywheel/core/Components.java @@ -1,22 +1,9 @@ package com.jozufozu.flywheel.core; -import java.util.function.BiConsumer; - import com.jozufozu.flywheel.Flywheel; -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.backend.instancing.indirect.IndirectComponent; -import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysComponent; -import com.jozufozu.flywheel.core.crumbling.CrumblingProgram; -import com.jozufozu.flywheel.core.source.FileResolution; -import com.jozufozu.flywheel.core.source.SourceChecks; -import com.jozufozu.flywheel.core.source.SourceFile; -import com.jozufozu.flywheel.core.source.error.ErrorReporter; +import com.jozufozu.flywheel.core.context.SimpleContext; 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.uniform.FlwShaderUniforms; import com.jozufozu.flywheel.core.vertex.Formats; import com.jozufozu.flywheel.util.ResourceUtil; @@ -25,107 +12,32 @@ import net.minecraft.resources.ResourceLocation; 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, (vertexType, structType) -> new InstancedArraysComponent(structType.getLayout().layoutItems, vertexType.getLayout() - .getAttributeCount())); - public static final PipelineShader INDIRECT = new PipelineShader(GLSLVersion.V460, Pipeline.INDIRECT_DRAW, Pipeline.DRAW_FRAGMENT, (vertexType, structType) -> new IndirectComponent(structType.getLayout().layoutItems)); - public static final FileResolution UTIL_TYPES = FileResolution.get(Flywheel.rl("util/types.glsl")); + public static final FlwShaderUniforms UNIFORM_PROVIDER = ComponentRegistry.register(new FlwShaderUniforms()); + public static final SimpleContext WORLD = ComponentRegistry.register(new SimpleContext(Files.WORLD_VERTEX, Files.WORLD_FRAGMENT)); + public static final SimpleContext CRUMBLING = ComponentRegistry.register(new SimpleContext(Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT)); public static void init() { - Files.init(); Formats.init(); StructTypes.init(); 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); - } + Pipelines.init(); } 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 ORIENTED = instanceVertex(ResourceUtil.subPath(Names.ORIENTED, ".vert")); - 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")); - public static final FileResolution CUTOUT_FRAGMENT = materialFragment(ResourceUtil.subPath(Names.CUTOUT, ".frag")); - public static final FileResolution WORLD_VERTEX = contextVertex(ResourceUtil.subPath(Names.WORLD, ".vert")); - public static final FileResolution WORLD_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.WORLD, ".frag")); - 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); - } - - private static FileResolution layoutVertex(ResourceLocation location) { - return FileResolution.get(location) - .validateWith(Checks.LAYOUT_VERTEX); - } - - private static FileResolution instanceVertex(ResourceLocation location) { - return FileResolution.get(location); // .validateWith(Checks.INSTANCE_VERTEX); - } - - private static FileResolution materialVertex(ResourceLocation location) { - return FileResolution.get(location) - .validateWith(Checks.MATERIAL_VERTEX); - } - - private static FileResolution materialFragment(ResourceLocation location) { - return FileResolution.get(location) - .validateWith(Checks.MATERIAL_FRAGMENT); - } - - private static FileResolution contextVertex(ResourceLocation location) { - return FileResolution.get(location) - .validateWith(Checks.CONTEXT_VERTEX); - } - - private static FileResolution contextFragment(ResourceLocation location) { - return FileResolution.get(location) - .validateWith(Checks.CONTEXT_FRAGMENT); - } - - public static void init() { - // noop, just in case - } - } - - public static class Checks { - - public static final BiConsumer LAYOUT_VERTEX = SourceChecks.checkFunctionArity("flw_layoutVertex", 0); - public static final BiConsumer INSTANCE_VERTEX = SourceChecks.checkFunctionParameterTypeExists("flw_instanceVertex", 1, 0); - 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 final ResourceLocation UNIFORMS = Flywheel.rl("uniform/flywheel.glsl"); + public static final ResourceLocation BLOCK_LAYOUT = ResourceUtil.subPath(Names.BLOCK, ".vert"); + public static final ResourceLocation POS_TEX_NORMAL_LAYOUT = ResourceUtil.subPath(Names.POS_TEX_NORMAL, ".vert"); + public static final ResourceLocation TRANSFORMED = ResourceUtil.subPath(Names.TRANSFORMED, ".vert"); + public static final ResourceLocation ORIENTED = ResourceUtil.subPath(Names.ORIENTED, ".vert"); + public static final ResourceLocation DEFAULT_VERTEX = ResourceUtil.subPath(Names.DEFAULT, ".vert"); + public static final ResourceLocation SHADED_VERTEX = ResourceUtil.subPath(Names.SHADED, ".vert"); + public static final ResourceLocation DEFAULT_FRAGMENT = ResourceUtil.subPath(Names.DEFAULT, ".frag"); + public static final ResourceLocation CUTOUT_FRAGMENT = ResourceUtil.subPath(Names.CUTOUT, ".frag"); + public static final ResourceLocation WORLD_VERTEX = ResourceUtil.subPath(Names.WORLD, ".vert"); + public static final ResourceLocation WORLD_FRAGMENT = ResourceUtil.subPath(Names.WORLD, ".frag"); + public static final ResourceLocation CRUMBLING_VERTEX = ResourceUtil.subPath(Names.CRUMBLING, ".vert"); + public static final ResourceLocation CRUMBLING_FRAGMENT = ResourceUtil.subPath(Names.CRUMBLING, ".frag"); } public static class Names { diff --git a/src/main/java/com/jozufozu/flywheel/core/DebugRender.java b/src/main/java/com/jozufozu/flywheel/core/DebugRender.java deleted file mode 100644 index 10c8e414c..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/DebugRender.java +++ /dev/null @@ -1,104 +0,0 @@ -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/Pipelines.java b/src/main/java/com/jozufozu/flywheel/core/Pipelines.java new file mode 100644 index 000000000..c26f98c6a --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/Pipelines.java @@ -0,0 +1,36 @@ +package com.jozufozu.flywheel.core; + +import com.jozufozu.flywheel.Flywheel; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.instancing.indirect.IndirectComponent; +import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysComponent; +import com.jozufozu.flywheel.core.pipeline.SimplePipeline; + +import net.minecraft.resources.ResourceLocation; + +public class Pipelines { + public static final SimplePipeline INSTANCED_ARRAYS = SimplePipeline.builder() + .glslVersion(GLSLVersion.V420) + .vertex(Files.INSTANCED_ARRAYS_DRAW) + .fragment(Files.DRAW_FRAGMENT) + .assemblerFactory(InstancedArraysComponent::new) + .build(); + public static final SimplePipeline INDIRECT = SimplePipeline.builder() + .glslVersion(GLSLVersion.V460) + .vertex(Files.INDIRECT_DRAW) + .fragment(Files.DRAW_FRAGMENT) + .assemblerFactory(IndirectComponent::new) + .build(); + + public static void init() { + // noop + } + + public static class Files { + public static final ResourceLocation DRAW_FRAGMENT = Flywheel.rl("pipeline/draw.frag"); + public static final ResourceLocation INSTANCED_ARRAYS_DRAW = Flywheel.rl("pipeline/instanced_arrays_draw.vert"); + public static final ResourceLocation INDIRECT_DRAW = Flywheel.rl("pipeline/indirect_draw.vert"); + public static final ResourceLocation INDIRECT_CULL = Flywheel.rl("pipeline/indirect_cull.glsl"); + public static final ResourceLocation UTIL_TYPES = Flywheel.rl("util/types.glsl"); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/SourceComponent.java b/src/main/java/com/jozufozu/flywheel/core/SourceComponent.java index 2adae92b6..16ae4102f 100644 --- a/src/main/java/com/jozufozu/flywheel/core/SourceComponent.java +++ b/src/main/java/com/jozufozu/flywheel/core/SourceComponent.java @@ -2,14 +2,12 @@ package com.jozufozu.flywheel.core; import java.util.Collection; -import com.jozufozu.flywheel.core.source.CompilationContext; - import net.minecraft.resources.ResourceLocation; public interface SourceComponent { Collection included(); - String source(CompilationContext ctx); + String source(); ResourceLocation name(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/WorldProgram.java b/src/main/java/com/jozufozu/flywheel/core/WorldProgram.java deleted file mode 100644 index 0be9030ce..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/WorldProgram.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.jozufozu.flywheel.core; - -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; - -import net.minecraft.resources.ResourceLocation; - -public class WorldProgram extends GlProgram { - - // TODO: sampler registry? - protected int diffuseTex; - protected int overlayTex; - protected int lightTex; - - public WorldProgram(ResourceLocation name, int handle) { - super(name, handle); - - bind(); - registerSamplers(); - unbind(); - } - - protected void registerSamplers() { - diffuseTex = setSamplerBinding("flw_diffuseTex", 0); - overlayTex = setSamplerBinding("flw_overlayTex", 1); - lightTex = setSamplerBinding("flw_lightTex", 2); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/DebugCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/DebugCompiler.java deleted file mode 100644 index 322d2777f..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/compile/DebugCompiler.java +++ /dev/null @@ -1,95 +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.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(); - - StringBuilder source = new StringBuilder(CompileUtil.generateHeader(GLSLVersion.V420, ctx.type)); - - var file = ctx.source.getFile(); - for (var include : file.flattenedImports) { - source.append(include.source(index)); - } - - source.append(file.source(index)); - - try { - return new GlShader(source.toString(), 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/Memoizer.java b/src/main/java/com/jozufozu/flywheel/core/compile/Memoizer.java deleted file mode 100644 index f2dbc95f1..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/compile/Memoizer.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.jozufozu.flywheel.core.compile; - -import java.util.HashMap; -import java.util.Map; - -public abstract class Memoizer { - - private final Map map = new HashMap<>(); - - public V get(K key) { - return map.computeIfAbsent(key, this::_create); - } - - public void invalidate() { - map.values().forEach(this::_destroy); - map.clear(); - } - - protected abstract V _create(K key); - - protected abstract void _destroy(V value); -} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramAssembler.java b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramAssembler.java deleted file mode 100644 index 8a549859d..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramAssembler.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.jozufozu.flywheel.core.compile; - -import static org.lwjgl.opengl.GL11.GL_TRUE; -import static org.lwjgl.opengl.GL20.GL_LINK_STATUS; -import static org.lwjgl.opengl.GL20.glAttachShader; -import static org.lwjgl.opengl.GL20.glCreateProgram; -import static org.lwjgl.opengl.GL20.glGetProgramInfoLog; -import static org.lwjgl.opengl.GL20.glGetProgrami; -import static org.lwjgl.opengl.GL20.glLinkProgram; - -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.backend.gl.shader.GlShader; - -import net.minecraft.resources.ResourceLocation; - -public class ProgramAssembler { - public final int program; - private final ResourceLocation name; - - public ProgramAssembler(ResourceLocation name) { - this.name = name; - this.program = glCreateProgram(); - } - - /** - * Links the attached shaders to this program. - */ - public ProgramAssembler link() { - glLinkProgram(this.program); - - String log = glGetProgramInfoLog(this.program); - - if (!log.isEmpty()) { - Backend.LOGGER.debug("Program link log for " + name + ": " + log); - } - - int result = glGetProgrami(this.program, GL_LINK_STATUS); - - if (result != GL_TRUE) { - throw new RuntimeException("Shader program linking failed, see log for details"); - } - - return this; - } - - public ProgramAssembler attachShader(GlShader glShader) { - glAttachShader(this.program, glShader.handle()); - return this; - } - - public GlProgram build(GlProgram.Factory factory) { - return factory.create(name, program); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ShaderCompilationException.java b/src/main/java/com/jozufozu/flywheel/core/compile/ShaderCompilationException.java deleted file mode 100644 index caa848fc0..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/compile/ShaderCompilationException.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jozufozu.flywheel.core.compile; - -import org.lwjgl.opengl.GL20; - -import com.jozufozu.flywheel.core.source.CompilationContext; -import com.jozufozu.flywheel.core.source.ShaderLoadingException; - -public class ShaderCompilationException extends ShaderLoadingException { - - private final int shaderHandle; - - public String errors = ""; - - public ShaderCompilationException(String message, int shaderHandle) { - super(message); - this.shaderHandle = shaderHandle; - } - - public ShaderLoadingException withErrorLog(CompilationContext ctx) { - if (this.shaderHandle == -1) - return this; - - this.errors = ctx.parseErrors(GL20.glGetShaderInfoLog(this.shaderHandle)); - - return this; - } - - @Override - public String getMessage() { - return super.getMessage() + '\n' + this.errors; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/context/SimpleContext.java b/src/main/java/com/jozufozu/flywheel/core/context/SimpleContext.java new file mode 100644 index 000000000..73ee24389 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/context/SimpleContext.java @@ -0,0 +1,27 @@ +package com.jozufozu.flywheel.core.context; + +import com.jozufozu.flywheel.api.context.Context; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; + +import net.minecraft.resources.ResourceLocation; + +public record SimpleContext(ResourceLocation vertexShader, ResourceLocation fragmentShader) implements Context { + @Override + public void onProgramLink(GlProgram program) { + program.bind(); + program.setSamplerBinding("flw_diffuseTex", 0); + program.setSamplerBinding("flw_overlayTex", 1); + program.setSamplerBinding("flw_lightTex", 2); + GlProgram.unbind(); + } + + @Override + public ResourceLocation vertexShader() { + return vertexShader; + } + + @Override + public ResourceLocation fragmentShader() { + return fragmentShader; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java deleted file mode 100644 index 777112ee6..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.jozufozu.flywheel.core.crumbling; - -import com.jozufozu.flywheel.core.WorldProgram; - -import net.minecraft.resources.ResourceLocation; - -public class CrumblingProgram extends WorldProgram { - public CrumblingProgram(ResourceLocation name, int handle) { - super(name, handle); - } - - @Override - protected void registerSamplers() { - diffuseTex = setSamplerBinding("flw_diffuseTex", 0); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java b/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java index 50df64305..d94c817e5 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java @@ -1,7 +1,7 @@ package com.jozufozu.flywheel.core.layout; -import com.jozufozu.flywheel.core.source.generate.GlslBuilder; import com.jozufozu.flywheel.core.source.generate.GlslExpr; +import com.jozufozu.flywheel.core.source.generate.GlslStruct; public record LayoutItem(InputType type, String name) { public GlslExpr unpackField(GlslExpr.Variable struct) { @@ -9,11 +9,11 @@ public record LayoutItem(InputType type, String name) { .transform(type()::unpack); } - public void addToStruct(GlslBuilder.StructBuilder structBuilder) { - structBuilder.addField(type().typeName(), name()); + public void addToStruct(GlslStruct glslStruct) { + glslStruct.addField(type().typeName(), name()); } - public void addPackedToStruct(GlslBuilder.StructBuilder packed) { + public void addPackedToStruct(GlslStruct packed) { packed.addField(type().packedTypeName(), name()); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/material/SimpleMaterial.java b/src/main/java/com/jozufozu/flywheel/core/material/SimpleMaterial.java index e8852bb1a..a1b98c8cb 100644 --- a/src/main/java/com/jozufozu/flywheel/core/material/SimpleMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/core/material/SimpleMaterial.java @@ -3,20 +3,20 @@ package com.jozufozu.flywheel.core.material; import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.core.ComponentRegistry; import com.jozufozu.flywheel.core.Components; -import com.jozufozu.flywheel.core.source.FileResolution; import net.minecraft.client.renderer.RenderStateShard; import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; public class SimpleMaterial implements Material { - protected final FileResolution vertexShader; - protected final FileResolution fragmentShader; + protected final ResourceLocation vertexShader; + protected final ResourceLocation fragmentShader; protected final Runnable setup; protected final Runnable clear; protected final RenderType batchingRenderType; protected final VertexTransformer vertexTransformer; - public SimpleMaterial(FileResolution vertexShader, FileResolution fragmentShader, Runnable setup, Runnable clear, RenderType batchingRenderType, VertexTransformer vertexTransformer) { + public SimpleMaterial(ResourceLocation vertexShader, ResourceLocation fragmentShader, Runnable setup, Runnable clear, RenderType batchingRenderType, VertexTransformer vertexTransformer) { this.vertexShader = vertexShader; this.fragmentShader = fragmentShader; this.setup = setup; @@ -30,12 +30,12 @@ public class SimpleMaterial implements Material { } @Override - public FileResolution getVertexShader() { + public ResourceLocation vertexShader() { return vertexShader; } @Override - public FileResolution getFragmentShader() { + public ResourceLocation fragmentShader() { return fragmentShader; } @@ -60,22 +60,23 @@ public class SimpleMaterial implements Material { } public static class Builder { - protected FileResolution vertexShader = Components.Files.DEFAULT_VERTEX; - protected FileResolution fragmentShader = Components.Files.DEFAULT_FRAGMENT; + protected ResourceLocation vertexShader = Components.Files.DEFAULT_VERTEX; + protected ResourceLocation fragmentShader = Components.Files.DEFAULT_FRAGMENT; protected Runnable setup = () -> {}; protected Runnable clear = () -> {}; protected RenderType batchingRenderType = RenderType.solid(); - protected VertexTransformer vertexTransformer = (vertexList, level) -> {}; + protected VertexTransformer vertexTransformer = (vertexList, level) -> { + }; public Builder() { } - public Builder vertexShader(FileResolution vertexShader) { + public Builder vertexShader(ResourceLocation vertexShader) { this.vertexShader = vertexShader; return this; } - public Builder fragmentShader(FileResolution fragmentShader) { + public Builder fragmentShader(ResourceLocation fragmentShader) { this.fragmentShader = fragmentShader; return this; } diff --git a/src/main/java/com/jozufozu/flywheel/core/pipeline/SimplePipeline.java b/src/main/java/com/jozufozu/flywheel/core/pipeline/SimplePipeline.java new file mode 100644 index 000000000..8717a55c2 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/pipeline/SimplePipeline.java @@ -0,0 +1,87 @@ +package com.jozufozu.flywheel.core.pipeline; + +import com.jozufozu.flywheel.api.pipeline.Pipeline; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.core.SourceComponent; + +import net.minecraft.resources.ResourceLocation; + +public final class SimplePipeline implements Pipeline { + private final GLSLVersion glslVersion; + private final ResourceLocation vertex; + private final ResourceLocation fragment; + private final InstanceAssemblerFactory factory; + + public SimplePipeline(GLSLVersion glslVersion, ResourceLocation vertex, ResourceLocation fragment, InstanceAssemblerFactory factory) { + this.glslVersion = glslVersion; + this.vertex = vertex; + this.fragment = fragment; + this.factory = factory; + } + + /** + * Generate the source component necessary to convert a packed {@link StructType} into its shader representation. + * + * @return A source component defining functions that unpack a representation of the given struct type. + */ + @Override + public SourceComponent assemble(InstanceAssemblerContext context) { + return factory.apply(context); + } + + @Override + public GLSLVersion glslVersion() { + return glslVersion; + } + + @Override + public ResourceLocation vertexShader() { + return vertex; + } + + @Override + public ResourceLocation fragmentShader() { + return fragment; + } + + @FunctionalInterface + public interface InstanceAssemblerFactory { + SourceComponent apply(InstanceAssemblerContext context); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private GLSLVersion glslVersion; + private ResourceLocation vertex; + private ResourceLocation fragment; + private InstanceAssemblerFactory factory; + + public Builder glslVersion(GLSLVersion glslVersion) { + this.glslVersion = glslVersion; + return this; + } + + public Builder vertex(ResourceLocation vertex) { + this.vertex = vertex; + return this; + } + + public Builder fragment(ResourceLocation fragment) { + this.fragment = fragment; + return this; + } + + public Builder assemblerFactory(InstanceAssemblerFactory factory) { + this.factory = factory; + return this; + } + + public SimplePipeline build() { + return new SimplePipeline(glslVersion, vertex, fragment, factory); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/CompilationContext.java b/src/main/java/com/jozufozu/flywheel/core/source/CompilationContext.java deleted file mode 100644 index c962694d3..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/source/CompilationContext.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.jozufozu.flywheel.core.source; - -import java.util.ArrayList; -import java.util.List; - -import org.jetbrains.annotations.Nullable; - -import com.jozufozu.flywheel.core.source.error.ErrorBuilder; -import com.jozufozu.flywheel.core.source.span.Span; - -public class CompilationContext { - public final List files = new ArrayList<>(); - - private String generatedSource = ""; - private int generatedLines = 0; - - - String sourceHeader(SourceFile sourceFile) { - return "#line " + 0 + ' ' + getOrCreateFileID(sourceFile) + " // " + sourceFile.name + '\n'; - } - - public String generatedHeader(String generatedCode, @Nullable String comment) { - generatedSource += generatedCode; - int lines = generatedCode.split("\n").length; - - var out = "#line " + generatedLines + ' ' + 0; - - generatedLines += lines; - - if (comment != null) { - out += " // " + comment; - } - - return out + '\n'; - } - - public boolean contains(SourceFile sourceFile) { - return files.contains(sourceFile); - } - - /** - * Returns an arbitrary file ID for use this compilation context, or generates one if missing. - * - * @param sourceFile The file to retrieve the ID for. - * @return A file ID unique to the given sourceFile. - */ - private int getOrCreateFileID(SourceFile sourceFile) { - int i = files.indexOf(sourceFile); - if (i != -1) { - return i + 1; - } - - files.add(sourceFile); - return files.size(); - } - - public Span getLineSpan(int fileId, int lineNo) { - if (fileId == 0) { - // TODO: Valid spans for generated code. - return null; - } - return getFile(fileId).getLineSpanNoWhitespace(lineNo); - } - - private SourceFile getFile(int fileId) { - return files.get(fileId - 1); - } - - public String parseErrors(String log) { - List lines = log.lines() - .toList(); - - StringBuilder errors = new StringBuilder(); - for (String line : lines) { - ErrorBuilder builder = parseCompilerError(line); - - if (builder != null) { - errors.append(builder.build()); - } else { - errors.append(line).append('\n'); - } - } - return errors.toString(); - } - - @Nullable - private ErrorBuilder parseCompilerError(String line) { - try { - return ErrorBuilder.fromLogLine(this, line); - } catch (Exception ignored) { - } - - return null; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java b/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java deleted file mode 100644 index ea4ca0d43..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java +++ /dev/null @@ -1,186 +0,0 @@ -package com.jozufozu.flywheel.core.source; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.BiConsumer; - -import com.jozufozu.flywheel.core.source.error.ErrorBuilder; -import com.jozufozu.flywheel.core.source.error.ErrorReporter; -import com.jozufozu.flywheel.core.source.span.Span; - -import net.minecraft.resources.ResourceLocation; - -/** - * A reference to a source file that might not be loaded when the owning object is created. - * - *

- * FileResolutions are used primarily while parsing import statements. {@link FileResolution#file} is initially - * null, but will be populated later on, after all SourceFiles are loaded (assuming - * {@link FileResolution#fileLoc} references an actual file). - *

- */ -public class FileResolution { - - private static final Map ALL = new HashMap<>(); - private static final Map WEAK = new HashMap<>(); - private static boolean tooLate = false; - - /** - * Extra info about where this resolution is required. Includes shader Spans. - */ - private final List neededAt = new ArrayList<>(); - private final List> checks = new ArrayList<>(); - - private final ResourceLocation fileLoc; - private final boolean weak; - - private SourceFile file; - - private FileResolution(ResourceLocation fileLoc, boolean weak) { - this.fileLoc = fileLoc; - this.weak = weak; - } - - public static FileResolution get(ResourceLocation file) { - if (!tooLate) { - return ALL.computeIfAbsent(file, loc -> new FileResolution(loc, false)); - } else { - // Lock the map after resolution has run. - FileResolution fileResolution = ALL.get(file); - - // ...so crash immediately if the file isn't found. - if (fileResolution == null) { - throw new ShaderLoadingException("could not find source for file: " + file); - } - - return fileResolution; - } - } - - /** - * Weak resolutions don't persist through resource reloads.

- * This should be used inside parsing code. - * - * @param file The location of the file to resolve. - * @return A weak resolution for the given file. - */ - public static FileResolution weak(ResourceLocation file) { - FileResolution fileResolution = ALL.get(file); - - if (fileResolution != null) { - return fileResolution; - } - // never too late for weak resolutions. - return WEAK.computeIfAbsent(file, loc -> new FileResolution(loc, true)); - } - - /** - * Try and resolve all referenced source files, printing errors if any aren't found. - */ - public static void run(ErrorReporter errorReporter, SourceFinder sources) { - for (FileResolution resolution : ALL.values()) { - resolution.resolve(errorReporter, sources); - } - - for (FileResolution resolution : WEAK.values()) { - resolution.resolve(errorReporter, sources); - } - - WEAK.clear(); - - tooLate = true; - } - - public static void checkAll(ErrorReporter errorReporter) { - for (FileResolution resolution : ALL.values()) { - resolution.runChecks(errorReporter); - } - } - - private void resolve(ErrorReporter errorReporter, SourceFinder sources) { - file = sources.findSource(fileLoc); - - if (file == null) { - reportMissing(errorReporter); - } - - // Let the GC do its thing - neededAt.clear(); - } - - private void reportMissing(ErrorReporter errorReporter) { - ErrorBuilder builder = errorReporter.error(String.format("could not find source for file %s", fileLoc)); - for (Span location : neededAt) { - builder.pointAtFile(location.getSourceFile()) - .pointAt(location); - } - } - - private void runChecks(ErrorReporter errorReporter) { - for (var check : checks) { - check.accept(errorReporter, file); - } - } - - public ResourceLocation getFileLoc() { - return fileLoc; - } - - /** - * Non-null if this file is resolved because there would have been a crash otherwise. - * @return The file that this resolution resolves to. - */ - public SourceFile getFile() { - return file; - } - - public boolean isWeak() { - return weak; - } - - /** - * Store the given span so this resolution can know all the places that reference the file. - * - *

- * Used for error reporting. - *

- * @param span A span where this file is referenced. - */ - public FileResolution addSpan(Span span) { - neededAt.add(span); - return this; - } - - public FileResolution validateWith(BiConsumer check) { - checks.add(check); - return this; - } - - @Override - 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/ShaderLoadingException.java b/src/main/java/com/jozufozu/flywheel/core/source/ShaderLoadingException.java index e09dfa99b..13dfc9857 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/ShaderLoadingException.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/ShaderLoadingException.java @@ -5,4 +5,8 @@ public class ShaderLoadingException extends RuntimeException { public ShaderLoadingException(String message) { super(message); } + + public ShaderLoadingException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/ShaderSources.java b/src/main/java/com/jozufozu/flywheel/core/source/ShaderSources.java index 4d8a2284b..3e4a7f21c 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/ShaderSources.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/ShaderSources.java @@ -1,13 +1,14 @@ package com.jozufozu.flywheel.core.source; import java.io.IOException; +import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collection; +import java.util.Deque; import java.util.HashMap; import java.util.Map; +import java.util.stream.Collectors; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import javax.annotation.Nonnull; import com.google.common.collect.Lists; import com.jozufozu.flywheel.core.source.error.ErrorReporter; @@ -15,72 +16,74 @@ import com.jozufozu.flywheel.util.ResourceUtil; import com.jozufozu.flywheel.util.StringUtil; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.ResourceManager; /** * The main object for loading and parsing source files. */ -public class ShaderSources implements SourceFinder { +public class ShaderSources { public static final String SHADER_DIR = "flywheel/"; public static final ArrayList EXTENSIONS = Lists.newArrayList(".vert", ".vsh", ".frag", ".fsh", ".glsl"); - private final Map shaderSources = new HashMap<>(); + private final Map cache = new HashMap<>(); - public final Index index; + /** + * Tracks where we are in the mutual recursion to detect circular imports. + */ + private final Deque findStack = new ArrayDeque<>(); - public final long loadTimeNs; - public final long indexTimeNs; - public final long totalTimeNs; + private final ResourceManager manager; + private final ErrorReporter errorReporter; public ShaderSources(ErrorReporter errorReporter, ResourceManager manager) { - long loadStart = System.nanoTime(); - - for (ResourceLocation location : getValidShaderFiles(manager)) { - try (Resource resource = manager.getResource(location)) { - String source = StringUtil.readToString(resource.getInputStream()); - - ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR); - - shaderSources.put(name, new SourceFile(errorReporter, this, name, source)); - } catch (IOException e) { - // - } - } - long loadEnd = System.nanoTime(); - - long indexStart = System.nanoTime(); - index = new Index(shaderSources); - long indexEnd = System.nanoTime(); - - loadTimeNs = loadEnd - loadStart; - indexTimeNs = indexEnd - indexStart; - totalTimeNs = indexEnd - loadStart; + this.errorReporter = errorReporter; + this.manager = manager; } - public void postResolve() { - for (SourceFile file : shaderSources.values()) { - file.postResolve(); + @Nonnull + public SourceFile find(ResourceLocation location) { + pushFindStack(location); + // Can't use computeIfAbsent because mutual recursion causes ConcurrentModificationExceptions + var out = cache.get(location); + if (out == null) { + out = load(location); + cache.put(location, out); + } + popFindStack(); + return out; + } + + @Nonnull + private SourceFile load(ResourceLocation loc) { + try { + var resource = manager.getResource(ResourceUtil.prefixed(SHADER_DIR, loc)); + + var sourceString = StringUtil.readToString(resource.getInputStream()); + + return new SourceFile(this, loc, sourceString); + } catch (IOException ioException) { + throw new ShaderLoadingException("Could not load shader " + loc, ioException); } } - @Override - @Nullable - public SourceFile findSource(ResourceLocation name) { - return shaderSources.get(name); + private void generateRecursiveImportException(ResourceLocation location) { + findStack.add(location); + String path = findStack.stream() + .dropWhile(l -> !l.equals(location)) + .map(ResourceLocation::toString) + .collect(Collectors.joining(" -> ")); + findStack.clear(); + throw new ShaderLoadingException("recursive import: " + path); } - @NotNull - private static Collection getValidShaderFiles(ResourceManager manager) { - return manager.listResources(SHADER_DIR, s -> { - for (String ext : EXTENSIONS) { - if (s.endsWith(ext)) return true; - } - return false; - }); + private void pushFindStack(ResourceLocation location) { + if (findStack.contains(location)) { + generateRecursiveImportException(location); + } + findStack.add(location); } - public String getLoadTime() { - return StringUtil.formatTime(totalTimeNs); + private void popFindStack() { + findStack.pop(); } } 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 f04bcd005..e26b9b94a 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/SourceChecks.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceChecks.java @@ -12,6 +12,17 @@ import com.jozufozu.flywheel.core.source.parse.ShaderVariable; public class SourceChecks { + // TODO: recycle to be invoked by the shader compiler + + public static final BiConsumer LAYOUT_VERTEX = checkFunctionArity("flw_layoutVertex", 0); + public static final BiConsumer INSTANCE_VERTEX = checkFunctionParameterTypeExists("flw_instanceVertex", 1, 0); + public static final BiConsumer MATERIAL_VERTEX = checkFunctionArity("flw_materialVertex", 0); + public static final BiConsumer MATERIAL_FRAGMENT = checkFunctionArity("flw_materialFragment", 0); + public static final BiConsumer CONTEXT_VERTEX = checkFunctionArity("flw_contextVertex", 0); + public static final BiConsumer CONTEXT_FRAGMENT = checkFunctionArity("flw_contextFragment", 0).andThen(checkFunctionArity("flw_initFragment", 0)); + public static final BiConsumer PIPELINE = checkFunctionArity("main", 0); + + public static BiConsumer checkFunctionArity(String name, int arity) { return (errorReporter, file) -> checkFunctionArity(errorReporter, file, name, arity); } 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 b90528ec9..4e6e33c1d 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java @@ -1,20 +1,11 @@ package com.jozufozu.flywheel.core.source; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.regex.Matcher; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.jozufozu.flywheel.core.SourceComponent; -import com.jozufozu.flywheel.core.source.error.ErrorReporter; import com.jozufozu.flywheel.core.source.parse.Import; import com.jozufozu.flywheel.core.source.parse.ShaderField; import com.jozufozu.flywheel.core.source.parse.ShaderFunction; @@ -37,9 +28,8 @@ public class SourceFile implements SourceComponent { public final ResourceLocation name; public final ShaderSources parent; - public final String source; - public final SourceLines lines; + public final SourceLines source; /** * Function lookup by name. @@ -57,30 +47,42 @@ public class SourceFile implements SourceComponent { public final ImmutableList imports; public final ImmutableMap fields; - // POST-RESOLUTION - public List flattenedImports; + public final List included; - public SourceFile(ErrorReporter errorReporter, ShaderSources parent, ResourceLocation name, String source) { - this.parent = parent; + public SourceFile(ShaderSources sourceFinder, ResourceLocation name, String source) { + this.parent = sourceFinder; this.name = name; - this.source = source; - this.lines = new SourceLines(source); + this.source = new SourceLines(source); - this.imports = parseImports(errorReporter); - this.functions = parseFunctions(); - this.structs = parseStructs(); - this.fields = parseFields(); + this.imports = parseImports(source); + this.functions = parseFunctions(source); + this.structs = parseStructs(source); + this.fields = parseFields(source); + + this.included = imports.stream() + .map(i -> i.file) + .map(Span::toString) + .distinct() + .mapMulti((file, sink) -> { + try { + var loc = new ResourceLocation(file); + var sourceFile = sourceFinder.find(loc); + sink.accept(sourceFile); + } catch (Exception ignored) { + } + }) + .toList(); } @Override public Collection included() { - return flattenedImports; + return included; } @Override - public String source(CompilationContext ctx) { - return ctx.sourceHeader(this) + this.elideImports(); + public String source() { + return this.genFinalSource(); } @Override @@ -88,52 +90,23 @@ public class SourceFile implements SourceComponent { return name; } - public void postResolve() { - this.flattenImports(); - } - - private void flattenImports() { - // somebody #used us and got resolved before we did - if (this.flattenedImports != null) { - return; - } - - if (this.imports.isEmpty()) { - this.flattenedImports = Collections.emptyList(); - return; - } - - ArrayList flat = new ArrayList<>(this.imports.size()); - - for (Import include : this.imports) { - SourceFile file = include.resolution.getFile(); - - file.flattenImports(); - - flat.addAll(file.flattenedImports); - flat.add(file); - } - - this.flattenedImports = flat.stream() - .distinct() - .toList(); - } - - public Span getLineSpan(int line) { - int begin = lines.getLineStart(line); - int end = begin + lines.getLine(line).length(); - return new StringSpan(this, lines.getCharPos(begin), lines.getCharPos(end)); + public Span getLineSpan(int lineNo) { + int begin = source.lineStartIndex(lineNo); + int end = begin + source.lineString(lineNo) + .length(); + return new StringSpan(this, source.getCharPos(begin), source.getCharPos(end)); } public Span getLineSpanNoWhitespace(int line) { - int begin = lines.getLineStart(line); - int end = begin + lines.getLine(line).length(); + int begin = source.lineStartIndex(line); + int end = begin + source.lineString(line) + .length(); while (begin < end && Character.isWhitespace(source.charAt(begin))) { begin++; } - return new StringSpan(this, lines.getCharPos(begin), lines.getCharPos(end)); + return new StringSpan(this, source.getCharPos(begin), source.getCharPos(end)); } /** @@ -142,12 +115,14 @@ public class SourceFile implements SourceComponent { * @param name The name of the struct to find. * @return null if no definition matches the name. */ - public Optional findStruct(String name) { + public Optional findStructByName(String name) { ShaderStruct struct = structs.get(name); - if (struct != null) return Optional.of(struct); + if (struct != null) { + return Optional.of(struct); + } - for (var include : flattenedImports) { + for (var include : included) { var external = include.structs.get(name); if (external != null) { @@ -169,7 +144,7 @@ public class SourceFile implements SourceComponent { if (local != null) return Optional.of(local); - for (var include : flattenedImports) { + for (var include : included) { var external = include.functions.get(name); if (external != null) { @@ -185,10 +160,10 @@ public class SourceFile implements SourceComponent { } public String printSource() { - return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers(); + return "Source for shader '" + name + "':\n" + source.printLinesWithNumbers(); } - private CharSequence elideImports() { + private String genFinalSource() { StringBuilder out = new StringBuilder(); int lastEnd = 0; @@ -203,13 +178,50 @@ public class SourceFile implements SourceComponent { out.append(this.source, lastEnd, this.source.length()); - return out; + return out.toString(); } + @Override + public String toString() { + return name.toString(); + } + + @Override + public boolean equals(Object o) { + // SourceFiles are only equal by reference. + return this == o; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + + /** + * Scan the source for {@code #use "..."} directives. + * Records the contents of the directive into an {@link Import} object, and marks the directive for elision. + */ + private ImmutableList parseImports(String source) { + Matcher uses = Import.PATTERN.matcher(source); + + var imports = ImmutableList.builder(); + + while (uses.find()) { + Span use = Span.fromMatcher(this, uses); + Span file = Span.fromMatcher(this, uses, 1); + + imports.add(new Import(use, file)); + } + + return imports.build(); + } + + /** * Scan the source for function definitions and "parse" them into objects that contain properties of the function. */ - private ImmutableMap parseFunctions() { + private ImmutableMap parseFunctions(String source) { Matcher matcher = ShaderFunction.PATTERN.matcher(source); Map functions = new HashMap<>(); @@ -220,7 +232,7 @@ public class SourceFile implements SourceComponent { Span args = Span.fromMatcher(this, matcher, 3); int blockStart = matcher.end(); - int blockEnd = findEndOfBlock(blockStart); + int blockEnd = findEndOfBlock(source, blockStart); Span self; Span body; @@ -243,7 +255,7 @@ public class SourceFile implements SourceComponent { /** * Scan the source for function definitions and "parse" them into objects that contain properties of the function. */ - private ImmutableMap parseStructs() { + private ImmutableMap parseStructs(String source) { Matcher matcher = ShaderStruct.PATTERN.matcher(source); ImmutableMap.Builder structs = ImmutableMap.builder(); @@ -251,8 +263,9 @@ public class SourceFile implements SourceComponent { Span self = Span.fromMatcher(this, matcher); Span name = Span.fromMatcher(this, matcher, 1); Span body = Span.fromMatcher(this, matcher, 2); + Span variableName = Span.fromMatcher(this, matcher, 3); - ShaderStruct shaderStruct = new ShaderStruct(self, name, body); + ShaderStruct shaderStruct = new ShaderStruct(self, name, body, variableName); structs.put(name.get(), shaderStruct); } @@ -263,7 +276,7 @@ public class SourceFile implements SourceComponent { /** * Scan the source for function definitions and "parse" them into objects that contain properties of the function. */ - private ImmutableMap parseFields() { + private ImmutableMap parseFields(String source) { Matcher matcher = ShaderField.PATTERN.matcher(source); ImmutableMap.Builder fields = ImmutableMap.builder(); @@ -280,36 +293,10 @@ public class SourceFile implements SourceComponent { return fields.build(); } - /** - * Scan the source for {@code #use "..."} directives. - * Records the contents of the directive into an {@link Import} object, and marks the directive for elision. - */ - private ImmutableList parseImports(ErrorReporter errorReporter) { - Matcher uses = Import.PATTERN.matcher(source); - - Set importedFiles = new HashSet<>(); - var imports = ImmutableList.builder(); - - while (uses.find()) { - Span use = Span.fromMatcher(this, uses); - Span file = Span.fromMatcher(this, uses, 1); - - String fileName = file.get(); - if (importedFiles.add(fileName)) { - var checked = Import.create(errorReporter, use, file); - if (checked != null) { - imports.add(checked); - } - } - } - - return imports.build(); - } - /** * Given the position of an opening brace, scans through the source for a paired closing brace. */ - private int findEndOfBlock(int start) { + private static int findEndOfBlock(String source, int start) { int blockDepth = 0; for (int i = start + 1; i < source.length(); i++) { char ch = source.charAt(i); @@ -324,20 +311,4 @@ public class SourceFile implements SourceComponent { return -1; } - - @Override - public String toString() { - return name.toString(); - } - - @Override - public boolean equals(Object o) { - // SourceFiles are only equal by reference. - return this == o; - } - - @Override - public int hashCode() { - return System.identityHashCode(this); - } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/SourceFinder.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceFinder.java deleted file mode 100644 index 5a2a6c94f..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/source/SourceFinder.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.jozufozu.flywheel.core.source; - -import org.jetbrains.annotations.Nullable; - -import net.minecraft.resources.ResourceLocation; - -/** - * A minimal source file lookup function. - */ -@FunctionalInterface -public interface SourceFinder { - - @Nullable - SourceFile findSource(ResourceLocation name); -} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/SourceLines.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceLines.java index f9ade475c..7b1b18b8c 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/SourceLines.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceLines.java @@ -3,14 +3,16 @@ package com.jozufozu.flywheel.core.source; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jetbrains.annotations.NotNull; + import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.core.source.span.CharPos; -import com.jozufozu.flywheel.util.StringUtil; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntLists; -public class SourceLines { +public class SourceLines implements CharSequence { private static final Pattern newLine = Pattern.compile("(\\r\\n|\\r|\\n)"); @@ -23,22 +25,23 @@ public class SourceLines { * 0-indexed line lookup */ private final ImmutableList lines; + public final String raw; - public SourceLines(String source) { - this.lineStarts = createLineLookup(source); - this.lines = getLines(source, lineStarts); + public SourceLines(String raw) { + this.raw = raw; + this.lineStarts = createLineLookup(raw); + this.lines = getLines(raw, lineStarts); } - public int getLineCount() { + public int count() { return lines.size(); } - public String getLine(int lineNo) { + public String lineString(int lineNo) { return lines.get(lineNo); } - public int getLineStart(int lineNo) { - + public int lineStartIndex(int lineNo) { return lineStarts.getInt(lineNo); } @@ -73,9 +76,12 @@ public class SourceLines { /** * Scan the source for line breaks, recording the position of the first character of each line. - * @param source */ private static IntList createLineLookup(String source) { + if (source.isEmpty()) { + return IntLists.emptyList(); + } + IntList l = new IntArrayList(); l.add(0); // first line is always at position 0 @@ -84,6 +90,7 @@ public class SourceLines { while (matcher.find()) { l.add(matcher.end()); } + return l; } @@ -94,9 +101,53 @@ public class SourceLines { int start = lines.getInt(i - 1); int end = lines.getInt(i); - builder.add(StringUtil.trimEnd(source.substring(start, end))); + builder.add(source.substring(start, end) + .stripTrailing()); } return builder.build(); } + + @Override + public String toString() { + return raw; + } + + @NotNull + @Override + public CharSequence subSequence(int start, int end) { + return raw.subSequence(start, end); + } + + public char charAt(int i) { + return raw.charAt(i); + } + + public int length() { + return raw.length(); + } + + public int lineStartCol(int spanLine) { + return 0; + } + + public int lineWidth(int spanLine) { + return lines.get(spanLine) + .length(); + } + + public int lineStartColTrimmed(final int line) { + final var lineString = lineString(line); + final int end = lineString.length(); + + int col = 0; + while (col < end && Character.isWhitespace(charAt(col))) { + col++; + } + return col; + } + + public int lineStartPosTrimmed(final int line) { + return lineStartIndex(line) + lineStartColTrimmed(line); + } } 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 6c156df7b..a234d9433 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 @@ -2,12 +2,9 @@ package com.jozufozu.flywheel.core.source.error; import java.util.ArrayList; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.jetbrains.annotations.Nullable; -import com.jozufozu.flywheel.core.source.CompilationContext; import com.jozufozu.flywheel.core.source.SourceFile; import com.jozufozu.flywheel.core.source.SourceLines; import com.jozufozu.flywheel.core.source.error.lines.ErrorLine; @@ -17,72 +14,61 @@ import com.jozufozu.flywheel.core.source.error.lines.SourceLine; import com.jozufozu.flywheel.core.source.error.lines.SpanHighlightLine; import com.jozufozu.flywheel.core.source.error.lines.TextLine; import com.jozufozu.flywheel.core.source.span.Span; -import com.jozufozu.flywheel.util.FlwUtil; +import com.jozufozu.flywheel.util.ConsoleColors; +import com.jozufozu.flywheel.util.StringUtil; public class ErrorBuilder { - private static final Pattern ERROR_LINE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)"); - private final List lines = new ArrayList<>(); - public ErrorBuilder() { + private ErrorBuilder() { } - public static ErrorBuilder error(CharSequence msg) { - return new ErrorBuilder() - .header(ErrorLevel.ERROR, msg); + public static ErrorBuilder create() { + return new ErrorBuilder(); } - public static ErrorBuilder compError(CharSequence msg) { - return new ErrorBuilder() - .extra(msg); + public ErrorBuilder error(String msg) { + return header(ErrorLevel.ERROR, msg); } - public static ErrorBuilder warn(CharSequence msg) { - return new ErrorBuilder() - .header(ErrorLevel.WARN, msg); + public ErrorBuilder warn(String msg) { + return header(ErrorLevel.WARN, msg); } - @Nullable - public static ErrorBuilder fromLogLine(CompilationContext env, String s) { - Matcher matcher = ERROR_LINE.matcher(s); - - if (matcher.find()) { - String fileId = matcher.group(1); - String lineNo = matcher.group(2); - String msg = matcher.group(3); - Span span = env.getLineSpan(Integer.parseInt(fileId), Integer.parseInt(lineNo)); - - if (span == null) { - return ErrorBuilder.compError("Error in generated code"); - } - - return ErrorBuilder.compError(msg) - .pointAtFile(span.getSourceFile()) - .pointAt(span, 1); - } else { - return null; - } + public ErrorBuilder hint(String msg) { + return header(ErrorLevel.HINT, msg); } - public ErrorBuilder header(ErrorLevel level, CharSequence msg) { + public ErrorBuilder note(String msg) { + return header(ErrorLevel.NOTE, msg); + } + + public ErrorBuilder header(ErrorLevel level, String msg) { lines.add(new HeaderLine(level.toString(), msg)); return this; } - public ErrorBuilder extra(CharSequence msg) { - lines.add(new TextLine(msg.toString())); + public ErrorBuilder extra(String msg) { + lines.add(new TextLine(msg)); return this; } public ErrorBuilder pointAtFile(SourceFile file) { - lines.add(new FileLine(file.name.toString())); + return pointAtFile(file.name.toString()); + } + + public ErrorBuilder pointAtFile(String file) { + lines.add(new FileLine(file)); return this; } - public ErrorBuilder hintIncludeFor(@Nullable Span span, CharSequence msg) { - if (span == null) return this; + public ErrorBuilder hintIncludeFor(@Nullable Span span, String msg) { + if (span == null) { + return this; + } + SourceFile sourceFile = span.getSourceFile(); String builder = "add " + sourceFile.importStatement() + ' ' + msg + "\n defined here:"; @@ -98,28 +84,37 @@ public class ErrorBuilder { } public ErrorBuilder pointAt(Span span, int ctxLines) { - if (span.lines() == 1) { - SourceLines lines = span.getSourceFile().lines; + SourceLines lines = span.getSourceFile().source; int spanLine = span.firstLine(); + int firstCol = span.getStart() + .col(); + int lastCol = span.getEnd() + .col(); - int firstLine = Math.max(0, spanLine - ctxLines); - int lastLine = Math.min(lines.getLineCount(), spanLine + ctxLines); + pointAtLine(lines, spanLine, ctxLines, firstCol, lastCol); + } - int firstCol = span.getStart() - .col(); - int lastCol = span.getEnd() - .col(); + return this; + } - for (int i = firstLine; i <= lastLine; i++) { - CharSequence line = lines.getLine(i); + public ErrorBuilder pointAtLine(SourceLines lines, int spanLine, int ctxLines) { + return pointAtLine(lines, spanLine, ctxLines, lines.lineStartColTrimmed(spanLine), lines.lineWidth(spanLine)); + } - this.lines.add(SourceLine.numbered(i + 1, line.toString())); + public ErrorBuilder pointAtLine(SourceLines lines, int spanLine, int ctxLines, int firstCol, int lastCol) { + int firstLine = Math.max(0, spanLine - ctxLines); + int lastLine = Math.min(lines.count(), spanLine + ctxLines); - if (i == spanLine) { - this.lines.add(new SpanHighlightLine(firstCol, lastCol)); - } + + for (int i = firstLine; i <= lastLine; i++) { + CharSequence line = lines.lineString(i); + + this.lines.add(SourceLine.numbered(i + 1, line.toString())); + + if (i == spanLine) { + this.lines.add(new SpanHighlightLine(firstCol, lastCol)); } } @@ -127,23 +122,25 @@ public class ErrorBuilder { } public String build() { - - int maxLength = -1; + int maxMargin = -1; for (ErrorLine line : lines) { - int length = line.neededMargin(); + int neededMargin = line.neededMargin(); - if (length > maxLength) { - maxLength = length; + if (neededMargin > maxMargin) { + maxMargin = neededMargin; } } StringBuilder builder = new StringBuilder(); - builder.append('\n'); for (ErrorLine line : lines) { - int length = line.neededMargin(); + int neededMargin = line.neededMargin(); - builder.append(FlwUtil.repeatChar(' ', maxLength - length)) - .append(line.build()) + if (neededMargin >= 0) { + builder.append(StringUtil.repeatChar(' ', maxMargin - neededMargin)); + } + + builder.append(line.build()) + .append(ConsoleColors.RESET) .append('\n'); } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorLevel.java b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorLevel.java index e45345124..316e3ceff 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorLevel.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorLevel.java @@ -1,9 +1,12 @@ package com.jozufozu.flywheel.core.source.error; +import com.jozufozu.flywheel.util.ConsoleColors; + public enum ErrorLevel { - WARN("warn"), - ERROR("error"), - HINT("hint"), + WARN(ConsoleColors.YELLOW + "warn"), + ERROR(ConsoleColors.RED + "error"), + HINT(ConsoleColors.WHITE_BRIGHT + "hint"), + NOTE(ConsoleColors.WHITE_BRIGHT + "note"), ; private final String error; 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 90224f0e2..b287f9627 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 @@ -2,17 +2,14 @@ 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; import com.jozufozu.flywheel.core.source.span.Span; import com.jozufozu.flywheel.util.FlwUtil; +import com.jozufozu.flywheel.util.StringUtil; public class ErrorReporter { @@ -23,15 +20,15 @@ public class ErrorReporter { } public void generateMissingStruct(SourceFile file, Span vertexName, CharSequence msg, CharSequence hint) { - Optional span = file.parent.index.getStructDefinitionsMatching(vertexName) - .stream() - .findFirst() - .map(ShaderStruct::getName); - - this.error(msg) - .pointAtFile(file) - .pointAt(vertexName, 1) - .hintIncludeFor(span.orElse(null), hint); + // Optional span = file.parent.index.getStructDefinitionsMatching(vertexName) + // .stream() + // .findFirst() + // .map(ShaderStruct::getName); + // + // this.error(msg) + // .pointAtFile(file) + // .pointAt(vertexName, 1) + // .hintIncludeFor(span.orElse(null), hint); } public void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg) { @@ -39,14 +36,14 @@ public class ErrorReporter { } public void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg, CharSequence hint) { - Optional span = file.parent.index.getFunctionDefinitionsMatching(functionName) - .stream() - .findFirst() - .map(ShaderFunction::getName); - - this.error(msg) - .pointAtFile(file) - .hintIncludeFor(span.orElse(null), hint); + // Optional span = file.parent.index.getFunctionDefinitionsMatching(functionName) + // .stream() + // .findFirst() + // .map(ShaderFunction::getName); + // + // this.error(msg) + // .pointAtFile(file) + // .hintIncludeFor(span.orElse(null), hint); } public ErrorBuilder generateFunctionArgumentCountError(String name, int requiredArguments, Span span) { @@ -64,18 +61,17 @@ public class ErrorReporter { public ErrorBuilder generateSpanError(Span span, String message) { SourceFile file = span.getSourceFile(); - return error(message) - .pointAtFile(file) + return error(message).pointAtFile(file) .pointAt(span, 2); } public ErrorBuilder generateFileError(SourceFile file, String message) { - return error(message) - .pointAtFile(file); + return error(message).pointAtFile(file); } - public ErrorBuilder error(CharSequence msg) { - var out = ErrorBuilder.error(msg); + public ErrorBuilder error(String msg) { + var out = ErrorBuilder.create() + .error(msg); reportedErrors.add(out); return out; } @@ -92,9 +88,7 @@ public class ErrorReporter { return new ShaderLoadingException(allErrors); } - public static void printLines(CharSequence source) { - String string = source.toString(); - + public static void printLines(String string) { List lines = string.lines() .toList(); @@ -107,7 +101,7 @@ public class ErrorReporter { for (int i = 0; i < size; i++) { builder.append(i) - .append(FlwUtil.repeatChar(' ', maxWidth - FlwUtil.numDigits(i))) + .append(StringUtil.repeatChar(' ', maxWidth - FlwUtil.numDigits(i))) .append("| ") .append(lines.get(i)) .append('\n'); diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/Divider.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/Divider.java index c9206c740..bca13fdc4 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/Divider.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/Divider.java @@ -2,7 +2,7 @@ package com.jozufozu.flywheel.core.source.error.lines; public enum Divider { BAR(" | "), - ARROW("-->"), + ARROW("-> "), ; private final String s; diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/FileLine.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/FileLine.java index f1facc2d5..3bcf9cbe9 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/FileLine.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/FileLine.java @@ -4,7 +4,7 @@ public record FileLine(String fileName) implements ErrorLine { @Override public String left() { - return "--"; + return "-"; } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/HeaderLine.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/HeaderLine.java index e806cc399..25cd63783 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/HeaderLine.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/HeaderLine.java @@ -4,7 +4,7 @@ public record HeaderLine(String level, CharSequence message) implements ErrorLin @Override public int neededMargin() { - return 0; + return -1; } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/FnSignature.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/FnSignature.java new file mode 100644 index 000000000..097da1c7e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/FnSignature.java @@ -0,0 +1,80 @@ +package com.jozufozu.flywheel.core.source.generate; + +import java.util.Collection; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.util.Pair; + +public record FnSignature(String returnType, String name, ImmutableList> args) { + + public static Builder create() { + return new Builder(); + } + + public static FnSignature of(String returnType, String name) { + return FnSignature.create() + .returnType(returnType) + .name(name) + .build(); + } + + public static FnSignature ofVoid(String name) { + return new FnSignature("void", name, ImmutableList.of()); + } + + public Collection createArgExpressions() { + return args.stream() + .map(Pair::second) + .map(GlslExpr::variable) + .collect(Collectors.toList()); + } + + public boolean isVoid() { + return "void".equals(returnType); + } + + public String fullDeclaration() { + return returnType + ' ' + name + '(' + args.stream() + .map(p -> p.first() + ' ' + p.second()) + .collect(Collectors.joining(", ")) + ')'; + } + + public String signatureDeclaration() { + return returnType + ' ' + name + '(' + args.stream() + .map(Pair::first) + .collect(Collectors.joining(", ")) + ')'; + } + + public static class Builder { + private String returnType; + private String name; + private final ImmutableList.Builder> args = ImmutableList.builder(); + + public Builder returnType(String returnType) { + this.returnType = returnType; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder arg(String type, String name) { + args.add(Pair.of(type, name)); + return this; + } + + public FnSignature build() { + if (returnType == null) { + throw new IllegalStateException("returnType not set"); + } + if (name == null) { + throw new IllegalStateException("name not set"); + } + return new FnSignature(returnType, name, args.build()); + } + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslBlock.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslBlock.java new file mode 100644 index 000000000..af8a3705b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslBlock.java @@ -0,0 +1,39 @@ +package com.jozufozu.flywheel.core.source.generate; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class GlslBlock { + private final List body = new ArrayList<>(); + + public static GlslBlock create() { + return new GlslBlock(); + } + + public GlslBlock add(GlslStmt stmt) { + body.add(stmt); + return this; + } + + public GlslBlock eval(GlslExpr expr) { + return add(GlslStmt.eval(expr)); + } + + public GlslBlock ret(GlslExpr call) { + add(GlslStmt.ret(call)); + return this; + } + + public GlslBlock breakStmt() { + add(GlslStmt.BREAK); + return this; + } + + public String prettyPrint() { + return body.stream() + .map(GlslStmt::prettyPrint) + .collect(Collectors.joining("\n")); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslBuilder.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslBuilder.java index b8d3bbd2a..67677abfb 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslBuilder.java @@ -4,48 +4,57 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -import com.jozufozu.flywheel.util.Pair; - public class GlslBuilder { - - private final List elements = new ArrayList<>(); + private final List elements = new ArrayList<>(); public void define(String name, String value) { add(new Define(name, value)); } - public StructBuilder struct() { - return add(new StructBuilder()); + public void undef(String key) { + add(new Undef(key)); } - public FunctionBuilder function() { - return add(new FunctionBuilder()); - } + public GlslStruct struct() { + return add(new GlslStruct()); + } - public VertexInputBuilder vertexInput() { - return add(new VertexInputBuilder()); - } + public GlslFn function() { + return add(new GlslFn()); + } - public T add(T element) { - elements.add(element); - return element; - } + public GlslVertexInput vertexInput() { + return add(new GlslVertexInput()); + } - public void blankLine() { - elements.add(Separators.BLANK_LINE); + public GlslUniformBlock uniformBlock() { + return add(new GlslUniformBlock()); + } + + public T add(T element) { + elements.add(element); + return element; + } + + public void blankLine() { + elements.add(Separators.BLANK_LINE); + } + + public void _addRaw(String sourceString) { + elements.add(() -> sourceString); } public String build() { return elements.stream() - .map(SourceElement::build) + .map(Declaration::prettyPrint) .collect(Collectors.joining("\n")); } - public interface SourceElement { - String build(); + public interface Declaration { + String prettyPrint(); } - public enum Separators implements SourceElement { + public enum Separators implements Declaration { BLANK_LINE(""), ; @@ -54,125 +63,25 @@ public class GlslBuilder { Separators(String separator) { this.separator = separator; } + @Override - public String build() { + public String prettyPrint() { return separator; } - } - public record Define(String name, String value) implements SourceElement { + public record Define(String name, String value) implements Declaration { @Override - public String build() { + public String prettyPrint() { return "#define " + name + " " + value; } } - public static class VertexInputBuilder implements SourceElement { - - private int binding; - private String type; - private String name; - - public VertexInputBuilder binding(int binding) { - this.binding = binding; - return this; - } - - public VertexInputBuilder type(String type) { - this.type = type; - return this; - } - - public VertexInputBuilder name(String name) { - this.name = name; - return this; - } - + public record Undef(String name) implements Declaration { @Override - public String build() { - return "layout(location = " + binding + ") in " + type + " " + name + ";"; + public String prettyPrint() { + return "#undef " + name; } } - public static class StructBuilder implements SourceElement { - - private final List> fields = new ArrayList<>(); - private String name; - - public void setName(String name) { - this.name = name; - } - - public void addField(String type, String name) { - fields.add(Pair.of(type, name)); - } - - private String buildFields() { - return fields.stream() - .map(p -> '\t' + p.first() + ' ' + p.second() + ';') - .collect(Collectors.joining("\n")); - } - - public String build() { - return """ - struct %s { - %s - }; - """.formatted(name, buildFields()); - } - } - - public static class FunctionBuilder implements SourceElement { - private final List> arguments = new ArrayList<>(); - private final List body = new ArrayList<>(); - private String returnType; - private String name; - - public FunctionBuilder returnType(String returnType) { - this.returnType = returnType; - return this; - } - - public FunctionBuilder name(String name) { - this.name = name; - return this; - } - - public FunctionBuilder argument(String type, String name) { - arguments.add(Pair.of(type, name)); - return this; - } - - public FunctionBuilder argumentIn(String type, String name) { - arguments.add(Pair.of("in " + type, name)); - return this; - } - - public FunctionBuilder statement(String statement) { - this.body.add(statement); - return this; - } - - - public String build() { - return """ - %s %s(%s) { - %s - } - """.formatted(returnType, name, buildArguments(), buildBody()); - } - - private String buildBody() { - return body.stream() - .map(s -> '\t' + s) - .collect(Collectors.joining("\n")); - } - - private String buildArguments() { - return arguments.stream() - .map(p -> p.first() + ' ' + p.second()) - .collect(Collectors.joining(", ")); - } - } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslExpr.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslExpr.java index 7ae03952b..cab45783f 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslExpr.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslExpr.java @@ -1,8 +1,12 @@ package com.jozufozu.flywheel.core.source.generate; +import java.util.Collection; import java.util.function.Function; +import java.util.stream.Collectors; -public sealed interface GlslExpr { +import com.google.common.collect.ImmutableList; + +public interface GlslExpr { /** * Create a glsl variable with the given name. @@ -14,7 +18,21 @@ public sealed interface GlslExpr { return new Variable(name); } - String minPrint(); + static FunctionCall call(String functionName, Collection args) { + return new FunctionCall(functionName, args); + } + + static FunctionCall0 call(String functionName) { + return new FunctionCall0(functionName); + } + + static GlslExpr literal(int expr) { + return new IntLiteral(expr); + } + + static GlslExpr literal(boolean expr) { + return new BoolLiteral(expr); + } /** * Call a one-parameter function with the given name on this expression. @@ -56,31 +74,66 @@ public sealed interface GlslExpr { return f.apply(this); } + String prettyPrint(); + record Variable(String name) implements GlslExpr { @Override - public String minPrint() { + public String prettyPrint() { return name; } + } - record FunctionCall(String name, GlslExpr target) implements GlslExpr { - @Override - public String minPrint() { - return name + "(" + target.minPrint() + ")"; + record FunctionCall(String name, Collection args) implements GlslExpr { + public FunctionCall(String name, GlslExpr target) { + this(name, ImmutableList.of(target)); } + + @Override + public String prettyPrint() { + var args = this.args.stream() + .map(GlslExpr::prettyPrint) + .collect(Collectors.joining(",")); + return name + "(" + args + ")"; + } + + } + + record FunctionCall0(String name) implements GlslExpr { + @Override + public String prettyPrint() { + return name + "()"; + } + } record Swizzle(GlslExpr target, String selection) implements GlslExpr { @Override - public String minPrint() { - return target.minPrint() + "." + selection; + public String prettyPrint() { + return target.prettyPrint() + "." + selection; } + } record Access(GlslExpr target, String argName) implements GlslExpr { @Override - public String minPrint() { - return target.minPrint() + "." + argName; + public String prettyPrint() { + return target.prettyPrint() + "." + argName; + } + + } + + record IntLiteral(int value) implements GlslExpr { + @Override + public String prettyPrint() { + return Integer.toString(value); + } + } + + record BoolLiteral(boolean value) implements GlslExpr { + @Override + public String prettyPrint() { + return Boolean.toString(value); } } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslFn.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslFn.java new file mode 100644 index 000000000..8a603b035 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslFn.java @@ -0,0 +1,28 @@ +package com.jozufozu.flywheel.core.source.generate; + +import java.util.function.Consumer; + +import com.jozufozu.flywheel.util.StringUtil; + +public class GlslFn implements GlslBuilder.Declaration { + private final GlslBlock body = new GlslBlock(); + private FnSignature signature; + + public GlslFn signature(FnSignature signature) { + this.signature = signature; + return this; + } + + public GlslFn body(Consumer f) { + f.accept(body); + return this; + } + + public String prettyPrint() { + return """ + %s { + %s + } + """.formatted(signature.fullDeclaration(), StringUtil.indent(body.prettyPrint(), 4)); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslStmt.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslStmt.java new file mode 100644 index 000000000..7b301af8d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslStmt.java @@ -0,0 +1,31 @@ +package com.jozufozu.flywheel.core.source.generate; + +public interface GlslStmt { + GlslStmt BREAK = () -> "break;"; + GlslStmt CONTINUE = () -> "continue;"; + GlslStmt RETURN = () -> "return;"; + + static GlslStmt eval(GlslExpr expr) { + return new Eval(expr); + } + + static GlslStmt ret(GlslExpr value) { + return new Return(value); + } + + String prettyPrint(); + + record Eval(GlslExpr expr) implements GlslStmt { + @Override + public String prettyPrint() { + return expr.prettyPrint() + ";"; + } + } + + record Return(GlslExpr expr) implements GlslStmt { + @Override + public String prettyPrint() { + return "return " + expr.prettyPrint() + ";"; + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslStruct.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslStruct.java new file mode 100644 index 000000000..a713117c9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslStruct.java @@ -0,0 +1,35 @@ +package com.jozufozu.flywheel.core.source.generate; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.jozufozu.flywheel.util.Pair; + +public class GlslStruct implements GlslBuilder.Declaration { + + private final List> fields = new ArrayList<>(); + private String name; + + public void setName(String name) { + this.name = name; + } + + public void addField(String type, String name) { + fields.add(Pair.of(type, name)); + } + + private String buildFields() { + return fields.stream() + .map(p -> p.first() + ' ' + p.second() + ';') + .collect(Collectors.joining("\n")); + } + + public String prettyPrint() { + return """ + struct %s { + %s + }; + """.formatted(name, buildFields().indent(4)); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslSwitch.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslSwitch.java new file mode 100644 index 000000000..120cdac64 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslSwitch.java @@ -0,0 +1,64 @@ +package com.jozufozu.flywheel.core.source.generate; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.NotNull; + +import com.jozufozu.flywheel.util.Pair; +import com.jozufozu.flywheel.util.StringUtil; + +public class GlslSwitch implements GlslStmt { + + private final GlslExpr on; + + private final List> cases = new ArrayList<>(); + private GlslBlock defaultCase = null; + + private GlslSwitch(GlslExpr on) { + this.on = on; + } + + public static GlslSwitch on(GlslExpr on) { + return new GlslSwitch(on); + } + + public void intCase(int expr, GlslBlock block) { + cases.add(Pair.of(GlslExpr.literal(expr), block)); + } + + public void defaultCase(GlslBlock block) { + defaultCase = block; + } + + @Override + public String prettyPrint() { + return """ + switch (%s) { + %s + }""".formatted(on.prettyPrint(), formatCases()); + } + + @NotNull + private String formatCases() { + var cases = this.cases.stream() + .map(GlslSwitch::prettyPrintCase) + .collect(Collectors.joining("\n")); + if (defaultCase != null) { + cases += "\ndefault:\n" + StringUtil.indent(defaultCase.prettyPrint(), 4); + } + return cases; + } + + private static String prettyPrintCase(Pair p) { + var variant = p.first() + .prettyPrint(); + var block = p.second() + .prettyPrint(); + return """ + case %s: + %s""".formatted(variant, StringUtil.indent(block, 4)); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslUniformBlock.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslUniformBlock.java new file mode 100644 index 000000000..78f3df8cb --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslUniformBlock.java @@ -0,0 +1,52 @@ +package com.jozufozu.flywheel.core.source.generate; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.jozufozu.flywheel.util.Pair; +import com.jozufozu.flywheel.util.StringUtil; + +public class GlslUniformBlock implements GlslBuilder.Declaration { + + + private String qualifier; + private int binding; + private String name; + private final List> members = new ArrayList<>(); + + @Override + public String prettyPrint() { + return """ + layout(%s, binding = %d) uniform %s { + %s + }; + """.formatted(qualifier, binding, name, StringUtil.indent(formatMembers(), 4)); + } + + private String formatMembers() { + return members.stream() + .map(p -> p.first() + " " + p.second() + ";") + .collect(Collectors.joining("\n")); + } + + public GlslUniformBlock layout(String qualifier) { + this.qualifier = qualifier; + return this; + } + + public GlslUniformBlock binding(int i) { + binding = i; + return this; + } + + public GlslUniformBlock name(String name) { + this.name = name; + return this; + } + + public GlslUniformBlock member(String typeName, String variableName) { + members.add(Pair.of(typeName, variableName)); + return this; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslVertexInput.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslVertexInput.java new file mode 100644 index 000000000..ca52e4a89 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslVertexInput.java @@ -0,0 +1,28 @@ +package com.jozufozu.flywheel.core.source.generate; + +public class GlslVertexInput implements GlslBuilder.Declaration { + + private int binding; + private String type; + private String name; + + public GlslVertexInput binding(int binding) { + this.binding = binding; + return this; + } + + public GlslVertexInput type(String type) { + this.type = type; + return this; + } + + public GlslVertexInput name(String name) { + this.name = name; + return this; + } + + @Override + public String prettyPrint() { + return "layout(location = " + binding + ") in " + type + " " + name + ";"; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/package-info.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/package-info.java similarity index 76% rename from src/main/java/com/jozufozu/flywheel/core/compile/package-info.java rename to src/main/java/com/jozufozu/flywheel/core/source/generate/package-info.java index d81aeffbb..6fa0c353e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/package-info.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/package-info.java @@ -1,5 +1,5 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -package com.jozufozu.flywheel.core.compile; +package com.jozufozu.flywheel.core.source.generate; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/source/parse/AbstractShaderElement.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/AbstractShaderElement.java deleted file mode 100644 index d230d013e..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/source/parse/AbstractShaderElement.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.jozufozu.flywheel.core.source.parse; - -import com.jozufozu.flywheel.core.source.span.Span; - -public abstract class AbstractShaderElement { - - public final Span self; - - public AbstractShaderElement(Span self) { - this.self = self; - } - -} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/parse/Import.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/Import.java index 764bcc4ef..f348b5684 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/parse/Import.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/Import.java @@ -1,52 +1,19 @@ package com.jozufozu.flywheel.core.source.parse; -import java.util.Optional; import java.util.regex.Pattern; -import org.jetbrains.annotations.Nullable; - -import com.jozufozu.flywheel.core.source.FileResolution; -import com.jozufozu.flywheel.core.source.SourceFile; -import com.jozufozu.flywheel.core.source.error.ErrorReporter; import com.jozufozu.flywheel.core.source.span.Span; -import net.minecraft.ResourceLocationException; -import net.minecraft.resources.ResourceLocation; - -public class Import extends AbstractShaderElement { +public class Import { public static final Pattern PATTERN = Pattern.compile("^\\s*#\\s*use\\s+\"(.*)\"", Pattern.MULTILINE); - public final FileResolution resolution; + public final Span self; + public final Span file; - protected Import(Span self, FileResolution resolution, Span file) { - super(self); - this.resolution = resolution.addSpan(file); + public Import(Span self, Span file) { + this.self = self; + this.file = file; } - @Nullable - public static Import create(ErrorReporter errorReporter, Span self, Span file) { - ResourceLocation fileLocation; - try { - fileLocation = new ResourceLocation(file.get()); - } catch (ResourceLocationException e) { - errorReporter.generateSpanError(file, "malformed source location"); - return null; - } - - return new Import(self, FileResolution.weak(fileLocation), file); - } - - public Optional getOptional() { - return Optional.ofNullable(resolution.getFile()); - } - - @Nullable - public SourceFile getFile() { - return resolution.getFile(); - } - - public ResourceLocation getFileLoc() { - return resolution.getFileLoc(); - } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderField.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderField.java index 39289ff22..82fe97e61 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderField.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderField.java @@ -6,16 +6,17 @@ import org.jetbrains.annotations.Nullable; import com.jozufozu.flywheel.core.source.span.Span; -public class ShaderField extends AbstractShaderElement { +public class ShaderField { public static final Pattern PATTERN = Pattern.compile("layout\\s*\\(location\\s*=\\s*(\\d+)\\)\\s+(in|out)\\s+([\\w\\d]+)\\s+" + "([\\w\\d]+)"); public final Span location; public final @Nullable Decoration decoration; public final Span type; public final Span name; + public final Span self; public ShaderField(Span self, Span location, Span inOut, Span type, Span name) { - super(self); + this.self = self; this.location = location; this.decoration = Decoration.fromSpan(inOut); diff --git a/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderFunction.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderFunction.java index 2a817e4ca..ae20a67a7 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderFunction.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderFunction.java @@ -7,13 +7,14 @@ import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.core.source.span.Span; -public class ShaderFunction extends AbstractShaderElement { +public class ShaderFunction { // https://regexr.com/60n3d public static final Pattern PATTERN = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{"); public static final Pattern argument = Pattern.compile("(?:(inout|in|out) )?(\\w+)\\s+(\\w+)"); public static final Pattern assignment = Pattern.compile("(\\w+)\\s*="); + public final Span self; private final Span type; private final Span name; @@ -23,7 +24,7 @@ public class ShaderFunction extends AbstractShaderElement { private final ImmutableList parameters; public ShaderFunction(Span self, Span type, Span name, Span args, Span body) { - super(self); + this.self = self; this.type = type; this.name = name; this.args = args; diff --git a/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderStruct.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderStruct.java index d89e5497e..f273490ac 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderStruct.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderStruct.java @@ -7,21 +7,24 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.jozufozu.flywheel.core.source.span.Span; -public class ShaderStruct extends AbstractShaderElement { +public class ShaderStruct { // https://regexr.com/61rpe - public static final Pattern PATTERN = Pattern.compile("struct\\s+([\\w\\d]*)\\s*\\{([\\w\\d\\s,;]*)}\\s*;\\s"); + public static final Pattern PATTERN = Pattern.compile("struct\\s+([\\w_]*)\\s*\\{(.*?)}\\s*([\\w_]*)?\\s*;\\s", Pattern.DOTALL); public final Span name; public final Span body; + public final Span self; + public final Span variableName; private final ImmutableList fields; private final ImmutableMap fields2Types; - public ShaderStruct(Span self, Span name, Span body) { - super(self); + public ShaderStruct(Span self, Span name, Span body, Span variableName) { + this.self = self; this.name = name; this.body = body; + this.variableName = variableName; this.fields = parseFields(); this.fields2Types = createTypeLookup(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderVariable.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderVariable.java index cf421e810..c202e8fa7 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderVariable.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderVariable.java @@ -2,15 +2,16 @@ package com.jozufozu.flywheel.core.source.parse; import com.jozufozu.flywheel.core.source.span.Span; -public class ShaderVariable extends AbstractShaderElement { +public class ShaderVariable { public final Span qualifierSpan; public final Span type; public final Span name; public final Qualifier qualifier; + public final Span self; public ShaderVariable(Span self, Span qualifier, Span type, Span name) { - super(self); + this.self = self; this.qualifierSpan = qualifier; this.type = type; this.name = name; diff --git a/src/main/java/com/jozufozu/flywheel/core/source/parse/StructField.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/StructField.java index 961ea3441..62b4649ab 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/parse/StructField.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/StructField.java @@ -4,14 +4,15 @@ import java.util.regex.Pattern; import com.jozufozu.flywheel.core.source.span.Span; -public class StructField extends AbstractShaderElement { +public class StructField { public static final Pattern fieldPattern = Pattern.compile("(\\S+)\\s*(\\S+);"); + public final Span self; public Span type; public Span name; public StructField(Span self, Span type, Span name) { - super(self); + this.self = self; this.type = type; this.name = name; } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/span/Span.java b/src/main/java/com/jozufozu/flywheel/core/source/span/Span.java index e295702b3..38abe6b36 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/span/Span.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/span/Span.java @@ -23,7 +23,7 @@ public abstract class Span implements CharSequence, Comparable { protected final CharPos end; public Span(SourceFile in, int start, int end) { - this(in, in.lines.getCharPos(start), in.lines.getCharPos(end)); + this(in, in.source.getCharPos(start), in.source.getCharPos(end)); } public Span(SourceFile in, CharPos start, CharPos end) { @@ -131,7 +131,7 @@ public abstract class Span implements CharSequence, Comparable { if (isErr()) { return Optional.empty(); } - return in.findStruct(this.toString()); + return in.findStructByName(this.toString()); } public Optional findFunction() { diff --git a/src/main/java/com/jozufozu/flywheel/core/source/span/StringSpan.java b/src/main/java/com/jozufozu/flywheel/core/source/span/StringSpan.java index f91be1f6d..5e0ef31c9 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/span/StringSpan.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/span/StringSpan.java @@ -19,8 +19,7 @@ public class StringSpan extends Span { @Override public String get() { - return in.source - .substring(start.pos(), end.pos()); + return in.source.raw.substring(start.pos(), end.pos()); } @Override 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 50b2dd6ee..a0270ade6 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 @@ -5,7 +5,6 @@ import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.CommonItems; -import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.util.RenderMath; import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; @@ -13,6 +12,8 @@ import com.mojang.math.Quaternion; import com.mojang.math.Vector3f; import com.mojang.math.Vector4f; +import net.minecraft.resources.ResourceLocation; + public class OrientedType implements StructType { public static final BufferLayout FORMAT = BufferLayout.builder() @@ -39,7 +40,7 @@ public class OrientedType implements StructType { } @Override - public FileResolution getInstanceShader() { + public ResourceLocation instanceShader() { return Components.Files.ORIENTED; } 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 296d89734..8413fedaf 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 @@ -5,11 +5,12 @@ import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.CommonItems; -import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.util.RenderMath; import com.mojang.math.Vector3f; import com.mojang.math.Vector4f; +import net.minecraft.resources.ResourceLocation; + public class TransformedType implements StructType { public static final BufferLayout FORMAT = BufferLayout.builder() @@ -35,7 +36,7 @@ public class TransformedType implements StructType { } @Override - public FileResolution getInstanceShader() { + public ResourceLocation instanceShader() { return Components.Files.TRANSFORMED; } diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/FlwShaderUniforms.java b/src/main/java/com/jozufozu/flywheel/core/uniform/FlwShaderUniforms.java new file mode 100644 index 000000000..17c7ec3e9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/FlwShaderUniforms.java @@ -0,0 +1,138 @@ +package com.jozufozu.flywheel.core.uniform; + +import java.util.function.Consumer; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.uniform.ShaderUniforms; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; +import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.RenderContext; +import com.jozufozu.flywheel.event.BeginFrameEvent; +import com.jozufozu.flywheel.util.MatrixUtil; +import com.mojang.blaze3d.systems.RenderSystem; + +import net.minecraft.core.Vec3i; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.MinecraftForge; + +public class FlwShaderUniforms implements ShaderUniforms { + + public static final int SIZE = 224; + + public static boolean FRUSTUM_PAUSED = false; + public static boolean FRUSTUM_CAPTURE = false; + public static boolean FOG_UPDATE = true; + + @Override + public int byteSize() { + return SIZE; + } + + @Override + public ResourceLocation uniformShader() { + return Components.Files.UNIFORMS; + } + + @Override + public Provider activate(long ptr) { + return new Active(ptr); + } + + public static class Active implements Provider, Consumer { + private final long ptr; + + private boolean dirty; + + public Active(long ptr) { + this.ptr = ptr; + MinecraftForge.EVENT_BUS.addListener(this); + } + + @Override + public void delete() { + MinecraftForge.EVENT_BUS.unregister(this); + } + + @Override + public boolean poll() { + boolean updated = maybeUpdateFog(); + updated |= dirty; + dirty = false; + return updated; + } + + @Override + public void accept(BeginFrameEvent event) { + if (ptr == MemoryUtil.NULL) { + return; + } + RenderContext context = event.getContext(); + + 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()); + + // don't want to mutate viewProjection + var vp = context.viewProjection() + .copy(); + vp.multiplyWithTranslation(-camX, -camY, -camZ); + + MatrixUtil.writeUnsafe(vp, ptr + 32); + MemoryUtil.memPutFloat(ptr + 96, camX); + MemoryUtil.memPutFloat(ptr + 100, camY); + MemoryUtil.memPutFloat(ptr + 104, camZ); + MemoryUtil.memPutFloat(ptr + 108, 0f); // vec4 alignment + MemoryUtil.memPutInt(ptr + 112, getConstantAmbientLightFlag(context)); + + updateFrustum(context, camX, camY, camZ); + + dirty = true; + } + + private static int getConstantAmbientLightFlag(RenderContext context) { + var constantAmbientLight = context.level() + .effects() + .constantAmbientLight(); + return constantAmbientLight ? 1 : 0; + } + + private boolean maybeUpdateFog() { + if (!FOG_UPDATE || ptr == MemoryUtil.NULL) { + return false; + } + + var color = RenderSystem.getShaderFogColor(); + + MemoryUtil.memPutFloat(ptr, color[0]); + MemoryUtil.memPutFloat(ptr + 4, color[1]); + MemoryUtil.memPutFloat(ptr + 8, color[2]); + MemoryUtil.memPutFloat(ptr + 12, color[3]); + MemoryUtil.memPutFloat(ptr + 16, RenderSystem.getShaderFogStart()); + MemoryUtil.memPutFloat(ptr + 20, RenderSystem.getShaderFogEnd()); + MemoryUtil.memPutInt(ptr + 24, RenderSystem.getShaderFogShape() + .getIndex()); + + FOG_UPDATE = false; + + return true; + } + + private void updateFrustum(RenderContext context, float camX, float camY, float camZ) { + if (FRUSTUM_PAUSED && !FRUSTUM_CAPTURE) { + return; + } + + var shiftedCuller = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ); + + shiftedCuller.getJozuPackedPlanes(ptr + 128); + + FRUSTUM_CAPTURE = false; + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/FogProvider.java b/src/main/java/com/jozufozu/flywheel/core/uniform/FogProvider.java deleted file mode 100644 index 05cfdc8b3..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/FogProvider.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.jozufozu.flywheel.core.uniform; - -import org.lwjgl.system.MemoryUtil; - -import com.jozufozu.flywheel.api.uniform.UniformProvider; -import com.jozufozu.flywheel.core.Components; -import com.jozufozu.flywheel.core.source.FileResolution; -import com.mojang.blaze3d.systems.RenderSystem; - -public class FogProvider extends UniformProvider { - - @Override - public int getActualByteSize() { - return 16 + 8 + 4; - } - - public void update() { - if (ptr == MemoryUtil.NULL) { - return; - } - - var color = RenderSystem.getShaderFogColor(); - - MemoryUtil.memPutFloat(ptr, color[0]); - MemoryUtil.memPutFloat(ptr + 4, color[1]); - MemoryUtil.memPutFloat(ptr + 8, color[2]); - MemoryUtil.memPutFloat(ptr + 12, color[3]); - MemoryUtil.memPutFloat(ptr + 16, RenderSystem.getShaderFogStart()); - MemoryUtil.memPutFloat(ptr + 20, RenderSystem.getShaderFogEnd()); - MemoryUtil.memPutInt(ptr + 24, RenderSystem.getShaderFogShape().getIndex()); - - notifier.signalChanged(); - } - - @Override - public FileResolution getUniformShader() { - return Components.Files.FOG_UNIFORMS; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java b/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java deleted file mode 100644 index 0f8346cad..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/FrustumProvider.java +++ /dev/null @@ -1,59 +0,0 @@ -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 e3ecee4ab..348801fe0 100644 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/UniformBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/core/uniform/UniformBuffer.java @@ -1,17 +1,17 @@ package com.jozufozu.flywheel.core.uniform; -import java.util.BitSet; import java.util.Collection; import java.util.List; import org.lwjgl.opengl.GL32; import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.api.uniform.UniformProvider; +import com.jozufozu.flywheel.api.uniform.ShaderUniforms; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.memory.MemoryBlock; import com.jozufozu.flywheel.core.ComponentRegistry; +import com.jozufozu.flywheel.util.FlwUtil; import com.jozufozu.flywheel.util.RenderMath; public class UniformBuffer { @@ -22,7 +22,7 @@ public class UniformBuffer { private static final boolean PO2_ALIGNMENT = RenderMath.isPowerOf2(OFFSET_ALIGNMENT); private static UniformBuffer instance; - private final List allocatedProviders; + private final ProviderSet providerSet; public static UniformBuffer getInstance() { if (instance == null) { @@ -32,51 +32,18 @@ public class UniformBuffer { } private final GlBuffer buffer; - private final MemoryBlock data; - - private final BitSet changedBytes; private UniformBuffer() { buffer = new GlBuffer(GlBufferType.UNIFORM_BUFFER); - - Collection providers = ComponentRegistry.getAllUniformProviders(); - - var builder = ImmutableList.builder(); - int totalBytes = 0; - int index = 0; - for (UniformProvider provider : providers) { - int size = alignPo2(provider.getActualByteSize(), 16); - - builder.add(new Allocated(provider, totalBytes, size, index)); - - totalBytes = alignUniformBuffer(totalBytes + size); - index++; - } - - allocatedProviders = builder.build(); - - data = MemoryBlock.mallocTracked(totalBytes); - changedBytes = new BitSet(totalBytes); - - for (Allocated p : allocatedProviders) { - p.updatePtr(data); - } + providerSet = new ProviderSet(ComponentRegistry.getAllUniformProviders()); } public void sync() { - if (changedBytes.isEmpty()) { - return; + if (providerSet.pollUpdates()) { + buffer.upload(providerSet.data); } - // TODO: upload only changed bytes - changedBytes.clear(); - - buffer.upload(data); - - int handle = buffer.handle(); - for (Allocated p : allocatedProviders) { - GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, p.index, handle, p.offset, p.size); - } + GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, 0, buffer.handle(), 0, providerSet.data.size()); } // https://stackoverflow.com/questions/3407012/rounding-up-to-the-nearest-multiple-of-a-number @@ -88,46 +55,61 @@ 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 static class LiveProvider { + private final ShaderUniforms provider; private final int offset; private final int size; - private final int index; + private ShaderUniforms.Provider activeProvider; - private Allocated(UniformProvider provider, int offset, int size, int index) { + private LiveProvider(ShaderUniforms provider, int offset, int size) { this.provider = provider; this.offset = offset; this.size = size; - this.index = index; - } - - @Override - public void signalChanged() { - changedBytes.set(offset, offset + size); } private void updatePtr(MemoryBlock bufferBase) { - provider.updatePtr(bufferBase.ptr() + offset, this); + if (activeProvider != null) { + activeProvider.delete(); + } + activeProvider = provider.activate(bufferBase.ptr() + offset); } - public UniformProvider provider() { - return provider; + public boolean maybePoll() { + return activeProvider != null && activeProvider.poll(); + } + } + + private static class ProviderSet { + private final List allocatedProviders; + + private final MemoryBlock data; + + private ProviderSet(final Collection providers) { + var builder = ImmutableList.builder(); + int totalBytes = 0; + for (ShaderUniforms provider : providers) { + int size = FlwUtil.align16(provider.byteSize()); + + builder.add(new LiveProvider(provider, totalBytes, size)); + + totalBytes += size; + } + + allocatedProviders = builder.build(); + + data = MemoryBlock.mallocTracked(totalBytes); + + for (LiveProvider p : allocatedProviders) { + p.updatePtr(data); + } } - public int offset() { - return offset; - } - - public int size() { - return size; - } - - public int index() { - return index; + public boolean pollUpdates() { + boolean changed = false; + for (LiveProvider p : allocatedProviders) { + changed |= p.maybePoll(); + } + return changed; } } } diff --git a/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java b/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java deleted file mode 100644 index fb10e3fc3..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java +++ /dev/null @@ -1,68 +0,0 @@ -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 com.jozufozu.flywheel.util.MatrixUtil; - -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.core.Vec3i; -import net.minecraft.world.phys.Vec3; -import net.minecraftforge.common.MinecraftForge; - -public class ViewProvider extends UniformProvider { - - public ViewProvider() { - MinecraftForge.EVENT_BUS.addListener(this::beginFrame); - } - - public void beginFrame(BeginFrameEvent event) { - update(event.getContext()); - } - - @Override - public int getActualByteSize() { - return 4 * 16 + 16 + 4; - } - - public void update(RenderContext context) { - if (ptr == MemoryUtil.NULL) { - return; - } - - ClientLevel level = context.level(); - - int constantAmbientLight = level.effects() - .constantAmbientLight() ? 1 : 0; - - Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(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()); - - // don't want to mutate viewProjection - var vp = context.viewProjection().copy(); - vp.multiplyWithTranslation(-camX, -camY, -camZ); - - MatrixUtil.writeUnsafe(vp, ptr); - MemoryUtil.memPutFloat(ptr + 64, camX); - MemoryUtil.memPutFloat(ptr + 68, camY); - MemoryUtil.memPutFloat(ptr + 72, camZ); - MemoryUtil.memPutInt(ptr + 76, constantAmbientLight); - - notifier.signalChanged(); - } - - @Override - public FileResolution getUniformShader() { - return Components.Files.VIEW_UNIFORMS; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java index e95692529..9ccf3245c 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java @@ -4,7 +4,8 @@ import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.CommonItems; -import com.jozufozu.flywheel.core.source.FileResolution; + +import net.minecraft.resources.ResourceLocation; public class BlockVertex implements VertexType { public static final BufferLayout FORMAT = BufferLayout.builder() @@ -22,7 +23,7 @@ public class BlockVertex implements VertexType { } @Override - public FileResolution getLayoutShader() { + public ResourceLocation layoutShader() { return Components.Files.BLOCK_LAYOUT; } diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java index 9d0ad7856..9df21e521 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java @@ -4,7 +4,8 @@ import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.CommonItems; -import com.jozufozu.flywheel.core.source.FileResolution; + +import net.minecraft.resources.ResourceLocation; public class PosTexNormalVertex implements VertexType { public static final BufferLayout FORMAT = BufferLayout.builder() @@ -19,7 +20,7 @@ public class PosTexNormalVertex implements VertexType { } @Override - public FileResolution getLayoutShader() { + public ResourceLocation layoutShader() { return Components.Files.POS_TEX_NORMAL_LAYOUT; } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/BlockEntityRenderDispatcherAccessor.java b/src/main/java/com/jozufozu/flywheel/mixin/BlockEntityRenderDispatcherAccessor.java deleted file mode 100644 index 7f7830218..000000000 --- a/src/main/java/com/jozufozu/flywheel/mixin/BlockEntityRenderDispatcherAccessor.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.jozufozu.flywheel.mixin; - -import java.util.Map; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; -import net.minecraft.world.level.block.entity.BlockEntityType; - -@Mixin(BlockEntityRenderDispatcher.class) -public interface BlockEntityRenderDispatcherAccessor { - @Accessor("renderers") - Map, BlockEntityRenderer> flywheel$getRenderers(); -} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/FogUpdateMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/FogUpdateMixin.java index 832949e1a..69a30e22a 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/FogUpdateMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/FogUpdateMixin.java @@ -5,14 +5,14 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import com.jozufozu.flywheel.core.Components; +import com.jozufozu.flywheel.core.uniform.FlwShaderUniforms; import net.minecraft.client.renderer.FogRenderer; @Mixin(FogRenderer.class) public class FogUpdateMixin { private static void flywheel$updateFog() { - Components.FOG_PROVIDER.update(); + FlwShaderUniforms.FOG_UPDATE = true; } @Inject(method = "setupNoFog", at = @At("TAIL")) diff --git a/src/main/java/com/jozufozu/flywheel/util/ConsoleColors.java b/src/main/java/com/jozufozu/flywheel/util/ConsoleColors.java new file mode 100644 index 000000000..9a67f6d00 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/ConsoleColors.java @@ -0,0 +1,77 @@ +package com.jozufozu.flywheel.util; + +// https://stackoverflow.com/a/45444716 +public class ConsoleColors { + // Reset + public static final String RESET = "\033[0m"; // Text Reset + + // Regular Colors + public static final String BLACK = "\033[0;30m"; // BLACK + public static final String RED = "\033[0;31m"; // RED + public static final String GREEN = "\033[0;32m"; // GREEN + public static final String YELLOW = "\033[0;33m"; // YELLOW + public static final String BLUE = "\033[0;34m"; // BLUE + public static final String PURPLE = "\033[0;35m"; // PURPLE + public static final String CYAN = "\033[0;36m"; // CYAN + public static final String WHITE = "\033[0;37m"; // WHITE + + // Bold + public static final String BLACK_BOLD = "\033[1;30m"; // BLACK + public static final String RED_BOLD = "\033[1;31m"; // RED + public static final String GREEN_BOLD = "\033[1;32m"; // GREEN + public static final String YELLOW_BOLD = "\033[1;33m"; // YELLOW + public static final String BLUE_BOLD = "\033[1;34m"; // BLUE + public static final String PURPLE_BOLD = "\033[1;35m"; // PURPLE + public static final String CYAN_BOLD = "\033[1;36m"; // CYAN + public static final String WHITE_BOLD = "\033[1;37m"; // WHITE + + // Underline + public static final String BLACK_UNDERLINED = "\033[4;30m"; // BLACK + public static final String RED_UNDERLINED = "\033[4;31m"; // RED + public static final String GREEN_UNDERLINED = "\033[4;32m"; // GREEN + public static final String YELLOW_UNDERLINED = "\033[4;33m"; // YELLOW + public static final String BLUE_UNDERLINED = "\033[4;34m"; // BLUE + public static final String PURPLE_UNDERLINED = "\033[4;35m"; // PURPLE + public static final String CYAN_UNDERLINED = "\033[4;36m"; // CYAN + public static final String WHITE_UNDERLINED = "\033[4;37m"; // WHITE + + // Background + public static final String BLACK_BACKGROUND = "\033[40m"; // BLACK + public static final String RED_BACKGROUND = "\033[41m"; // RED + public static final String GREEN_BACKGROUND = "\033[42m"; // GREEN + public static final String YELLOW_BACKGROUND = "\033[43m"; // YELLOW + public static final String BLUE_BACKGROUND = "\033[44m"; // BLUE + public static final String PURPLE_BACKGROUND = "\033[45m"; // PURPLE + public static final String CYAN_BACKGROUND = "\033[46m"; // CYAN + public static final String WHITE_BACKGROUND = "\033[47m"; // WHITE + + // High Intensity + public static final String BLACK_BRIGHT = "\033[0;90m"; // BLACK + public static final String RED_BRIGHT = "\033[0;91m"; // RED + public static final String GREEN_BRIGHT = "\033[0;92m"; // GREEN + public static final String YELLOW_BRIGHT = "\033[0;93m"; // YELLOW + public static final String BLUE_BRIGHT = "\033[0;94m"; // BLUE + public static final String PURPLE_BRIGHT = "\033[0;95m"; // PURPLE + public static final String CYAN_BRIGHT = "\033[0;96m"; // CYAN + public static final String WHITE_BRIGHT = "\033[0;97m"; // WHITE + + // Bold High Intensity + public static final String BLACK_BOLD_BRIGHT = "\033[1;90m"; // BLACK + public static final String RED_BOLD_BRIGHT = "\033[1;91m"; // RED + public static final String GREEN_BOLD_BRIGHT = "\033[1;92m"; // GREEN + public static final String YELLOW_BOLD_BRIGHT = "\033[1;93m";// YELLOW + public static final String BLUE_BOLD_BRIGHT = "\033[1;94m"; // BLUE + public static final String PURPLE_BOLD_BRIGHT = "\033[1;95m";// PURPLE + public static final String CYAN_BOLD_BRIGHT = "\033[1;96m"; // CYAN + public static final String WHITE_BOLD_BRIGHT = "\033[1;97m"; // WHITE + + // High Intensity backgrounds + public static final String BLACK_BACKGROUND_BRIGHT = "\033[0;100m";// BLACK + public static final String RED_BACKGROUND_BRIGHT = "\033[0;101m";// RED + public static final String GREEN_BACKGROUND_BRIGHT = "\033[0;102m";// GREEN + public static final String YELLOW_BACKGROUND_BRIGHT = "\033[0;103m";// YELLOW + public static final String BLUE_BACKGROUND_BRIGHT = "\033[0;104m";// BLUE + public static final String PURPLE_BACKGROUND_BRIGHT = "\033[0;105m"; // PURPLE + public static final String CYAN_BACKGROUND_BRIGHT = "\033[0;106m"; // CYAN + public static final String WHITE_BACKGROUND_BRIGHT = "\033[0;107m"; // WHITE +} diff --git a/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java b/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java index b40e70427..1f45c196d 100644 --- a/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java +++ b/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java @@ -1,38 +1,15 @@ package com.jozufozu.flywheel.util; -import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import java.util.stream.Stream; -import com.jozufozu.flywheel.mixin.BlockEntityRenderDispatcherAccessor; import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; -import net.minecraft.world.level.block.entity.BlockEntityType; - public class FlwUtil { - /** - * Get the (effectively global) map of BlockEntityTypes to Renderers. - * @return An immutable map of BlockEntityTypes to BlockEntityRenderers. - */ - public static Map, BlockEntityRenderer> getBlockEntityRenderers() { - Minecraft mc = Minecraft.getInstance(); - return ((BlockEntityRenderDispatcherAccessor) mc.getBlockEntityRenderDispatcher()).flywheel$getRenderers(); - } - - public static String repeatChar(char c, int n) { - char[] arr = new char[n]; - - Arrays.fill(arr, c); - - return new String(arr); - } - public static PoseStack copyPoseStack(PoseStack stack) { PoseStack copy = new PoseStack(); copy.last().pose().load(stack.last().pose()); @@ -82,7 +59,16 @@ public class FlwUtil { } public static Stream mapValues(Map map) { - return map.values().stream(); + return map.values() + .stream(); + } + + public static void noop(T object) { + // noop + } + + public static int align16(int numToRound) { + return (numToRound + 16 - 1) & -16; } public static Set createWeakHashSet() { diff --git a/src/main/java/com/jozufozu/flywheel/util/Mods.java b/src/main/java/com/jozufozu/flywheel/util/Mods.java index 27c0cb2f2..8ca9b6380 100644 --- a/src/main/java/com/jozufozu/flywheel/util/Mods.java +++ b/src/main/java/com/jozufozu/flywheel/util/Mods.java @@ -18,7 +18,7 @@ public enum Mods { } /** - * @return a boolean of whether the mod is loaded or not based on mod id + * @return a whether the mod is loaded or not based on mod id */ public boolean isLoaded() { return ModList.get().isLoaded(id); @@ -30,8 +30,10 @@ public enum Mods { * @return Optional.empty() if the mod is not loaded, otherwise an Optional of the return value of the given supplier */ public Optional runIfInstalled(Supplier> toRun) { - if (isLoaded()) - return Optional.of(toRun.get().get()); + if (isLoaded()) { + return Optional.of(toRun.get() + .get()); + } return Optional.empty(); } diff --git a/src/main/java/com/jozufozu/flywheel/util/Pair.java b/src/main/java/com/jozufozu/flywheel/util/Pair.java index 6b3a48d7d..bc7dd61bb 100644 --- a/src/main/java/com/jozufozu/flywheel/util/Pair.java +++ b/src/main/java/com/jozufozu/flywheel/util/Pair.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.util; import java.util.Objects; +import java.util.function.Function; public record Pair(F first, S second) { @@ -8,6 +9,10 @@ public record Pair(F first, S second) { return new Pair<>(first, second); } + public static Function, Pair> both(Function map) { + return pair -> of(map.apply(pair.first), map.apply(pair.second)); + } + public Pair swap() { return Pair.of(second, first); } diff --git a/src/main/java/com/jozufozu/flywheel/util/ResourceUtil.java b/src/main/java/com/jozufozu/flywheel/util/ResourceUtil.java index 476ff33c8..1498752a0 100644 --- a/src/main/java/com/jozufozu/flywheel/util/ResourceUtil.java +++ b/src/main/java/com/jozufozu/flywheel/util/ResourceUtil.java @@ -1,9 +1,14 @@ package com.jozufozu.flywheel.util; +import java.util.regex.Pattern; + import net.minecraft.resources.ResourceLocation; public class ResourceUtil { + // Match the complement of alphanumeric and underscore. + private static final Pattern UNSAFE_CHARS = Pattern.compile("[^a-zA-Z0-9_]"); + public static ResourceLocation subPath(ResourceLocation root, String subPath) { return new ResourceLocation(root.getNamespace(), root.getPath() + subPath); } @@ -17,4 +22,14 @@ public class ResourceUtil { String path = loc.getPath(); return new ResourceLocation(loc.getNamespace(), path.substring(prefix.length(), path.length() - suffix.length())); } + + public static String toSafeString(ResourceLocation rl) { + return UNSAFE_CHARS.matcher(rl.toString()) + .replaceAll("_"); + } + + public static ResourceLocation prefixed(String basePath, ResourceLocation resourceLocation) { + String path = resourceLocation.getPath(); + return new ResourceLocation(resourceLocation.getNamespace(), basePath + path); + } } diff --git a/src/main/java/com/jozufozu/flywheel/util/StringUtil.java b/src/main/java/com/jozufozu/flywheel/util/StringUtil.java index 4579e8ada..1d316b47c 100644 --- a/src/main/java/com/jozufozu/flywheel/util/StringUtil.java +++ b/src/main/java/com/jozufozu/flywheel/util/StringUtil.java @@ -12,6 +12,9 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Arrays; import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.annotation.Nonnull; import org.lwjgl.system.MemoryUtil; @@ -21,6 +24,11 @@ public class StringUtil { private static final NumberFormat THREE_DECIMAL_PLACES = new DecimalFormat("#0.000"); + public static int countLines(String s) { + return (int) s.lines() + .count(); + } + public static String formatBytes(long bytes) { if (bytes < 1024) { return bytes + " B"; @@ -56,16 +64,43 @@ public class StringUtil { .collect(Collectors.joining(", ")) + ')'; } - public static String trimEnd(String value) { - int len = value.length(); - int st = 0; - while ((st < len) && Character.isWhitespace(value.charAt(len - 1))) { - len--; + public static String trimPrefix(String s, String prefix) { + if (s.startsWith(prefix)) { + return s.substring(prefix.length()); + } else { + return s; } - return value.substring(0, len); } - public static String readToString(InputStream is) { + public static String trimSuffix(String s, String prefix) { + if (s.endsWith(prefix)) { + return s.substring(0, s.length() - prefix.length()); + } else { + return s; + } + } + + /** + * Copy of {@link String#indent(int)} with the trailing newline removed. + */ + public static String indent(String str, int n) { + if (str.isEmpty()) { + return ""; + } + Stream stream = str.lines(); + if (n > 0) { + final String spaces = repeatChar(' ', n); + stream = stream.map(s -> spaces + s); + } else if (n == Integer.MIN_VALUE) { + stream = stream.map(String::stripLeading); + } else if (n < 0) { + throw new IllegalArgumentException("Requested indentation (" + n + ") is unsupported"); + } + return stream.collect(Collectors.joining("\n")); + } + + @Nonnull + public static String readToString(InputStream is) throws IOException { ByteBuffer bytebuffer = null; try { @@ -73,17 +108,14 @@ public class StringUtil { int i = bytebuffer.position(); ((Buffer) bytebuffer).rewind(); return MemoryUtil.memASCII(bytebuffer, i); - } catch (IOException ignored) { - // } finally { if (bytebuffer != null) { FlwMemoryTracker.freeBuffer(bytebuffer); } } - - return null; } + @Nonnull public static ByteBuffer readToBuffer(InputStream is) throws IOException { if (is instanceof FileInputStream fileinputstream) { return readFileInputStream(fileinputstream); @@ -92,6 +124,7 @@ public class StringUtil { } } + @Nonnull private static ByteBuffer readInputStream(InputStream is) throws IOException { ByteBuffer bytebuffer = FlwMemoryTracker.mallocBuffer(8192); ReadableByteChannel readablebytechannel = Channels.newChannel(is); @@ -104,6 +137,7 @@ public class StringUtil { return bytebuffer; } + @Nonnull private static ByteBuffer readFileInputStream(FileInputStream fileinputstream) throws IOException { FileChannel filechannel = fileinputstream.getChannel(); ByteBuffer bytebuffer = FlwMemoryTracker.mallocBuffer((int) filechannel.size() + 1); @@ -112,4 +146,12 @@ public class StringUtil { } return bytebuffer; } + + public static String repeatChar(char c, int n) { + char[] arr = new char[n]; + + Arrays.fill(arr, c); + + return new String(arr); + } } diff --git a/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl b/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl index 11be92a1e..a8028e8b9 100644 --- a/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl +++ b/src/main/resources/assets/flywheel/flywheel/api/fragment.glsl @@ -13,6 +13,8 @@ in vec4 flw_var1; in vec4 flw_var2; in vec4 flw_var3; +flat in uint flw_materialFragmentID; + // /*const*/ vec4 flw_sampleColor; diff --git a/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl b/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl index c00b99cbd..f823366de 100644 --- a/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl +++ b/src/main/resources/assets/flywheel/flywheel/api/vertex.glsl @@ -1,4 +1,6 @@ #ifdef VERTEX_SHADER +uint flw_materialVertexID; + out vec4 flw_vertexPos; out vec4 flw_vertexColor; out vec2 flw_vertexTexCoord; @@ -12,4 +14,5 @@ out vec4 flw_var0; out vec4 flw_var1; out vec4 flw_var2; out vec4 flw_var3; +flat out uint flw_materialFragmentID; #endif diff --git a/src/main/resources/assets/flywheel/flywheel/compute/mat.glsl b/src/main/resources/assets/flywheel/flywheel/compute/mat.glsl new file mode 100644 index 000000000..4e0f5f8d1 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/compute/mat.glsl @@ -0,0 +1,50 @@ +/* +This is what generated ubershaders should look like + +uint flw_materialVertexID = 0; +uint flw_materialFragmentID = 0; + +void flw_materialVertex() { + switch (flw_materialVertexID) { + case 0: flw_materialVertex_flywheel_cutout_vert(); break; + default: break; + } +} + +void flw_materialFragment() { + switch (flw_materialFragmentID) { + case 0: flw_materialFragment_flywheel_cutout_frag(); break; + default: break; + } +} + +bool flw_discardPredicate(vec4 finalColor) { + switch (flw_materialFragmentID) { + case 0: return flw_discardPredicate_flywheel_cutout_frag(finalColor); + default: return false; + } +} + +vec4 flw_fogFilter(vec4 color) { + switch (flw_materialFragmentID) { + case 0: return flw_fogFilter_flywheel_cutout_frag(color); + default: return color; + } +} + + +void flw_materialVertex_flywheel_cutout_vert() { +} + +void flw_materialFragment_flywheel_cutout_frag() { +} + +bool flw_discardPredicate_flywheel_cutout_frag(vec4 finalColor) { + return finalColor.a < 0.1; +} + +vec4 flw_fogFilter_flywheel_cutout_frag(vec4 color) { + return linear_fog(color, flw_distance, flw_fogRange.x, flw_fogRange.y, flw_fogColor); +} + +*/ diff --git a/src/main/resources/assets/flywheel/flywheel/context/common.vert b/src/main/resources/assets/flywheel/flywheel/context/common.vert index 46730bb38..65e126de1 100644 --- a/src/main/resources/assets/flywheel/flywheel/context/common.vert +++ b/src/main/resources/assets/flywheel/flywheel/context/common.vert @@ -1,10 +1,8 @@ #use "flywheel:api/vertex.glsl" #use "flywheel:util/fog.glsl" -#use "flywheel:uniform/fog.glsl" -#use "flywheel:uniform/view.glsl" void flw_contextVertex() { - flw_distance = fog_distance(flw_vertexPos.xyz, flw_cameraPos.xyz, flw_fogShape); - gl_Position = flw_viewProjection * flw_vertexPos; + flw_distance = fog_distance(flw_vertexPos.xyz, flywheel.cameraPos.xyz, flywheel.fogShape); + gl_Position = flywheel.viewProjection * flw_vertexPos; flw_vertexNormal = normalize(flw_vertexNormal); } diff --git a/src/main/resources/assets/flywheel/flywheel/debug/debug.vert b/src/main/resources/assets/flywheel/flywheel/debug/debug.vert index 6a5d60ca1..d6c1e3380 100644 --- a/src/main/resources/assets/flywheel/flywheel/debug/debug.vert +++ b/src/main/resources/assets/flywheel/flywheel/debug/debug.vert @@ -1,7 +1,5 @@ -#use "flywheel:uniform/view.glsl" - layout(location = 0) in vec3 worldPos; void main() { - gl_Position = flw_viewProjection * vec4(worldPos, 1.0); + gl_Position = flywheel.viewProjection * vec4(worldPos, 1.0); } diff --git a/src/main/resources/assets/flywheel/flywheel/material/cutout.frag b/src/main/resources/assets/flywheel/flywheel/material/cutout.frag index 59342decf..c86c4202a 100644 --- a/src/main/resources/assets/flywheel/flywheel/material/cutout.frag +++ b/src/main/resources/assets/flywheel/flywheel/material/cutout.frag @@ -1,6 +1,5 @@ #use "flywheel:api/fragment.glsl" #use "flywheel:util/fog.glsl" -#use "flywheel:uniform/fog.glsl" void flw_materialFragment() { } @@ -11,5 +10,5 @@ bool flw_discardPredicate(vec4 finalColor) { } vec4 flw_fogFilter(vec4 color) { - return linear_fog(color, flw_distance, flw_fogRange.x, flw_fogRange.y, flw_fogColor); + return linear_fog(color, flw_distance, flywheel.fogRange.x, flywheel.fogRange.y, flywheel.fogColor); } diff --git a/src/main/resources/assets/flywheel/flywheel/material/default.frag b/src/main/resources/assets/flywheel/flywheel/material/default.frag index 808233fb4..e5ea00f6c 100644 --- a/src/main/resources/assets/flywheel/flywheel/material/default.frag +++ b/src/main/resources/assets/flywheel/flywheel/material/default.frag @@ -1,10 +1,9 @@ #use "flywheel:api/fragment.glsl" #use "flywheel:util/fog.glsl" -#use "flywheel:uniform/fog.glsl" void flw_materialFragment() { } vec4 flw_fogFilter(vec4 color) { - return linear_fog(color, flw_distance, flw_fogRange.x, flw_fogRange.y, flw_fogColor); + return linear_fog(color, flw_distance, flywheel.fogRange.x, flywheel.fogRange.y, flywheel.fogColor); } diff --git a/src/main/resources/assets/flywheel/flywheel/material/shaded.vert b/src/main/resources/assets/flywheel/flywheel/material/shaded.vert index f7ac29954..a14f8f826 100644 --- a/src/main/resources/assets/flywheel/flywheel/material/shaded.vert +++ b/src/main/resources/assets/flywheel/flywheel/material/shaded.vert @@ -1,12 +1,11 @@ #use "flywheel:api/vertex.glsl" #use "flywheel:util/diffuse.glsl" -#use "flywheel:uniform/view.glsl" void flw_materialVertex() { flw_vertexNormal = normalize(flw_vertexNormal); float diffuseFactor; - if (flw_constantAmbientLight == 1) { + if (flywheel.constantAmbientLight == 1) { diffuseFactor = diffuseNether(flw_vertexNormal); } else { diffuseFactor = diffuse(flw_vertexNormal); diff --git a/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl index b52c12d30..0362e33da 100644 --- a/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_cull.glsl @@ -1,18 +1,8 @@ #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; -}; +#use "flywheel:pipeline/indirect_draw_command.glsl" // populated by instancers layout(std430, binding = 0) restrict readonly buffer ObjectBuffer { @@ -33,8 +23,8 @@ layout(std430, binding = 3) restrict buffer 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); + bvec4 xyInside = greaterThanEqual(fma(flywheel.planes.xyX, center.xxxx, fma(flywheel.planes.xyY, center.yyyy, fma(flywheel.planes.xyZ, center.zzzz, flywheel.planes.xyW))), -radius.xxxx); + bvec2 zInside = greaterThanEqual(fma(flywheel.planes.zX, center.xx, fma(flywheel.planes.zY, center.yy, fma(flywheel.planes.zZ, center.zz, flywheel.planes.zW))), -radius.xx); return all(xyInside) && all(zInside); } diff --git a/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw.vert b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw.vert index 2a29b662b..84abac66d 100644 --- a/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw.vert +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw.vert @@ -1,4 +1,5 @@ #use "flywheel:api/vertex.glsl" +#use "flywheel:pipeline/indirect_draw_command.glsl" layout(std430, binding = 0) restrict readonly buffer ObjectBuffer { FlwPackedInstance objects[]; @@ -8,9 +9,22 @@ layout(std430, binding = 1) restrict readonly buffer TargetBuffer { uint objectIDs[]; }; +layout(std430, binding = 2) restrict readonly buffer BatchBuffer { + uint batchIDs[]; +}; + +layout(std430, binding = 3) restrict readonly buffer DrawCommands { + MeshDrawCommand drawCommands[]; +}; + void main() { uint instanceIndex = objectIDs[gl_BaseInstance + gl_InstanceID]; + uint batchID = batchIDs[instanceIndex]; FlwInstance i = flw_unpackInstance(objects[instanceIndex]); + + flw_materialVertexID = drawCommands[batchID].vertexMaterialID; + flw_materialFragmentID = drawCommands[batchID].fragmentMaterialID; + flw_layoutVertex(); flw_instanceVertex(i); flw_materialVertex(); diff --git a/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw_command.glsl b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw_command.glsl new file mode 100644 index 000000000..6699bd126 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/indirect_draw_command.glsl @@ -0,0 +1,13 @@ +#use "flywheel:util/types.glsl" + +struct MeshDrawCommand { + uint indexCount; + uint instanceCount; + uint firstIndex; + uint vertexOffset; + uint baseInstance; + + BoundingSphere boundingSphere; + uint vertexMaterialID; + uint fragmentMaterialID; +}; 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 index 47a939bf4..4e69ffbc7 100644 --- a/src/main/resources/assets/flywheel/flywheel/pipeline/instanced_arrays_draw.vert +++ b/src/main/resources/assets/flywheel/flywheel/pipeline/instanced_arrays_draw.vert @@ -1,7 +1,13 @@ #use "flywheel:api/vertex.glsl" +uniform uvec2 _flw_materialID_instancing; + void main() { flw_layoutVertex(); + + flw_materialVertexID = _flw_materialID_instancing.x; + flw_materialFragmentID = _flw_materialID_instancing.y; + FlwInstance i = flw_unpackInstance(); flw_instanceVertex(i); flw_materialVertex(); diff --git a/src/main/resources/assets/flywheel/flywheel/uniform/flywheel.glsl b/src/main/resources/assets/flywheel/flywheel/uniform/flywheel.glsl new file mode 100644 index 000000000..645929821 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/uniform/flywheel.glsl @@ -0,0 +1,20 @@ +struct FLWPackedPlanes { + vec4 xyX;// + vec4 xyY;// + vec4 xyZ;// + vec4 xyW;// + vec2 zX;// + vec2 zY;// + vec2 zZ;// + vec2 zW;// +}; + +struct flywheel_uniforms { + vec4 fogColor; + vec2 fogRange; + int fogShape; + mat4 viewProjection; + vec4 cameraPos; + int constantAmbientLight; + FLWPackedPlanes planes; +}; diff --git a/src/main/resources/assets/flywheel/flywheel/uniform/fog.glsl b/src/main/resources/assets/flywheel/flywheel/uniform/fog.glsl deleted file mode 100644 index 2acbef9e1..000000000 --- a/src/main/resources/assets/flywheel/flywheel/uniform/fog.glsl +++ /dev/null @@ -1,5 +0,0 @@ -layout(std140, binding = 1) uniform flw_fog { - vec4 flw_fogColor; - vec2 flw_fogRange; - int flw_fogShape; -}; diff --git a/src/main/resources/assets/flywheel/flywheel/uniform/frustum.glsl b/src/main/resources/assets/flywheel/flywheel/uniform/frustum.glsl deleted file mode 100644 index 39866a922..000000000 --- a/src/main/resources/assets/flywheel/flywheel/uniform/frustum.glsl +++ /dev/null @@ -1,14 +0,0 @@ -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/uniform/view.glsl b/src/main/resources/assets/flywheel/flywheel/uniform/view.glsl deleted file mode 100644 index 52170bccd..000000000 --- a/src/main/resources/assets/flywheel/flywheel/uniform/view.glsl +++ /dev/null @@ -1,5 +0,0 @@ -layout(std140, binding = 0) uniform flw_view { - mat4 flw_viewProjection; - vec4 flw_cameraPos; - int flw_constantAmbientLight; -}; diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index e72ceff1d..d97661abd 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -5,7 +5,6 @@ "compatibilityLevel": "JAVA_17", "refmap": "flywheel.refmap.json", "client": [ - "BlockEntityRenderDispatcherAccessor", "BlockEntityTypeMixin", "BufferBuilderMixin", "ClientLevelMixin",