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 165512540..5b7afb654 100644 --- a/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java +++ b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java @@ -32,6 +32,8 @@ public interface VertexType { */ VertexList createReader(ByteBuffer buffer, int vertexCount); + String getShaderHeader(); + default int getStride() { return getLayout().getStride(); } @@ -39,6 +41,4 @@ public interface VertexType { default int byteOffset(int vertexIndex) { return getStride() * vertexIndex; } - - String writeShaderHeader(); } 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 67e31c833..575e43c42 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 @@ -5,7 +5,6 @@ import org.lwjgl.opengl.GL20; import com.jozufozu.flywheel.backend.gl.GlObject; import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; import com.jozufozu.flywheel.backend.source.ShaderLoadingException; -import com.jozufozu.flywheel.core.compile.ShaderCompiler; import net.minecraft.resources.ResourceLocation; @@ -14,8 +13,8 @@ public class GlShader extends GlObject { public final ResourceLocation name; public final ShaderType type; - public GlShader(ShaderCompiler env, ShaderType type, String source) { - name = env.name; + public GlShader(ResourceLocation name, ShaderType type, String source) { + this.name = name; this.type = type; int handle = GL20.glCreateShader(type.glEnum); @@ -24,9 +23,9 @@ public class GlShader extends GlObject { String log = GL20.glGetShaderInfoLog(handle); - if (!log.isEmpty()) { - env.printShaderInfoLog(source, log, this.name); - } +// if (!log.isEmpty()) { +// env.printShaderInfoLog(source, log, this.name); +// } if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) { throw new ShaderLoadingException("Could not compile " + name + ". See log for details."); 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 c7848e19a..72a03d5dc 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 @@ -3,15 +3,12 @@ package com.jozufozu.flywheel.backend.instancing.instancing; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; -import java.util.function.Supplier; import java.util.stream.Stream; import javax.annotation.Nullable; import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.backend.RenderLayer; -import com.jozufozu.flywheel.backend.gl.GlVertexArray; -import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.core.compile.ProgramCompiler; @@ -24,7 +21,6 @@ import net.minecraft.client.Camera; import net.minecraft.client.renderer.RenderType; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; -import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; public class InstancingEngine

implements Engine { diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java index afda73953..04578dc41 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java @@ -16,7 +16,7 @@ import com.jozufozu.flywheel.backend.source.error.lines.SourceLine; import com.jozufozu.flywheel.backend.source.error.lines.SpanHighlightLine; import com.jozufozu.flywheel.backend.source.error.lines.TextLine; import com.jozufozu.flywheel.backend.source.span.Span; -import com.jozufozu.flywheel.core.compile.ShaderCompiler; +import com.jozufozu.flywheel.core.compile.FileIndex; import com.jozufozu.flywheel.util.FlwUtil; public class ErrorBuilder { @@ -45,7 +45,7 @@ public class ErrorBuilder { } @Nullable - public static ErrorBuilder fromLogLine(ShaderCompiler env, String s) { + public static ErrorBuilder fromLogLine(FileIndex env, String s) { Matcher matcher = ERROR_LINE.matcher(s); if (matcher.find()) { diff --git a/src/main/java/com/jozufozu/flywheel/core/Contexts.java b/src/main/java/com/jozufozu/flywheel/core/Contexts.java index f4dc621cd..66bf2b0f5 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Contexts.java +++ b/src/main/java/com/jozufozu/flywheel/core/Contexts.java @@ -27,8 +27,8 @@ public class Contexts { FileResolution worldBuiltins = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.WORLD, ".glsl")); FileResolution crumblingBuiltins = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.CRUMBLING, ".glsl")); - WORLD = Templates.INSTANCING.programCompiler(WorldProgram::new, worldBuiltins); - CRUMBLING = Templates.INSTANCING.programCompiler(CrumblingProgram::new, crumblingBuiltins); + WORLD = ProgramCompiler.create(Templates.INSTANCING, WorldProgram::new, worldBuiltins); + CRUMBLING = ProgramCompiler.create(Templates.INSTANCING, CrumblingProgram::new, crumblingBuiltins); } public static class Names { diff --git a/src/main/java/com/jozufozu/flywheel/core/Templates.java b/src/main/java/com/jozufozu/flywheel/core/Templates.java index 0946a6e94..59b538a2f 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Templates.java +++ b/src/main/java/com/jozufozu/flywheel/core/Templates.java @@ -1,12 +1,14 @@ package com.jozufozu.flywheel.core; import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.core.compile.FragmentTemplateData; import com.jozufozu.flywheel.core.compile.InstancingTemplateData; import com.jozufozu.flywheel.core.compile.OneShotTemplateData; import com.jozufozu.flywheel.core.compile.Template; public class Templates { - public static final Template INSTANCING = new Template(GLSLVersion.V330, InstancingTemplateData::new); - public static final Template ONE_SHOT = new Template(GLSLVersion.V150, OneShotTemplateData::new); + public static final Template INSTANCING = new Template<>(GLSLVersion.V330, InstancingTemplateData::new); + public static final Template ONE_SHOT = new Template<>(GLSLVersion.V150, OneShotTemplateData::new); + public static final Template FRAGMENT = new Template<>(GLSLVersion.V150, FragmentTemplateData::new); } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/FileIndex.java b/src/main/java/com/jozufozu/flywheel/core/compile/FileIndex.java index ec08aade6..48e435a6c 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/FileIndex.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/FileIndex.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.core.compile; import com.jozufozu.flywheel.backend.source.SourceFile; +import com.jozufozu.flywheel.backend.source.span.Span; public interface FileIndex { /** @@ -10,4 +11,10 @@ public interface FileIndex { * @return A file ID unique to the given sourceFile. */ int getFileID(SourceFile sourceFile); + + SourceFile getFile(int fileID); + + default Span getLineSpan(int fileId, int lineNo) { + return getFile(fileId).getLineSpanNoWhitespace(lineNo); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/FileIndexImpl.java b/src/main/java/com/jozufozu/flywheel/core/compile/FileIndexImpl.java new file mode 100644 index 000000000..0958c094e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/FileIndexImpl.java @@ -0,0 +1,77 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nullable; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.source.SourceFile; +import com.jozufozu.flywheel.backend.source.error.ErrorBuilder; +import com.jozufozu.flywheel.backend.source.error.ErrorReporter; + +import net.minecraft.resources.ResourceLocation; + +public class FileIndexImpl implements FileIndex { + public final List files = new ArrayList<>(); + + /** + * 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. + */ + @Override + public int getFileID(SourceFile sourceFile) { + int i = files.indexOf(sourceFile); + if (i != -1) { + return i; + } + + int size = files.size(); + files.add(sourceFile); + return size; + } + + @Override + public SourceFile getFile(int fileId) { + return files.get(fileId); + } + + + public void printShaderInfoLog(String source, String log, ResourceLocation name) { + List lines = log.lines() + .toList(); + + boolean needsSourceDump = false; + + 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'); + needsSourceDump = true; + } + } + Backend.LOGGER.error("Errors compiling '" + name + "': \n" + errors); + if (needsSourceDump) { + // TODO: generated code gets its own "file" + ErrorReporter.printLines(source); + } + } + + @Nullable + private ErrorBuilder parseCompilerError(String line) { + try { + ErrorBuilder error = ErrorBuilder.fromLogLine(this, line); + if (error != null) { + return error; + } + } catch (Exception ignored) { + } + + return null; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/FragmentData.java b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentData.java new file mode 100644 index 000000000..b50d3b2c5 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentData.java @@ -0,0 +1,8 @@ +package com.jozufozu.flywheel.core.compile; + +public interface FragmentData { + /** + * Generate the necessary glue code here. + */ + String generateFooter(); +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/FragmentTemplateData.java b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentTemplateData.java new file mode 100644 index 000000000..6e041d732 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentTemplateData.java @@ -0,0 +1,88 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.Optional; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.backend.source.SourceFile; +import com.jozufozu.flywheel.backend.source.error.ErrorReporter; +import com.jozufozu.flywheel.backend.source.parse.ShaderFunction; +import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; +import com.jozufozu.flywheel.backend.source.parse.StructField; +import com.jozufozu.flywheel.backend.source.parse.Variable; +import com.jozufozu.flywheel.backend.source.span.Span; + +public class FragmentTemplateData implements FragmentData { + public final SourceFile file; + public final Span interpolantName; + public final ShaderStruct interpolant; + public final ShaderFunction fragmentMain; + + public FragmentTemplateData(SourceFile file) { + this.file = file; + + Optional maybeFragmentMain = file.findFunction("fragment"); + + if (maybeFragmentMain.isEmpty()) { + ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined"); + throw new RuntimeException(); + } + + fragmentMain = maybeFragmentMain.get(); + ImmutableList fragmentParameters = fragmentMain.getParameters(); + + + if (fragmentParameters.size() != 1) { + ErrorReporter.generateSpanError(fragmentMain.getArgs(), "fragment function must have exactly one argument"); + throw new RuntimeException(); + } + + interpolantName = fragmentMain.getParameters().get(0).type; + + Optional maybeInterpolant = file.findStruct(interpolantName); + + if (maybeInterpolant.isEmpty()) { + ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined"); + + throw new RuntimeException(); + } + + interpolant = maybeInterpolant.get(); + } + + @Override + public String generateFooter() { + StringBuilder builder = new StringBuilder(); + prefixFields(builder, interpolant, "in", "v2f_"); + + builder.append(String.format(""" + void main() { + Fragment o; + o.color = v2f_color; + o.texCoords = v2f_texCoords; + o.light = v2f_light; + o.diffuse = v2f_diffuse; + + vec4 color = %s; + FLWFinalizeColor(color); + } + """, + fragmentMain.call("o") + )); + + return builder.toString(); + } + + public static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) { + ImmutableList fields = struct.getFields(); + + for (StructField field : fields) { + builder.append(qualifier) + .append(' ') + .append(field.type) + .append(' ') + .append(prefix) + .append(field.name) + .append(";\n"); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/InstancingTemplateData.java b/src/main/java/com/jozufozu/flywheel/core/compile/InstancingTemplateData.java index ba3db483c..875c806d9 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/InstancingTemplateData.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/InstancingTemplateData.java @@ -13,43 +13,27 @@ import com.jozufozu.flywheel.backend.source.parse.StructField; import com.jozufozu.flywheel.backend.source.parse.Variable; import com.jozufozu.flywheel.backend.source.span.Span; -public class InstancingTemplateData implements TemplateData { +public class InstancingTemplateData implements VertexData { public final SourceFile file; public final ShaderFunction vertexMain; - public final ShaderFunction fragmentMain; - public final Span interpolantName; public final Span vertexName; public final Span instanceName; - public final ShaderStruct interpolant; public final ShaderStruct instance; public InstancingTemplateData(SourceFile file) { this.file = file; Optional vertexFunc = file.findFunction("vertex"); - Optional fragmentFunc = file.findFunction("fragment"); - if (fragmentFunc.isEmpty()) { - ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined"); - } if (vertexFunc.isEmpty()) { ErrorReporter.generateFileError(file, "could not find \"vertex\" function"); - } - - if (fragmentFunc.isEmpty() || vertexFunc.isEmpty()) { throw new ShaderLoadingException(); } - fragmentMain = fragmentFunc.get(); vertexMain = vertexFunc.get(); - ImmutableList parameters = fragmentMain.getParameters(); ImmutableList vertexParams = vertexMain.getParameters(); - if (parameters.size() != 1) { - ErrorReporter.generateSpanError(fragmentMain.getArgs(), "instancing requires fragment function to have 1 argument"); - } - if (vertexParams.size() != 2) { ErrorReporter.generateSpanError(vertexMain.getArgs(), "instancing requires vertex function to have 2 arguments"); throw new ShaderLoadingException(); @@ -68,35 +52,27 @@ public class InstancingTemplateData implements TemplateData { throw new ShaderLoadingException(); } - interpolantName = parameters.get(0).type; instanceName = vertexParams.get(1).type; - Optional maybeInterpolant = file.findStruct(interpolantName); Optional maybeInstance = file.findStruct(instanceName); - if (maybeInterpolant.isEmpty()) { - ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined"); - } - if (maybeInstance.isEmpty()) { ErrorReporter.generateMissingStruct(file, instanceName, "struct not defined"); - } - - if (maybeInterpolant.isEmpty() || maybeInstance.isEmpty()) { throw new ShaderLoadingException(); } - interpolant = maybeInterpolant.get(); instance = maybeInstance.get(); } - public void vertexFooter(StringBuilder template, ShaderCompiler shader) { + @Override + public String generateFooter(FileIndex shader, VertexType vertexType) { ImmutableList fields = instance.getFields(); - VertexType vertexType = shader.vertexType; int attributeBinding = vertexType.getLayout() .getAttributeCount(); + StringBuilder template = new StringBuilder(); + for (StructField field : fields) { template.append("layout(location = ") .append(attributeBinding) @@ -109,9 +85,12 @@ public class InstancingTemplateData implements TemplateData { .append(";\n"); attributeBinding += TypeHelper.getAttributeCount(field.type); } - Template.prefixFields(template, interpolant, "out", "v2f_"); - template.append(String.format(""" + out vec4 v2f_color; + out vec2 v2f_texCoords; + out vec2 v2f_light; + out float v2f_diffuse; + void main() { Vertex v = FLWCreateVertex(); %s i; @@ -130,26 +109,26 @@ public class InstancingTemplateData implements TemplateData { } """, instanceName, - Template.assignFields(instance, "i.", "a_i_") + assignFields(instance, "i.", "a_i_") )); + + return template.toString(); } - public void fragmentFooter(StringBuilder template, FileIndex shader) { - Template.prefixFields(template, interpolant, "in", "v2f_"); + public static StringBuilder assignFields(ShaderStruct struct, String prefix1, String prefix2) { + ImmutableList fields = struct.getFields(); - template.append(String.format(""" - void main() { - Fragment o; - o.color = v2f_color; - o.texCoords = v2f_texCoords; - o.light = v2f_light; - o.diffuse = v2f_diffuse; + StringBuilder builder = new StringBuilder(); - vec4 color = %s; - FLWFinalizeColor(color); - } - """, - fragmentMain.call("o") - )); + for (StructField field : fields) { + builder.append(prefix1) + .append(field.name) + .append(" = ") + .append(prefix2) + .append(field.name) + .append(";\n"); + } + + return builder; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/OneShotTemplateData.java b/src/main/java/com/jozufozu/flywheel/core/compile/OneShotTemplateData.java index 49d409faf..21c4eda4d 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/OneShotTemplateData.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/OneShotTemplateData.java @@ -3,54 +3,33 @@ package com.jozufozu.flywheel.core.compile; import java.util.Optional; import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.source.ShaderLoadingException; import com.jozufozu.flywheel.backend.source.SourceFile; import com.jozufozu.flywheel.backend.source.error.ErrorReporter; import com.jozufozu.flywheel.backend.source.parse.ShaderFunction; -import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; import com.jozufozu.flywheel.backend.source.parse.Variable; -import com.jozufozu.flywheel.backend.source.span.Span; -public class OneShotTemplateData implements TemplateData { +public class OneShotTemplateData implements VertexData { public final SourceFile file; public final ShaderFunction vertexMain; - public final Span interpolantName; - public final ShaderStruct interpolant; - public final ShaderFunction fragmentMain; public OneShotTemplateData(SourceFile file) { this.file = file; Optional maybeVertexMain = file.findFunction("vertex"); - Optional maybeFragmentMain = file.findFunction("fragment"); if (maybeVertexMain.isEmpty()) { ErrorReporter.generateFileError(file, "could not find \"vertex\" function"); - } - - if (maybeFragmentMain.isEmpty()) { - ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined"); - } - - if (maybeVertexMain.isEmpty() || maybeFragmentMain.isEmpty()) { throw new RuntimeException(); } vertexMain = maybeVertexMain.get(); - fragmentMain = maybeFragmentMain.get(); - ImmutableList fragmentParameters = fragmentMain.getParameters(); ImmutableList vertexParameters = vertexMain.getParameters(); if (vertexParameters.size() != 1) { ErrorReporter.generateSpanError(vertexMain.getArgs(), "a basic model requires vertex function to have one argument"); - } - - if (fragmentParameters.size() != 1) { - ErrorReporter.generateSpanError(fragmentMain.getArgs(), "fragment function must have exactly one argument"); - } - - if (vertexParameters.size() != 1 || fragmentParameters.size() != 1) { throw new RuntimeException(); } @@ -64,57 +43,30 @@ public class OneShotTemplateData implements TemplateData { ErrorReporter.generateSpanError(vertexParam.qualifierSpan, "first parameter must be inout Vertex"); throw new ShaderLoadingException(); } - - interpolantName = fragmentMain.getParameters().get(0).type; - - Optional maybeInterpolant = file.findStruct(interpolantName); - - if (maybeInterpolant.isEmpty()) { - ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined"); - - throw new RuntimeException(); - } - - interpolant = maybeInterpolant.get(); } - public void vertexFooter(StringBuilder template, ShaderCompiler file) { - Template.prefixFields(template, interpolant, "out", "v2f_"); + @Override + public String generateFooter(FileIndex file, VertexType vertexType) { + return """ + out vec4 v2f_color; + out vec2 v2f_texCoords; + out vec2 v2f_light; + out float v2f_diffuse; - template.append(""" - void main() { - Vertex v = FLWCreateVertex(); - vertex(v); - gl_Position = FLWVertex(v); - v.normal = normalize(v.normal); - - v2f_color = v.color; - v2f_texCoords = v.texCoords; - v2f_light = v.light; - v2f_diffuse = diffuse(v.normal); - #if defined(DEBUG_NORMAL) - v2f_color = vec4(v.normal, 1.); - #endif - } - """); - } - - public void fragmentFooter(StringBuilder template, FileIndex file) { - Template.prefixFields(template, interpolant, "in", "v2f_"); - - template.append(String.format(""" void main() { - Fragment o; - o.color = v2f_color; - o.texCoords = v2f_texCoords; - o.light = v2f_light; - o.diffuse = v2f_diffuse; + Vertex v = FLWCreateVertex(); + vertex(v); + gl_Position = FLWVertex(v); + v.normal = normalize(v.normal); - vec4 color = %s; - FLWFinalizeColor(color); + v2f_color = v.color; + v2f_texCoords = v.texCoords; + v2f_light = v.light; + v2f_diffuse = diffuse(v.normal); + #if defined(DEBUG_NORMAL) + v2f_color = vec4(v.normal, 1.); + #endif } - """, - fragmentMain.call("o") - )); - } + """; + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java index fe0f84423..d08745fcd 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java @@ -2,10 +2,12 @@ package com.jozufozu.flywheel.core.compile; import java.util.HashMap; import java.util.Map; +import java.util.function.Function; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.source.FileResolution; +import com.jozufozu.flywheel.core.Templates; /** * A caching compiler. @@ -18,15 +20,27 @@ import com.jozufozu.flywheel.backend.source.FileResolution; public class ProgramCompiler

{ protected final Map cache = new HashMap<>(); + private final GlProgram.Factory

factory; + private final Function vertexCompiler; + private final Function fragmentCompiler; - private final Template template; - private final FileResolution header; - - public ProgramCompiler(GlProgram.Factory

factory, Template template, FileResolution header) { + public ProgramCompiler(GlProgram.Factory

factory, Function vertexCompiler, Function fragmentCompiler) { this.factory = factory; - this.template = template; - this.header = header; + this.vertexCompiler = vertexCompiler; + this.fragmentCompiler = fragmentCompiler; + } + + /** + * Creates a program compiler using this template. + * @param template The vertex template to use. + * @param factory A factory to add meaning to compiled programs. + * @param header The header file to use for the program. + * @param

The type of program to compile. + * @return A program compiler. + */ + public static ProgramCompiler

create(Template template, GlProgram.Factory

factory, FileResolution header) { + return new ProgramCompiler<>(factory, ctx -> ShaderCompiler.compileVertex(ctx, template, header), ctx -> ShaderCompiler.compileFragment(ctx, Templates.FRAGMENT, header)); } /** @@ -45,11 +59,10 @@ public class ProgramCompiler

{ } private P compile(ProgramContext ctx) { - ShaderCompiler compiler = new ShaderCompiler(ctx, template, header); - return new ProgramAssembler(compiler.name) - .attachShader(compiler.compile(ShaderType.VERTEX)) - .attachShader(compiler.compile(ShaderType.FRAGMENT)) + return new ProgramAssembler(ctx.spec().name) + .attachShader(vertexCompiler.apply(ctx)) + .attachShader(fragmentCompiler.apply(ctx)) .link() .deleteLinkedShaders() .build(this.factory); diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramContext.java b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramContext.java index 6b0450ed0..fdb87be5c 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramContext.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramContext.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.core.compile; +import java.util.List; import java.util.Objects; import javax.annotation.Nullable; @@ -7,7 +8,6 @@ import javax.annotation.Nullable; import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.RenderLayer; -import com.jozufozu.flywheel.backend.source.SourceFile; import com.jozufozu.flywheel.core.shader.ProgramSpec; import net.minecraft.resources.ResourceLocation; @@ -51,8 +51,8 @@ public record ProgramContext(float alphaDiscard, VertexType vertexType, ProgramS return layer == RenderLayer.CUTOUT ? 0.1f : 0f; } - public SourceFile getFile() { - return spec().getSource(); + public List createDefines() { + return spec().getDefines(ctx()); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ShaderCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/ShaderCompiler.java index fb5a3ffd5..a5f3428ea 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/ShaderCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/ShaderCompiler.java @@ -1,183 +1,88 @@ package com.jozufozu.flywheel.core.compile; -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.Nullable; - -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +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.source.FileResolution; -import com.jozufozu.flywheel.backend.source.SourceFile; -import com.jozufozu.flywheel.backend.source.error.ErrorBuilder; -import com.jozufozu.flywheel.backend.source.error.ErrorReporter; -import com.jozufozu.flywheel.backend.source.span.Span; -import com.jozufozu.flywheel.core.shader.WorldProgram; - -import net.minecraft.resources.ResourceLocation; +import com.jozufozu.flywheel.core.shader.ProgramSpec; /** * Compiles a shader program. */ -public class ShaderCompiler implements FileIndex { - - /** - * The name of the file responsible for this compilation. - */ - public final ResourceLocation name; - - /** - * The template we'll be using to generate the final source. - */ - public final Template template; - - private final FileResolution header; - - /** - * Extra {@code #define}s to be added to the shader. - */ - private final List defines; - - /** - * Alpha threshold below which pixels are discarded. - */ - private final float alphaDiscard; - - /** - * The vertex type to use. - */ - public final VertexType vertexType; - - /** - * The main file to compile. - */ - public final SourceFile mainFile; - - private final List files = new ArrayList<>(); - - public ShaderCompiler(ProgramContext context, Template template, FileResolution header) { - this.name = context.spec().name; - this.template = template; - this.header = header; - this.mainFile = context.getFile(); - this.defines = context.spec() - .getDefines(context.ctx()); - this.vertexType = context.vertexType(); - this.alphaDiscard = context.alphaDiscard(); - } - - public GlShader compile(ShaderType type) { +public class ShaderCompiler { + public static GlShader compileVertex(ProgramContext context, Template template, FileResolution header) { StringBuilder finalSource = new StringBuilder(); - finalSource.append("#version ") - .append(template.getVersion()) - .append('\n') - .append("#extension GL_ARB_explicit_attrib_location : enable\n") - .append("#extension GL_ARB_conservative_depth : enable\n") - .append(type.getDefineStatement()); // special case shader type declaration + finalSource.append(generateHeader(template.getVersion(), ShaderType.VERTEX)); - if (alphaDiscard > 0) { - finalSource.append("#define ALPHA_DISCARD 0.1\n"); - } - - for (String def : defines) { + for (String def : context.createDefines()) { finalSource.append("#define ") .append(def) .append('\n'); } - if (type == ShaderType.VERTEX) { - finalSource.append(""" - struct Vertex { - vec3 pos; - vec4 color; - vec2 texCoords; - vec2 light; - vec3 normal; - }; - """); - finalSource.append(vertexType.writeShaderHeader()); - } + finalSource.append(""" + struct Vertex { + vec3 pos; + vec4 color; + vec2 texCoords; + vec2 light; + vec3 normal; + }; + """); + finalSource.append(context.vertexType() + .getShaderHeader()); - files.clear(); - header.getFile().generateFinalSource(this, finalSource); - mainFile.generateFinalSource(this, finalSource); + FileIndexImpl index = new FileIndexImpl(); - template.getMetadata(mainFile).generateFooter(finalSource, type, this); + header.getFile() + .generateFinalSource(index, finalSource); + ProgramSpec spec = context.spec(); + spec.getVertexFile() + .generateFinalSource(index, finalSource); - return new GlShader(this, type, finalSource.toString()); + T appliedTemplate = template.apply(spec.getVertexFile()); + finalSource.append(appliedTemplate.generateFooter(index, context.vertexType())); + + return new GlShader(spec.name, ShaderType.VERTEX, finalSource.toString()); } - public

P compile(GlProgram.Factory

worldShaderPipeline) { - return new ProgramAssembler(this.name) - .attachShader(compile(ShaderType.VERTEX)) - .attachShader(compile(ShaderType.FRAGMENT)) - .link() - .deleteLinkedShaders() - .build(worldShaderPipeline); - } + public static GlShader compileFragment(ProgramContext context, Template template, FileResolution header) { - /** - * 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. - */ - @Override - public int getFileID(SourceFile sourceFile) { - int i = files.indexOf(sourceFile); - if (i != -1) { - return i; + StringBuilder finalSource = new StringBuilder(); + + finalSource.append(generateHeader(template.getVersion(), ShaderType.FRAGMENT)); + for (String def : context.createDefines()) { + finalSource.append("#define ") + .append(def) + .append('\n'); } - int size = files.size(); - files.add(sourceFile); - return size; - } - - public Span getLineSpan(int fileId, int lineNo) { - SourceFile file = files.get(fileId); - - return file.getLineSpanNoWhitespace(lineNo); - } - - public void printShaderInfoLog(String source, String log, ResourceLocation name) { - List lines = log.lines() - .toList(); - - boolean needsSourceDump = false; - - 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'); - needsSourceDump = true; - } - } - Backend.LOGGER.error("Errors compiling '" + name + "': \n" + errors); - if (needsSourceDump) { - // TODO: generated code gets its own "file" - ErrorReporter.printLines(source); - } - } - - @Nullable - private ErrorBuilder parseCompilerError(String line) { - try { - ErrorBuilder error = ErrorBuilder.fromLogLine(this, line); - if (error != null) { - return error; - } - } catch (Exception ignored) { + if (context.alphaDiscard() > 0) { + finalSource.append("#define ALPHA_DISCARD 0.1\n"); } - return null; + + FileIndexImpl index = new FileIndexImpl(); + + ProgramSpec spec = context.spec(); + header.getFile().generateFinalSource(index, finalSource); + spec.getFragmentFile() + .generateFinalSource(index, finalSource); + + T appliedTemplate = template.apply(spec.getFragmentFile()); + finalSource.append(appliedTemplate.generateFooter()); + + return new GlShader(spec.name, ShaderType.FRAGMENT, finalSource.toString()); + } + + protected static String generateHeader(GLSLVersion version, ShaderType type) { + return "#version " + + version + + '\n' + + "#extension GL_ARB_explicit_attrib_location : enable\n" + + "#extension GL_ARB_conservative_depth : enable\n" + + type.getDefineStatement(); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/Template.java b/src/main/java/com/jozufozu/flywheel/core/compile/Template.java index 995218b2d..d5eb6a725 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/Template.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/Template.java @@ -4,13 +4,8 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Function; -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.source.FileResolution; import com.jozufozu.flywheel.backend.source.SourceFile; -import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.source.parse.StructField; /** * A class that generates glsl glue code given a SourceFile. @@ -20,66 +15,33 @@ import com.jozufozu.flywheel.backend.source.parse.StructField; * metadata to generate shader code that OpenGL can use to call into our shader programs. *

*/ -public class Template { +public class Template { - private final Map metadata = new HashMap<>(); + private final Map metadata = new HashMap<>(); - private final Function reader; + private final Function reader; private final GLSLVersion glslVersion; - public Template(GLSLVersion glslVersion, Function reader) { + public Template(GLSLVersion glslVersion, Function reader) { this.reader = reader; this.glslVersion = glslVersion; } - public TemplateData getMetadata(SourceFile file) { + /** + * Verify that the given SourceFile is valid for this Template and return the metadata. + * @param file The SourceFile to apply this Template to. + * @return The applied template metadata. + */ + public T apply(SourceFile file) { // lazily read files, cache results return metadata.computeIfAbsent(file, reader); } /** - * Creates a program compiler using this template. - * @param factory A factory to add meaning to compiled programs. - * @param header The header file to use for the program. - * @param

The type of program to compile. - * @return A program compiler. + * @return The GLSL version this template requires. */ - public

ProgramCompiler

programCompiler(GlProgram.Factory

factory, FileResolution header) { - return new ProgramCompiler<>(factory, this, header); - } - public GLSLVersion getVersion() { return glslVersion; } - public static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) { - ImmutableList fields = struct.getFields(); - - for (StructField field : fields) { - builder.append(qualifier) - .append(' ') - .append(field.type) - .append(' ') - .append(prefix) - .append(field.name) - .append(";\n"); - } - } - - public static StringBuilder assignFields(ShaderStruct struct, String prefix1, String prefix2) { - ImmutableList fields = struct.getFields(); - - StringBuilder builder = new StringBuilder(); - - for (StructField field : fields) { - builder.append(prefix1) - .append(field.name) - .append(" = ") - .append(prefix2) - .append(field.name) - .append(";\n"); - } - - return builder; - } } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/TemplateData.java b/src/main/java/com/jozufozu/flywheel/core/compile/TemplateData.java deleted file mode 100644 index 60c68d080..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/compile/TemplateData.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.jozufozu.flywheel.core.compile; - -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; - -public interface TemplateData { - void vertexFooter(StringBuilder builder, ShaderCompiler file); - void fragmentFooter(StringBuilder builder, FileIndex file); - - /** - * Generate the necessary glue code here. - * - * @param builder The builder to generate the source into. - * @param type The shader stage glue code is needed for. - * @param file The SourceFile with user written code. - */ - default void generateFooter(StringBuilder builder, ShaderType type, ShaderCompiler file) { - if (type == ShaderType.VERTEX) { - vertexFooter(builder, file); - } else if (type == ShaderType.FRAGMENT) { - fragmentFooter(builder, file); - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/VertexData.java b/src/main/java/com/jozufozu/flywheel/core/compile/VertexData.java new file mode 100644 index 000000000..4865ef0f3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/VertexData.java @@ -0,0 +1,11 @@ +package com.jozufozu.flywheel.core.compile; + +import com.jozufozu.flywheel.api.vertex.VertexType; + +public interface VertexData { + /** + * Generate the necessary glue code here. + * @param file The SourceFile with user written code. + */ + String generateFooter(FileIndex file, VertexType vertexType); +} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/ProgramSpec.java b/src/main/java/com/jozufozu/flywheel/core/shader/ProgramSpec.java index 34111f068..bbaca28f1 100644 --- a/src/main/java/com/jozufozu/flywheel/core/shader/ProgramSpec.java +++ b/src/main/java/com/jozufozu/flywheel/core/shader/ProgramSpec.java @@ -30,34 +30,47 @@ public class ProgramSpec { // TODO: Block model style inheritance? public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - ResourceLocation.CODEC.fieldOf("source") + ResourceLocation.CODEC.fieldOf("vertex") .forGetter(ProgramSpec::getSourceLoc), + ResourceLocation.CODEC.fieldOf("fragment") + .forGetter(ProgramSpec::getFragmentLoc), ProgramState.CODEC.listOf() .optionalFieldOf("states", Collections.emptyList()) .forGetter(ProgramSpec::getStates)) .apply(instance, ProgramSpec::new)); public ResourceLocation name; - public final FileResolution source; + public final FileResolution vertex; + public final FileResolution fragment; public final ImmutableList states; - public ProgramSpec(ResourceLocation source, List states) { - this.source = Resolver.INSTANCE.get(source); + public ProgramSpec(ResourceLocation vertex, ResourceLocation fragment, List states) { + this.vertex = Resolver.INSTANCE.get(vertex); + this.fragment = Resolver.INSTANCE.get(fragment); this.states = ImmutableList.copyOf(states); } public void setName(ResourceLocation name) { this.name = name; - this.source.addSpec(name); + this.vertex.addSpec(name); + this.fragment.addSpec(name); } public ResourceLocation getSourceLoc() { - return source.getFileLoc(); + return vertex.getFileLoc(); } - public SourceFile getSource() { - return source.getFile(); + public ResourceLocation getFragmentLoc() { + return fragment.getFileLoc(); + } + + public SourceFile getVertexFile() { + return vertex.getFile(); + } + + public SourceFile getFragmentFile() { + return fragment.getFile(); } public ImmutableList getStates() { 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 4a4cb1021..e382d0ab7 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java @@ -37,7 +37,7 @@ public class BlockVertex implements VertexType { } @Override - public String writeShaderHeader() { + public String getShaderHeader() { return """ layout (location = 0) in vec3 _flw_v_pos; layout (location = 1) in vec4 _flw_v_color; 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 63610ee87..b10139c88 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java @@ -28,7 +28,7 @@ public class PosTexNormalVertex implements VertexType { } @Override - public String writeShaderHeader() { + public String getShaderHeader() { return """ layout (location = 0) in vec3 _flw_v_pos; layout (location = 1) in vec2 _flw_v_texCoords; diff --git a/src/main/resources/assets/flywheel/flywheel/programs/model.json b/src/main/resources/assets/flywheel/flywheel/programs/model.json index 921a6e093..d6c2b6d73 100644 --- a/src/main/resources/assets/flywheel/flywheel/programs/model.json +++ b/src/main/resources/assets/flywheel/flywheel/programs/model.json @@ -1,5 +1,6 @@ { - "source": "flywheel:model.vert", + "vertex": "flywheel:model.vert", + "fragment": "flywheel:block.frag", "states": [ { "when": "flywheel:normal_debug", diff --git a/src/main/resources/assets/flywheel/flywheel/programs/oriented.json b/src/main/resources/assets/flywheel/flywheel/programs/oriented.json index 9f0709ebf..33ebc3291 100644 --- a/src/main/resources/assets/flywheel/flywheel/programs/oriented.json +++ b/src/main/resources/assets/flywheel/flywheel/programs/oriented.json @@ -1,5 +1,6 @@ { - "source": "flywheel:oriented.vert", + "vertex": "flywheel:oriented.vert", + "fragment": "flywheel:block.frag", "states": [ { "when": "flywheel:normal_debug", diff --git a/src/main/resources/assets/flywheel/flywheel/programs/passthru.json b/src/main/resources/assets/flywheel/flywheel/programs/passthru.json index bdb8aabff..5934f447c 100644 --- a/src/main/resources/assets/flywheel/flywheel/programs/passthru.json +++ b/src/main/resources/assets/flywheel/flywheel/programs/passthru.json @@ -1,5 +1,6 @@ { - "source": "flywheel:passthru.vert", + "vertex": "flywheel:passthru.vert", + "fragment": "flywheel:block.frag", "states": [ { "when": "flywheel:normal_debug", diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/block.frag b/src/main/resources/assets/flywheel/flywheel/shaders/block.frag index 70bbe95f4..366d2cc63 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/block.frag +++ b/src/main/resources/assets/flywheel/flywheel/shaders/block.frag @@ -6,10 +6,8 @@ struct Fragment { vec2 light; }; -#if defined(FRAGMENT_SHADER) vec4 fragment(Fragment r) { vec4 tex = FLWBlockTexture(r.texCoords); return vec4(tex.rgb * FLWLight(r.light).rgb * r.diffuse, tex.a) * r.color; } -#endif diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/model.vert b/src/main/resources/assets/flywheel/flywheel/shaders/model.vert index 075269708..0feeb9a1b 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/model.vert +++ b/src/main/resources/assets/flywheel/flywheel/shaders/model.vert @@ -1,6 +1,3 @@ -#use "flywheel:block.frag" - -#if defined(VERTEX_SHADER) struct Instance { vec2 light; @@ -15,4 +12,3 @@ void vertex(inout Vertex v, Instance i) { v.color = i.color; v.light = i.light; } -#endif diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/oriented.vert b/src/main/resources/assets/flywheel/flywheel/shaders/oriented.vert index b89479a8b..8478e8b4a 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/oriented.vert +++ b/src/main/resources/assets/flywheel/flywheel/shaders/oriented.vert @@ -1,6 +1,3 @@ -#use "flywheel:block.frag" - -#if defined(VERTEX_SHADER) #use "flywheel:core/quaternion.glsl" struct Oriented { @@ -17,4 +14,3 @@ void vertex(inout Vertex v, Oriented o) { v.color = o.color; v.light = o.light; } -#endif diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/passthru.vert b/src/main/resources/assets/flywheel/flywheel/shaders/passthru.vert index a598d8ebd..a7642d2ab 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/passthru.vert +++ b/src/main/resources/assets/flywheel/flywheel/shaders/passthru.vert @@ -1,7 +1,4 @@ -#use "flywheel:block.frag" -#if defined(VERTEX_SHADER) void vertex(inout Vertex v) { } -#endif