From 82ea5b1720ea132c16d80accd603c29c2bdfc783 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 10 Aug 2021 02:06:22 -0700 Subject: [PATCH] A heck of a lot - Reorganize everything - Isolate SourceFile related things - Should consider decoupling ShaderLoader from resource loading - Document a lot of newer things - Index functions - Awkward WorldContext builder - Template responsible for providing shader inputs - Template is now an abstract class - Template provides GLSL version - ProgramSpecs now only accept one file --- .../jozufozu/flywheel/backend/Backend.java | 22 +++-- .../flywheel/backend/FileResolution.java | 56 ------------ .../com/jozufozu/flywheel/backend/Index.java | 34 -------- .../flywheel/backend/ShaderContext.java | 33 ------- .../backend/{pipeline => gl}/GLSLVersion.java | 2 +- .../backend/instancing/InstanceManager.java | 16 ++-- .../instancing/InstancedRenderDispatcher.java | 20 +++-- .../backend/pipeline/IShaderPipeline.java | 4 + .../flywheel/backend/pipeline/ITemplate.java | 41 --------- .../flywheel/backend/pipeline/Includer.java | 40 --------- ...ta.java => InstancingProgramMetaData.java} | 24 +++--- .../backend/pipeline/InstancingTemplate.java | 46 +++++----- ...tData.java => OneShotProgramMetaData.java} | 36 ++++---- .../backend/pipeline/OneShotTemplate.java | 38 ++++---- .../{loading => pipeline}/ProtoProgram.java | 54 ++++++------ .../backend/pipeline/ShaderInput.java | 38 ++++++++ .../flywheel/backend/pipeline/Template.java | 82 ++++++++++++++++++ .../{loading => pipeline}/TypeHelper.java | 2 +- .../{ShaderBuilder.java => WorldShader.java} | 52 +++++------ .../backend/pipeline/WorldShaderPipeline.java | 51 ++++------- .../backend/pipeline/error/ErrorReporter.java | 50 ----------- .../backend/source/FileResolution.java | 86 +++++++++++++++++++ .../flywheel/backend/source/Index.java | 42 +++++++++ .../ShaderLoadingException.java | 2 +- .../backend/{ => source}/ShaderSources.java | 27 +++--- .../{pipeline => source}/SourceFile.java | 29 +++---- .../error/ErrorBuilder.java | 22 ++--- .../backend/source/error/ErrorReporter.java | 73 ++++++++++++++++ .../backend/source/error/package-info.java | 6 ++ .../{loading => source}/package-info.java | 2 +- .../parse/AbstractShaderElement.java | 4 +- .../{pipeline => source}/parse/Import.java | 12 +-- .../parse/ShaderFunction.java | 4 +- .../parse/ShaderStruct.java | 14 +-- .../parse/StructField.java | 5 +- .../{pipeline => source}/parse/Variable.java | 5 +- .../parse/package-info.java | 2 +- .../{pipeline => source}/span/CharPos.java | 2 +- .../{pipeline => source}/span/ErrorSpan.java | 4 +- .../{pipeline => source}/span/Span.java | 4 +- .../{pipeline => source}/span/StringSpan.java | 4 +- .../backend/source/span/package-info.java | 6 ++ .../com/jozufozu/flywheel/core/Contexts.java | 6 +- .../jozufozu/flywheel/core/WorldContext.java | 72 ++++++++++++---- .../core/shader/spec/ProgramSpec.java | 44 +++++----- .../flywheel/mixin/RenderHooksMixin.java | 3 + .../flywheel/flywheel/programs/model.json | 3 +- .../flywheel/flywheel/programs/oriented.json | 3 +- 48 files changed, 668 insertions(+), 559 deletions(-) delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/FileResolution.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/Index.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/ShaderContext.java rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => gl}/GLSLVersion.java (82%) delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/ITemplate.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/Includer.java rename src/main/java/com/jozufozu/flywheel/backend/pipeline/{InstanceTemplateData.java => InstancingProgramMetaData.java} (73%) rename src/main/java/com/jozufozu/flywheel/backend/pipeline/{OneShotData.java => OneShotProgramMetaData.java} (59%) rename src/main/java/com/jozufozu/flywheel/backend/{loading => pipeline}/ProtoProgram.java (56%) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderInput.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java rename src/main/java/com/jozufozu/flywheel/backend/{loading => pipeline}/TypeHelper.java (94%) rename src/main/java/com/jozufozu/flywheel/backend/pipeline/{ShaderBuilder.java => WorldShader.java} (52%) delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/error/ErrorReporter.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/source/Index.java rename src/main/java/com/jozufozu/flywheel/backend/{loading => source}/ShaderLoadingException.java (91%) rename src/main/java/com/jozufozu/flywheel/backend/{ => source}/ShaderSources.java (90%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/SourceFile.java (92%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/error/ErrorBuilder.java (80%) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorReporter.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/source/error/package-info.java rename src/main/java/com/jozufozu/flywheel/backend/{loading => source}/package-info.java (77%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/parse/AbstractShaderElement.java (56%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/parse/Import.java (77%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/parse/ShaderFunction.java (94%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/parse/ShaderStruct.java (77%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/parse/StructField.java (73%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/parse/Variable.java (68%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/parse/package-info.java (74%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/span/CharPos.java (87%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/span/ErrorSpan.java (84%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/span/Span.java (95%) rename src/main/java/com/jozufozu/flywheel/backend/{pipeline => source}/span/StringSpan.java (82%) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/source/span/package-info.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index 51853e0ba..880aa5e12 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -16,6 +16,7 @@ import org.lwjgl.opengl.GLCapabilities; import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; import com.jozufozu.flywheel.backend.instancing.InstanceData; import com.jozufozu.flywheel.backend.material.MaterialSpec; +import com.jozufozu.flywheel.backend.source.ShaderSources; import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; @@ -59,14 +60,6 @@ public class Backend { OptifineHandler.init(); } - void clearContexts() { - SpecMetaRegistry.clear(); - programSpecRegistry.clear(); - contexts.forEach(IShaderContext::delete); - contexts.clear(); - materialRegistry.clear(); - } - /** * Get a string describing the Flywheel backend. When there are eventually multiple backends * (Meshlet, MDI, GL31 Draw Instanced are planned), this will name which one is in use. @@ -98,7 +91,7 @@ public class Backend { /** * Register a shader context. */ - public > C register(C spec) { + public > C register(C spec) { contexts.add(spec); return spec; } @@ -201,6 +194,17 @@ public class Backend { RenderWork.enqueue(Minecraft.getInstance().levelRenderer::allChanged); } + /** + * INTERNAL USE ONLY + */ + public void _clearContexts() { + SpecMetaRegistry.clear(); + programSpecRegistry.clear(); + contexts.forEach(IShaderContext::delete); + contexts.clear(); + materialRegistry.clear(); + } + public static void init() { } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/FileResolution.java b/src/main/java/com/jozufozu/flywheel/backend/FileResolution.java deleted file mode 100644 index 8561d2136..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/FileResolution.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.jozufozu.flywheel.backend; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.Nullable; - -import com.jozufozu.flywheel.backend.pipeline.SourceFile; -import com.jozufozu.flywheel.backend.pipeline.error.ErrorBuilder; -import com.jozufozu.flywheel.backend.pipeline.span.Span; - -import net.minecraft.util.ResourceLocation; - -public class FileResolution { - - private final List foundSpans = new ArrayList<>(); - private final ResourceLocation fileLoc; - private SourceFile file; - - - public FileResolution(ResourceLocation fileLoc) { - this.fileLoc = fileLoc; - } - - public ResourceLocation getFileLoc() { - return fileLoc; - } - - @Nullable - public SourceFile getFile() { - return file; - } - - public void addSpan(Span span) { - foundSpans.add(span); - } - - public void resolve(ShaderSources sources) { - - try { - file = sources.source(fileLoc); - } catch (RuntimeException error) { - ErrorBuilder builder = new ErrorBuilder(); - builder.error(String.format("could not find source for file %s", fileLoc)); - for (Span span : foundSpans) { - builder.in(span.getSourceFile()) - .pointAt(span, 2); - } - Backend.log.error(builder.build()); - } - } - - void invalidate() { - - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/Index.java b/src/main/java/com/jozufozu/flywheel/backend/Index.java deleted file mode 100644 index 640f6a13c..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/Index.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.jozufozu.flywheel.backend; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.google.common.collect.Multimap; -import com.google.common.collect.MultimapBuilder; -import com.jozufozu.flywheel.backend.pipeline.SourceFile; -import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct; - -import net.minecraft.util.ResourceLocation; - -/** - * Indexes many shader source definitions to allow for error fix suggestions. - */ -public class Index { - - private final Multimap knownNames = MultimapBuilder.hashKeys().hashSetValues().build(); - - public Index(Map sources) { - Collection files = sources.values(); - - for (SourceFile file : files) { - file.getStructs().forEach(knownNames::put); - - } - } - - public Collection getStructDefinitionsMatching(CharSequence name) { - return knownNames.get(name.toString()); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/ShaderContext.java b/src/main/java/com/jozufozu/flywheel/backend/ShaderContext.java deleted file mode 100644 index 6841e8d22..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/ShaderContext.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.jozufozu.flywheel.backend; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Supplier; - -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.core.shader.IMultiProgram; - -import net.minecraft.util.ResourceLocation; - -public abstract class ShaderContext

implements IShaderContext

{ - - protected final Map> programs = new HashMap<>(); - - public final Backend backend; - - public ShaderContext(Backend backend) { - this.backend = backend; - } - - @Override - public Supplier

getProgramSupplier(ResourceLocation spec) { - return programs.get(spec); - } - - @Override - public void delete() { - programs.values() - .forEach(IMultiProgram::delete); - programs.clear(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/GLSLVersion.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java similarity index 82% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/GLSLVersion.java rename to src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java index a2dcfab4c..438504c0d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/GLSLVersion.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.pipeline; +package com.jozufozu.flywheel.backend.gl; public enum GLSLVersion { V110(110), diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 9c88d2ff1..be81b970a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -152,6 +152,14 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL } } + public void queueUpdate(T obj) { + if (!Backend.getInstance() + .canUseInstancing()) return; + synchronized (queuedUpdates) { + queuedUpdates.add(obj); + } + } + /** * Update the instance associated with an object. * @@ -185,14 +193,6 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL } } - public void queueUpdate(T obj) { - if (!Backend.getInstance() - .canUseInstancing()) return; - synchronized (queuedUpdates) { - queuedUpdates.add(obj); - } - } - public void onLightUpdate(T obj) { if (!Backend.getInstance() .canUseInstancing()) return; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java index 1db796450..1186d7ea3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java @@ -27,6 +27,18 @@ public class InstancedRenderDispatcher { private static final WorldAttached instanceWorlds = new WorldAttached<>($ -> new InstanceWorld()); + /** + * Call this when you want to invoke + * @param te + */ + public static void enqueueUpdate(TileEntity te) { + getTiles(te.getLevel()).queueUpdate(te); + } + + public static void enqueueUpdate(Entity entity) { + getEntities(entity.level).queueUpdate(entity); + } + @Nonnull public static InstanceManager getTiles(IWorld world) { return instanceWorlds.get(world) @@ -52,14 +64,6 @@ public class InstancedRenderDispatcher { instanceWorlds.get(world).tick(); } - public static void enqueueUpdate(TileEntity te) { - getTiles(te.getLevel()).queueUpdate(te); - } - - public static void enqueueUpdate(Entity entity) { - getEntities(entity.level).queueUpdate(entity); - } - @SubscribeEvent public static void onBeginFrame(BeginFrameEvent event) { instanceWorlds.get(event.getWorld()).beginFrame(event); diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/IShaderPipeline.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/IShaderPipeline.java index 36eff8e28..370281fcd 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/IShaderPipeline.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/IShaderPipeline.java @@ -4,6 +4,10 @@ import com.jozufozu.flywheel.core.shader.IMultiProgram; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; +/** + * The main interface for compiling usable shaders from program specs. + * @param

the type of the program that this pipeline compiles. + */ public interface IShaderPipeline

{ IMultiProgram

compile(ProgramSpec spec); diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ITemplate.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/ITemplate.java deleted file mode 100644 index 9547eac6c..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ITemplate.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.backend.loading.ProtoProgram; -import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.pipeline.parse.StructField; - -public interface ITemplate { - void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file); - - void attachAttributes(ProtoProgram program, SourceFile file); - - - 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"); - } - } - - static void assignFields(StringBuilder builder, ShaderStruct struct, String prefix1, String prefix2) { - ImmutableList fields = struct.getFields(); - - for (StructField field : fields) { - builder.append(prefix1) - .append(field.name) - .append(" = ") - .append(prefix2) - .append(field.name) - .append(";\n"); - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Includer.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Includer.java deleted file mode 100644 index d76bb2327..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Includer.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.pipeline.parse.Import; - -import net.minecraft.util.ResourceLocation; - -public class Includer { - - public static List recurseIncludes(SourceFile from) { - Set seen = new HashSet<>(); - - seen.add(from.name); - - List out = new ArrayList<>(); - - process(seen, out, from); - - return out; - } - - private static void process(Set seen, List out, SourceFile source) { - ImmutableList imports = source.getIncludes(); - - for (Import use : imports) { - - SourceFile file = use.getFile(); - if (file != null && seen.add(file.name)) { - process(seen, out, file); - - out.add(file); - } - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstanceTemplateData.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingProgramMetaData.java similarity index 73% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/InstanceTemplateData.java rename to src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingProgramMetaData.java index 8b503ca01..612e8989f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstanceTemplateData.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingProgramMetaData.java @@ -3,13 +3,14 @@ package com.jozufozu.flywheel.backend.pipeline; import java.util.Optional; import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter; -import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction; -import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.pipeline.parse.Variable; -import com.jozufozu.flywheel.backend.pipeline.span.Span; +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 InstanceTemplateData { +public class InstancingProgramMetaData { public final SourceFile file; public final ShaderFunction vertexMain; @@ -21,15 +22,14 @@ public class InstanceTemplateData { public final ShaderStruct vertex; public final ShaderStruct instance; - public InstanceTemplateData(SourceFile file) { + public InstancingProgramMetaData(SourceFile file) { this.file = file; Optional vertexFunc = file.findFunction("vertex"); Optional fragmentFunc = file.findFunction("fragment"); - if (!fragmentFunc.isPresent()) { - ErrorReporter.generateFileError(file, "could not find \"fragment\" function"); + ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined"); } if (!vertexFunc.isPresent()) { ErrorReporter.generateFileError(file, "could not find \"vertex\" function"); @@ -64,15 +64,15 @@ public class InstanceTemplateData { Optional maybeInstance = file.findStruct(instanceName); if (!maybeVertex.isPresent()) { - ErrorReporter.generateMissingStruct(file, vertexName); + ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined"); } if (!maybeInterpolant.isPresent()) { - ErrorReporter.generateMissingStruct(file, interpolantName); + ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined"); } if (!maybeInstance.isPresent()) { - ErrorReporter.generateMissingStruct(file, instanceName); + ErrorReporter.generateMissingStruct(file, instanceName, "struct not defined"); } if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent() || !maybeInstance.isPresent()) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingTemplate.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingTemplate.java index 0a01be533..33fc589f3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingTemplate.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingTemplate.java @@ -1,16 +1,19 @@ package com.jozufozu.flywheel.backend.pipeline; -import java.util.HashMap; -import java.util.Map; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.backend.loading.ProtoProgram; +import com.jozufozu.flywheel.backend.source.SourceFile; -public class InstancingTemplate implements ITemplate { +public class InstancingTemplate extends Template { public static final InstancingTemplate INSTANCE = new InstancingTemplate(); - private final Map datas = new HashMap<>(); + public InstancingTemplate() { + super(InstancingProgramMetaData::new); + } @Override public void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file) { @@ -22,51 +25,50 @@ public class InstancingTemplate implements ITemplate { } @Override - public void attachAttributes(ProtoProgram program, SourceFile file) { - InstanceTemplateData data = getData(file); - data.vertex.addPrefixedAttributes(program, "a_v_"); - data.instance.addPrefixedAttributes(program, "a_i_"); - } + public Collection getShaderInputs(SourceFile file) { + InstancingProgramMetaData data = getMetadata(file); - public InstanceTemplateData getData(SourceFile file) { - return datas.computeIfAbsent(file, InstanceTemplateData::new); + List inputs = new ArrayList<>(ShaderInput.fromStruct(data.vertex, "a_v_")); + inputs.addAll(ShaderInput.fromStruct(data.instance, "a_i_")); + + return inputs; } public void vertexFooter(StringBuilder template, SourceFile file) { - InstanceTemplateData data = getData(file); + InstancingProgramMetaData data = getMetadata(file); - ITemplate.prefixFields(template, data.vertex, "attribute", "a_v_"); - ITemplate.prefixFields(template, data.instance, "attribute", "a_i_"); - ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_"); + Template.prefixFields(template, data.vertex, "attribute", "a_v_"); + Template.prefixFields(template, data.instance, "attribute", "a_i_"); + Template.prefixFields(template, data.interpolant, "varying", "v2f_"); template.append("void main() {\n"); template.append(data.vertexName) .append(" v;\n"); - ITemplate.assignFields(template, data.vertex, "v.", "a_v_"); + Template.assignFields(template, data.vertex, "v.", "a_v_"); template.append(data.instanceName) .append(" i;\n"); - ITemplate.assignFields(template, data.instance, "i.", "a_i_"); + Template.assignFields(template, data.instance, "i.", "a_i_"); template.append(data.interpolantName) .append(" o = ") .append(data.vertexMain.call("v", "i")) .append(";\n"); - ITemplate.assignFields(template, data.interpolant, "v2f_", "o."); + Template.assignFields(template, data.interpolant, "v2f_", "o."); template.append('}'); } public void fragmentFooter(StringBuilder template, SourceFile file) { - InstanceTemplateData data = getData(file); + InstancingProgramMetaData data = getMetadata(file); - ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_"); + Template.prefixFields(template, data.interpolant, "varying", "v2f_"); template.append("void main() {\n"); template.append(data.interpolantName) .append(" o;\n"); - ITemplate.assignFields(template, data.interpolant, "o.", "v2f_"); + Template.assignFields(template, data.interpolant, "o.", "v2f_"); template.append(data.fragmentMain.call("o")) .append(";\n"); diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotData.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotProgramMetaData.java similarity index 59% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotData.java rename to src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotProgramMetaData.java index 99ced1cd8..d353be2b0 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotData.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotProgramMetaData.java @@ -3,13 +3,14 @@ package com.jozufozu.flywheel.backend.pipeline; import java.util.Optional; import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter; -import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction; -import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.pipeline.parse.Variable; -import com.jozufozu.flywheel.backend.pipeline.span.Span; +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 OneShotData { +public class OneShotProgramMetaData { public final SourceFile file; public final ShaderFunction vertexMain; @@ -19,7 +20,7 @@ public class OneShotData { public final ShaderStruct vertex; public final ShaderFunction fragmentMain; - public OneShotData(SourceFile file) { + public OneShotProgramMetaData(SourceFile file) { this.file = file; Optional maybeVertexMain = file.findFunction("vertex"); @@ -27,29 +28,32 @@ public class OneShotData { if (!maybeVertexMain.isPresent()) { ErrorReporter.generateFileError(file, "could not find \"vertex\" function"); - throw new RuntimeException(); } if (!maybeFragmentMain.isPresent()) { - ErrorReporter.generateFileError(file, "could not find \"fragment\" function"); + ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined"); + } + + if (!maybeVertexMain.isPresent() || !maybeFragmentMain.isPresent()) { throw new RuntimeException(); } vertexMain = maybeVertexMain.get(); fragmentMain = maybeFragmentMain.get(); - ImmutableList parameters = fragmentMain.getParameters(); + 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"); - throw new RuntimeException(); } - if (parameters.size() != 1) { - ErrorReporter.generateSpanError(fragmentMain.getArgs(), "instancing requires fragment function to have 1 argument"); - throw new RuntimeException(); + 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(); + } interpolantName = vertexMain.getType(); vertexName = vertexParameters.get(0) @@ -59,10 +63,10 @@ public class OneShotData { Optional maybeVertex = file.findStruct(vertexName); if (!maybeVertex.isPresent()) - ErrorReporter.generateMissingStruct(file, vertexName); + ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined"); if (!maybeInterpolant.isPresent()) - ErrorReporter.generateMissingStruct(file, interpolantName); + ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined"); if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent()) { throw new RuntimeException(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotTemplate.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotTemplate.java index 805ac34d2..82f001cb3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotTemplate.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotTemplate.java @@ -1,18 +1,17 @@ package com.jozufozu.flywheel.backend.pipeline; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import java.util.Collection; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.backend.loading.ProtoProgram; +import com.jozufozu.flywheel.backend.source.SourceFile; -public class OneShotTemplate implements ITemplate { +public class OneShotTemplate extends Template { public static final OneShotTemplate INSTANCE = new OneShotTemplate(); - - private final Map datas = new HashMap<>(); + public OneShotTemplate() { + super(OneShotProgramMetaData::new); + } @Override public void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file) { @@ -24,45 +23,42 @@ public class OneShotTemplate implements ITemplate { } @Override - public void attachAttributes(ProtoProgram program, SourceFile file) { - OneShotData data = getData(file); - data.vertex.addPrefixedAttributes(program, "a_v_"); - } + public Collection getShaderInputs(SourceFile file) { + OneShotProgramMetaData data = getMetadata(file); - public OneShotData getData(SourceFile file) { - return datas.computeIfAbsent(file, OneShotData::new); + return ShaderInput.fromStruct(data.vertex, "a_v_"); } public void vertexFooter(StringBuilder template, SourceFile file) { - OneShotData data = getData(file); + OneShotProgramMetaData data = getMetadata(file); - ITemplate.prefixFields(template, data.vertex, "attribute", "a_v_"); - ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_"); + Template.prefixFields(template, data.vertex, "attribute", "a_v_"); + Template.prefixFields(template, data.interpolant, "varying", "v2f_"); template.append("void main() {\n"); template.append(data.vertexName) .append(" v;\n"); - ITemplate.assignFields(template, data.vertex, "v.", "a_v_"); + Template.assignFields(template, data.vertex, "v.", "a_v_"); template.append(data.interpolantName) .append(" o = ") .append(data.vertexMain.call("v")) .append(";\n"); - ITemplate.assignFields(template, data.interpolant, "v2f_", "o."); + Template.assignFields(template, data.interpolant, "v2f_", "o."); template.append('}'); } public void fragmentFooter(StringBuilder template, SourceFile file) { - OneShotData data = getData(file); + OneShotProgramMetaData data = getMetadata(file); - ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_"); + Template.prefixFields(template, data.interpolant, "varying", "v2f_"); template.append("void main() {\n"); template.append(data.interpolant.name) .append(" o;\n"); - ITemplate.assignFields(template, data.interpolant, "o.", "v2f_"); + Template.assignFields(template, data.interpolant, "o.", "v2f_"); template.append(data.fragmentMain.call("o")) .append(";\n"); diff --git a/src/main/java/com/jozufozu/flywheel/backend/loading/ProtoProgram.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/ProtoProgram.java similarity index 56% rename from src/main/java/com/jozufozu/flywheel/backend/loading/ProtoProgram.java rename to src/main/java/com/jozufozu/flywheel/backend/pipeline/ProtoProgram.java index 374a5a3d7..17e7c321a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/loading/ProtoProgram.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/ProtoProgram.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.loading; +package com.jozufozu.flywheel.backend.pipeline; import static org.lwjgl.opengl.GL20.GL_LINK_STATUS; import static org.lwjgl.opengl.GL20.GL_TRUE; @@ -9,53 +9,48 @@ import static org.lwjgl.opengl.GL20.glGetProgramInfoLog; import static org.lwjgl.opengl.GL20.glGetProgrami; import static org.lwjgl.opengl.GL20.glLinkProgram; -import java.util.EnumMap; -import java.util.Map; -import java.util.function.IntConsumer; - -import org.lwjgl.opengl.GL20; +import java.util.List; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; -import net.minecraft.util.ResourceLocation; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; public class ProtoProgram { public final int program; + public final WorldShader parent; - public ResourceLocation name; private int attributeIndex; - private final IntList shaders; + private final List shaders; - public ProtoProgram() { + public ProtoProgram(WorldShader parent) { + this.parent = parent; this.program = glCreateProgram(); - shaders = new IntArrayList(2); + shaders = new ObjectArrayList<>(); } - public void attachShader(GlShader glShader) { - glAttachShader(this.program, glShader.handle()); - } - - public void addAttribute(String name, int attributeCount) { - glBindAttribLocation(this.program, attributeIndex, name); - attributeIndex += attributeCount; + public ProtoProgram compilePart(ShaderType type) { + GlShader shader = parent.compile(type); + attachShader(shader); + return this; } /** * Links the attached shaders to this program. */ - public ProtoProgram link(ResourceLocation name) { - this.name = name; + public ProtoProgram link() { + + parent.template.getShaderInputs(parent.mainFile) + .forEach(this::addAttribute); + glLinkProgram(this.program); String log = glGetProgramInfoLog(this.program); if (!log.isEmpty()) { - Backend.log.debug("Program link log for " + name + ": " + log); + Backend.log.debug("Program link log for " + parent.name + ": " + log); } int result = glGetProgrami(this.program, GL_LINK_STATUS); @@ -68,7 +63,18 @@ public class ProtoProgram { } public ProtoProgram deleteLinkedShaders() { - shaders.forEach((IntConsumer) GL20::glDeleteShader); + shaders.forEach(GlShader::delete); return this; } + + private void attachShader(GlShader glShader) { + shaders.add(glShader); + glAttachShader(this.program, glShader.handle()); + } + + private void addAttribute(ShaderInput shaderInput) { + glBindAttribLocation(this.program, attributeIndex, shaderInput.name); + attributeIndex += shaderInput.attribCount; + } + } diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderInput.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderInput.java new file mode 100644 index 000000000..bf9b3b95c --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderInput.java @@ -0,0 +1,38 @@ +package com.jozufozu.flywheel.backend.pipeline; + +import java.util.Collection; +import java.util.stream.Collectors; + +import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; +import com.jozufozu.flywheel.backend.source.parse.StructField; + +/** + * A single input to a shader. + */ +public class ShaderInput { + public final CharSequence name; + public final int attribCount; + + public ShaderInput(CharSequence name, int attribCount) { + this.name = name; + this.attribCount = attribCount; + } + + public ShaderInput withPrefix(CharSequence prefix) { + return new ShaderInput(prefix.toString() + name, attribCount); + } + + public static Collection fromStruct(ShaderStruct struct, String prefix) { + return struct.getFields() + .stream() + .map(ShaderInput::from) + .map(a -> a.withPrefix(prefix)) + .collect(Collectors.toList()); + } + + public static ShaderInput from(StructField field) { + int attributeCount = TypeHelper.getAttributeCount(field.type); + + return new ShaderInput(field.name, attributeCount); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java new file mode 100644 index 000000000..fd65afa86 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java @@ -0,0 +1,82 @@ +package com.jozufozu.flywheel.backend.pipeline; + +import java.util.Collection; +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.ShaderType; +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. + * + *

+ * Shader files are written somewhat abstractly. Subclasses of Template handle those abstractions, using SourceFile + * metadata to generate shader code that OpenGL can use to call into our shader programs. + *

+ * @param Holds metadata, generates errors. + */ +public abstract class Template { + + private final Map metadata = new HashMap<>(); + + private final Function parser; + + protected Template(Function parser) { + this.parser = parser; + } + + /** + * Generate the necessary glue code here. + * + *

+ * See {@link InstancingTemplate} and {@link OneShotTemplate} for examples. + *

+ * @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. + */ + public abstract void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file); + + public abstract Collection getShaderInputs(SourceFile file); + + public D getMetadata(SourceFile file) { + return metadata.computeIfAbsent(file, parser); + } + + public GLSLVersion getVersion() { + return GLSLVersion.V120; + } + + 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 void assignFields(StringBuilder builder, ShaderStruct struct, String prefix1, String prefix2) { + ImmutableList fields = struct.getFields(); + + for (StructField field : fields) { + builder.append(prefix1) + .append(field.name) + .append(" = ") + .append(prefix2) + .append(field.name) + .append(";\n"); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/loading/TypeHelper.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/TypeHelper.java similarity index 94% rename from src/main/java/com/jozufozu/flywheel/backend/loading/TypeHelper.java rename to src/main/java/com/jozufozu/flywheel/backend/pipeline/TypeHelper.java index c9738a98d..283f51a69 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/loading/TypeHelper.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/TypeHelper.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.loading; +package com.jozufozu.flywheel.backend.pipeline; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderBuilder.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShader.java similarity index 52% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderBuilder.java rename to src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShader.java index 3c4c704cd..e9add629c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShader.java @@ -1,37 +1,35 @@ package com.jozufozu.flywheel.backend.pipeline; import java.util.List; +import java.util.Optional; -import com.jozufozu.flywheel.backend.FileResolution; +import com.jozufozu.flywheel.backend.source.FileResolution; import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.backend.source.SourceFile; import net.minecraft.util.ResourceLocation; -public class ShaderBuilder { +public class WorldShader { public final ResourceLocation name; - public final ITemplate template; - public final FileResolution header; + public final Template template; + public final CharSequence header; public SourceFile mainFile; - private GLSLVersion version; - private StringBuilder source; + private CharSequence source; private StringBuilder defines; - public ShaderBuilder(ResourceLocation name, ITemplate template, FileResolution header) { + public WorldShader(ResourceLocation name, Template template, FileResolution header) { this.name = name; this.template = template; - this.header = header; + this.header = Optional.ofNullable(header.getFile()) + .map(SourceFile::generateFinalSource) + .orElse(""); } - public ShaderBuilder setVersion(GLSLVersion version) { - this.version = version; - return this; - } - - public ShaderBuilder setDefines(List defs) { + public WorldShader setDefines(List defs) { defines = new StringBuilder(); for (String def : defs) { @@ -42,35 +40,37 @@ public class ShaderBuilder { return this; } - public ShaderBuilder setMainSource(SourceFile file) { + public WorldShader setMainSource(SourceFile file) { if (mainFile == file) return this; mainFile = file; - source = new StringBuilder(); - - file.generateFinalSource(source); + source = file.generateFinalSource(); return this; } - public GlShader compile(ResourceLocation name, ShaderType type) { + public GlShader compile(ShaderType type) { StringBuilder finalSource = new StringBuilder(); finalSource.append("#version ") - .append(version) + .append(template.getVersion()) .append('\n') .append("#define ") .append(type.define) .append('\n') - .append(defines != null ? defines : ""); - SourceFile file = header.getFile(); - if (file != null) { - file.generateFinalSource(finalSource); - } - mainFile.generateFinalSource(finalSource); + .append(defines != null ? defines : "") + .append(header) + .append('\n') + .append(source) + .append('\n'); + template.generateTemplateSource(finalSource, type, mainFile); return new GlShader(name, type, finalSource); } + + public ProtoProgram createProgram() { + return new ProtoProgram(this); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShaderPipeline.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShaderPipeline.java index 436a42f51..26871b6ef 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShaderPipeline.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShaderPipeline.java @@ -1,22 +1,15 @@ package com.jozufozu.flywheel.backend.pipeline; -import static org.lwjgl.opengl.GL11.GL_TRUE; -import static org.lwjgl.opengl.GL20.GL_LINK_STATUS; import static org.lwjgl.opengl.GL20.glGetProgramInfoLog; -import static org.lwjgl.opengl.GL20.glGetProgrami; import java.util.List; import javax.annotation.Nullable; -import org.lwjgl.opengl.GL20; - -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.FileResolution; -import com.jozufozu.flywheel.backend.ShaderSources; -import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.source.FileResolution; +import com.jozufozu.flywheel.backend.source.ShaderSources; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.backend.loading.ProtoProgram; +import com.jozufozu.flywheel.backend.source.SourceFile; import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram; import com.jozufozu.flywheel.core.shader.GameStateProgram; import com.jozufozu.flywheel.core.shader.IMultiProgram; @@ -32,10 +25,10 @@ public class WorldShaderPipeline

implements IShaderPipel private final ExtensibleGlProgram.Factory

factory; - private final ITemplate template; + private final Template template; private final FileResolution header; - public WorldShaderPipeline(ShaderSources sources, ExtensibleGlProgram.Factory

factory, ITemplate template, FileResolution header) { + public WorldShaderPipeline(ShaderSources sources, ExtensibleGlProgram.Factory

factory, Template template, FileResolution header) { this.sources = sources; this.factory = factory; this.template = template; @@ -44,48 +37,40 @@ public class WorldShaderPipeline

implements IShaderPipel public IMultiProgram

compile(ProgramSpec spec) { - SourceFile file = sources.source(spec.vert); + SourceFile file = sources.source(spec.source); return compile(spec.name, file, spec.getStates()); } public IMultiProgram

compile(ResourceLocation name, SourceFile file, List variants) { - ShaderBuilder shader = new ShaderBuilder(name, template, header) - .setMainSource(file) - .setVersion(GLSLVersion.V110); + WorldShader shader = new WorldShader(name, template, header) + .setMainSource(file); - GameStateProgram.Builder

builder = GameStateProgram.builder(compile(shader, name, null)); + GameStateProgram.Builder

builder = GameStateProgram.builder(compile(shader, null)); for (ProgramState variant : variants) { - builder.withVariant(variant.getContext(), compile(shader, name, variant)); + builder.withVariant(variant.getContext(), compile(shader, variant)); } return builder.build(); } - private P compile(ShaderBuilder shader, ResourceLocation name, @Nullable ProgramState variant) { + private P compile(WorldShader shader, @Nullable ProgramState variant) { if (variant != null) { shader.setDefines(variant.getDefines()); } - GlShader vertex = shader.compile(name, ShaderType.VERTEX); - GlShader fragment = shader.compile(name, ShaderType.FRAGMENT); - - ProtoProgram program = new ProtoProgram(); - - program.attachShader(vertex); - program.attachShader(fragment); - - template.attachAttributes(program, shader.mainFile); - - program.link(name); - program.deleteLinkedShaders(); + ProtoProgram program = shader.createProgram() + .compilePart(ShaderType.VERTEX) + .compilePart(ShaderType.FRAGMENT) + .link() + .deleteLinkedShaders(); if (variant != null) { - return factory.create(name, program.program, variant.getExtensions()); + return factory.create(shader.name, program.program, variant.getExtensions()); } else { - return factory.create(name, program.program, null); + return factory.create(shader.name, program.program, null); } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/error/ErrorReporter.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/error/ErrorReporter.java deleted file mode 100644 index 03095e40f..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/error/ErrorReporter.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline.error; - -import java.util.Optional; - -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.pipeline.SourceFile; -import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.pipeline.span.Span; - -public class ErrorReporter { - - public static void generateSpanError(Span span, String message) { - SourceFile file = span.getSourceFile(); - - ErrorBuilder builder = new ErrorBuilder(); - - CharSequence error = builder.error(message) - .in(file) - .pointAt(span, 2) - .build(); - - Backend.log.error(error); - } - - public static void generateFileError(SourceFile file, String message) { - - ErrorBuilder builder = new ErrorBuilder(); - - CharSequence error = builder.error(message) - .in(file) - .build(); - - Backend.log.error(error); - } - - public static void generateMissingStruct(SourceFile file, Span vertexName) { - Optional span = file.parent.index.getStructDefinitionsMatching(vertexName) - .stream() - .findFirst() - .map(ShaderStruct::getName); - ErrorBuilder builder = new ErrorBuilder(); - - ErrorBuilder error = builder.error("struct not defined") - .in(file) - .pointAt(vertexName, 2) - .hintIncludeFor(span.orElse(null)); - - Backend.log.error(error.build()); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java b/src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java new file mode 100644 index 000000000..a340ff845 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java @@ -0,0 +1,86 @@ +package com.jozufozu.flywheel.backend.source; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nullable; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.source.error.ErrorBuilder; +import com.jozufozu.flywheel.backend.source.span.Span; + +import net.minecraft.util.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 { + + /** + * Spans that have references that resolved to this. + */ + private final List foundSpans = new ArrayList<>(); + private final ShaderSources parent; + private final ResourceLocation fileLoc; + private SourceFile file; + + public FileResolution(ShaderSources parent, ResourceLocation fileLoc) { + this.parent = parent; + this.fileLoc = fileLoc; + } + + public ResourceLocation getFileLoc() { + return fileLoc; + } + + @Nullable + public SourceFile getFile() { + return file; + } + + /** + * 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 void addSpan(Span span) { + foundSpans.add(span); + } + + /** + * Check to see if this file actually resolves to something. + * + *

+ * Called after all files are loaded. If we can't find the file here, it doesn't exist. + *

+ */ + void resolve() { + + try { + file = this.parent.source(fileLoc); + } catch (RuntimeException error) { + ErrorBuilder builder = new ErrorBuilder(); + builder.error(String.format("could not find source for file %s", fileLoc)); + // print the location of all places where this file was referenced + for (Span span : foundSpans) { + builder.in(span.getSourceFile()) + .pointAt(span, 2); + } + Backend.log.error(builder.build()); + } + } + + void invalidate() { + foundSpans.clear(); + file = null; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/Index.java b/src/main/java/com/jozufozu/flywheel/backend/source/Index.java new file mode 100644 index 000000000..75325a7be --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/Index.java @@ -0,0 +1,42 @@ +package com.jozufozu.flywheel.backend.source; + +import java.util.Collection; +import java.util.Map; + +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; +import com.jozufozu.flywheel.backend.source.parse.ShaderFunction; +import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; + +import net.minecraft.util.ResourceLocation; + +/** + * Indexes many shader source definitions to allow for error fix suggestions. + */ +public class Index { + + private final Multimap knownStructs = MultimapBuilder.hashKeys() + .hashSetValues() + .build(); + + private final Multimap knownFunctions = MultimapBuilder.hashKeys() + .hashSetValues() + .build(); + + public Index(Map sources) { + Collection files = sources.values(); + + for (SourceFile file : files) { + file.getStructs().forEach(knownStructs::put); + file.getFunctions().forEach(knownFunctions::put); + } + } + + public Collection getStructDefinitionsMatching(CharSequence name) { + return knownStructs.get(name.toString()); + } + + public Collection getFunctionDefinitionsMatching(CharSequence name) { + return knownFunctions.get(name.toString()); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/loading/ShaderLoadingException.java b/src/main/java/com/jozufozu/flywheel/backend/source/ShaderLoadingException.java similarity index 91% rename from src/main/java/com/jozufozu/flywheel/backend/loading/ShaderLoadingException.java rename to src/main/java/com/jozufozu/flywheel/backend/source/ShaderLoadingException.java index 5c32c7d98..02e013091 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/loading/ShaderLoadingException.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/ShaderLoadingException.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.loading; +package com.jozufozu.flywheel.backend.source; public class ShaderLoadingException extends RuntimeException { public ShaderLoadingException() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/ShaderSources.java b/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java similarity index 90% rename from src/main/java/com/jozufozu/flywheel/backend/ShaderSources.java rename to src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java index 17b974d81..bab564af4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/ShaderSources.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend; +package com.jozufozu.flywheel.backend.source; import java.io.IOException; import java.util.ArrayList; @@ -7,16 +7,14 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Predicate; -import javax.annotation.Nonnull; - import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.IShaderContext; +import com.jozufozu.flywheel.backend.ResourceUtil; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; -import com.jozufozu.flywheel.backend.loading.ShaderLoadingException; -import com.jozufozu.flywheel.backend.pipeline.SourceFile; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; import com.jozufozu.flywheel.event.GatherContextEvent; @@ -36,6 +34,13 @@ import net.minecraftforge.resource.IResourceType; import net.minecraftforge.resource.ISelectiveResourceReloadListener; import net.minecraftforge.resource.VanillaResourceType; +/** + * The main entity for loading shaders. + * + *

+ * This class is responsible for invoking the loading, parsing, and compilation stages. + *

+ */ public class ShaderSources implements ISelectiveResourceReloadListener { public static final String SHADER_DIR = "flywheel/shaders/"; public static final String PROGRAM_DIR = "flywheel/programs/"; @@ -70,7 +75,7 @@ public class ShaderSources implements ISelectiveResourceReloadListener { } public FileResolution resolveFile(ResourceLocation fileLoc) { - return resolutions.computeIfAbsent(fileLoc, FileResolution::new); + return resolutions.computeIfAbsent(fileLoc, l -> new FileResolution(this, l)); } @Deprecated @@ -86,17 +91,17 @@ public class ShaderSources implements ISelectiveResourceReloadListener { if (backend.gl20()) { shouldCrash = false; - backend.clearContexts(); + backend._clearContexts(); + resolutions.values().forEach(FileResolution::invalidate); + shaderSources.clear(); ModLoader.get() .postEvent(new GatherContextEvent(backend)); - resolutions.values().forEach(FileResolution::invalidate); - loadProgramSpecs(manager); loadShaderSources(manager); for (FileResolution resolution : resolutions.values()) { - resolution.resolve(this); + resolution.resolve(); } for (IShaderContext context : backend.allContexts()) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/SourceFile.java b/src/main/java/com/jozufozu/flywheel/backend/source/SourceFile.java similarity index 92% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/SourceFile.java rename to src/main/java/com/jozufozu/flywheel/backend/source/SourceFile.java index 6c02398f4..c4c3d65f0 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/SourceFile.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/SourceFile.java @@ -1,29 +1,22 @@ -package com.jozufozu.flywheel.backend.pipeline; +package com.jozufozu.flywheel.backend.source; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Queue; -import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.Nullable; - import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.jozufozu.flywheel.backend.FileResolution; -import com.jozufozu.flywheel.backend.ShaderSources; -import com.jozufozu.flywheel.backend.pipeline.parse.Import; -import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction; -import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.pipeline.span.CharPos; -import com.jozufozu.flywheel.backend.pipeline.span.ErrorSpan; -import com.jozufozu.flywheel.backend.pipeline.span.Span; -import com.jozufozu.flywheel.backend.pipeline.span.StringSpan; +import com.jozufozu.flywheel.backend.source.parse.Import; +import com.jozufozu.flywheel.backend.source.parse.ShaderFunction; +import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; +import com.jozufozu.flywheel.backend.source.span.CharPos; +import com.jozufozu.flywheel.backend.source.span.ErrorSpan; +import com.jozufozu.flywheel.backend.source.span.Span; +import com.jozufozu.flywheel.backend.source.span.StringSpan; import com.jozufozu.flywheel.util.StringUtil; import it.unimi.dsi.fastutil.ints.IntArrayList; @@ -142,6 +135,12 @@ public class SourceFile { return "#use " + '"' + name + '"'; } + public CharSequence generateFinalSource() { + StringBuilder builder = new StringBuilder(); + generateFinalSource(builder); + return builder; + } + public void generateFinalSource(StringBuilder source) { for (Import include : getIncludes()) { SourceFile file = include.getFile(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/error/ErrorBuilder.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java similarity index 80% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/error/ErrorBuilder.java rename to src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java index 4ba70f099..9c01a9d19 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/error/ErrorBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java @@ -1,11 +1,7 @@ -package com.jozufozu.flywheel.backend.pipeline.error; +package com.jozufozu.flywheel.backend.source.error; -import java.util.Optional; - -import javax.annotation.Nullable; - -import com.jozufozu.flywheel.backend.pipeline.SourceFile; -import com.jozufozu.flywheel.backend.pipeline.span.Span; +import com.jozufozu.flywheel.backend.source.SourceFile; +import com.jozufozu.flywheel.backend.source.span.Span; import com.jozufozu.flywheel.util.FlwUtil; public class ErrorBuilder { @@ -40,6 +36,11 @@ public class ErrorBuilder { return line(String.valueOf(no), content); } + public ErrorBuilder numberedLine(int no, int width, CharSequence content) { + String fmt = "%1$" + width + 's'; + return line(String.format(fmt, no), content); + } + public ErrorBuilder line(CharSequence leftColumn, CharSequence rightColumn) { internal.append(leftColumn) @@ -54,10 +55,10 @@ public class ErrorBuilder { return this; } - public ErrorBuilder hintIncludeFor(Span span) { + public ErrorBuilder hintIncludeFor(Span span, CharSequence msg) { if (span == null) return this; - hint("add " + span.getSourceFile().importStatement()) + hint("add " + span.getSourceFile().importStatement() + " " + msg) .in(span.getSourceFile()) .pointAt(span, 1); @@ -77,14 +78,13 @@ public class ErrorBuilder { int digits = FlwUtil.numDigits(lastLine); - int firstCol = span.getStart().getCol(); int lastCol = span.getEnd().getCol(); for (int i = firstLine; i <= lastLine; i++) { CharSequence line = file.getLine(i); - numberedLine(i + 1, line); + numberedLine(i + 1, digits, line); if (i == spanLine) { line(FlwUtil.repeatChar(' ', digits), generateUnderline(firstCol, lastCol)); diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorReporter.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorReporter.java new file mode 100644 index 000000000..f62686bdf --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorReporter.java @@ -0,0 +1,73 @@ +package com.jozufozu.flywheel.backend.source.error; + +import java.util.Optional; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.source.SourceFile; +import com.jozufozu.flywheel.backend.source.parse.ShaderFunction; +import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; +import com.jozufozu.flywheel.backend.source.span.Span; + +public class ErrorReporter { + + public static void generateSpanError(Span span, String message) { + SourceFile file = span.getSourceFile(); + + ErrorBuilder builder = new ErrorBuilder(); + + CharSequence error = builder.error(message) + .in(file) + .pointAt(span, 2) + .build(); + + Backend.log.error(error); + } + + public static void generateFileError(SourceFile file, String message) { + + ErrorBuilder builder = new ErrorBuilder(); + + CharSequence error = builder.error(message) + .in(file) + .build(); + + Backend.log.error(error); + } + + public static void generateMissingStruct(SourceFile file, Span vertexName, CharSequence msg) { + generateMissingStruct(file, vertexName, msg, ""); + } + + public static void generateMissingStruct(SourceFile file, Span vertexName, CharSequence msg, CharSequence hint) { + Optional span = file.parent.index.getStructDefinitionsMatching(vertexName) + .stream() + .findFirst() + .map(ShaderStruct::getName); + ErrorBuilder builder = new ErrorBuilder(); + + ErrorBuilder error = builder.error(msg) + .in(file) + .pointAt(vertexName, 2) + .hintIncludeFor(span.orElse(null), hint); + + Backend.log.error(error.build()); + } + + public static void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg) { + generateMissingFunction(file, functionName, msg, ""); + } + + public static void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg, CharSequence hint) { + Optional span = file.parent.index.getFunctionDefinitionsMatching(functionName) + .stream() + .findFirst() + .map(ShaderFunction::getName); + ErrorBuilder builder = new ErrorBuilder(); + + ErrorBuilder error = builder.error(msg) + .in(file) + .hintIncludeFor(span.orElse(null), hint); + + Backend.log.error(error.build()); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/package-info.java new file mode 100644 index 000000000..536999b8f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.source.error; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/loading/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/source/package-info.java similarity index 77% rename from src/main/java/com/jozufozu/flywheel/backend/loading/package-info.java rename to src/main/java/com/jozufozu/flywheel/backend/source/package-info.java index 21e83243b..5deff62a4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/loading/package-info.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/package-info.java @@ -1,5 +1,5 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -package com.jozufozu.flywheel.backend.loading; +package com.jozufozu.flywheel.backend.source; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/AbstractShaderElement.java b/src/main/java/com/jozufozu/flywheel/backend/source/parse/AbstractShaderElement.java similarity index 56% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/AbstractShaderElement.java rename to src/main/java/com/jozufozu/flywheel/backend/source/parse/AbstractShaderElement.java index e7a623158..594ae4d02 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/AbstractShaderElement.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/parse/AbstractShaderElement.java @@ -1,6 +1,6 @@ -package com.jozufozu.flywheel.backend.pipeline.parse; +package com.jozufozu.flywheel.backend.source.parse; -import com.jozufozu.flywheel.backend.pipeline.span.Span; +import com.jozufozu.flywheel.backend.source.span.Span; public abstract class AbstractShaderElement { diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Import.java b/src/main/java/com/jozufozu/flywheel/backend/source/parse/Import.java similarity index 77% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Import.java rename to src/main/java/com/jozufozu/flywheel/backend/source/parse/Import.java index 9702b7dfb..6df09b009 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Import.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/parse/Import.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.pipeline.parse; +package com.jozufozu.flywheel.backend.source.parse; import java.util.ArrayList; import java.util.List; @@ -6,11 +6,11 @@ import java.util.Optional; import javax.annotation.Nullable; -import com.jozufozu.flywheel.backend.FileResolution; -import com.jozufozu.flywheel.backend.ShaderSources; -import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter; -import com.jozufozu.flywheel.backend.pipeline.SourceFile; -import com.jozufozu.flywheel.backend.pipeline.span.Span; +import com.jozufozu.flywheel.backend.source.FileResolution; +import com.jozufozu.flywheel.backend.source.ShaderSources; +import com.jozufozu.flywheel.backend.source.error.ErrorReporter; +import com.jozufozu.flywheel.backend.source.SourceFile; +import com.jozufozu.flywheel.backend.source.span.Span; import net.minecraft.util.ResourceLocation; diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderFunction.java b/src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderFunction.java similarity index 94% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderFunction.java rename to src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderFunction.java index c183e25d8..1621debf7 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderFunction.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderFunction.java @@ -1,11 +1,11 @@ -package com.jozufozu.flywheel.backend.pipeline.parse; +package com.jozufozu.flywheel.backend.source.parse; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.pipeline.span.Span; +import com.jozufozu.flywheel.backend.source.span.Span; public class ShaderFunction extends AbstractShaderElement { diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderStruct.java b/src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderStruct.java similarity index 77% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderStruct.java rename to src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderStruct.java index d28be3a9e..80200ff32 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderStruct.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderStruct.java @@ -1,13 +1,11 @@ -package com.jozufozu.flywheel.backend.pipeline.parse; +package com.jozufozu.flywheel.backend.source.parse; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.jozufozu.flywheel.backend.loading.ProtoProgram; -import com.jozufozu.flywheel.backend.loading.TypeHelper; -import com.jozufozu.flywheel.backend.pipeline.span.Span; +import com.jozufozu.flywheel.backend.source.span.Span; public class ShaderStruct extends AbstractShaderElement { @@ -65,14 +63,6 @@ public class ShaderStruct extends AbstractShaderElement { return fields.build(); } - public void addPrefixedAttributes(ProtoProgram builder, String prefix) { - for (StructField field : fields) { - int attributeCount = TypeHelper.getAttributeCount(field.type); - - builder.addAttribute(prefix + field.name, attributeCount); - } - } - @Override public String toString() { return "struct " + name; diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/StructField.java b/src/main/java/com/jozufozu/flywheel/backend/source/parse/StructField.java similarity index 73% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/StructField.java rename to src/main/java/com/jozufozu/flywheel/backend/source/parse/StructField.java index fcd7f6954..77e03f555 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/StructField.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/parse/StructField.java @@ -1,9 +1,8 @@ -package com.jozufozu.flywheel.backend.pipeline.parse; +package com.jozufozu.flywheel.backend.source.parse; import java.util.regex.Pattern; -import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter; -import com.jozufozu.flywheel.backend.pipeline.span.Span; +import com.jozufozu.flywheel.backend.source.span.Span; public class StructField extends AbstractShaderElement { public static final Pattern fieldPattern = Pattern.compile("(\\S+)\\s*(\\S+);"); diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Variable.java b/src/main/java/com/jozufozu/flywheel/backend/source/parse/Variable.java similarity index 68% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Variable.java rename to src/main/java/com/jozufozu/flywheel/backend/source/parse/Variable.java index 723607005..270acee61 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Variable.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/parse/Variable.java @@ -1,7 +1,6 @@ -package com.jozufozu.flywheel.backend.pipeline.parse; +package com.jozufozu.flywheel.backend.source.parse; -import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter; -import com.jozufozu.flywheel.backend.pipeline.span.Span; +import com.jozufozu.flywheel.backend.source.span.Span; public class Variable extends AbstractShaderElement { diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/source/parse/package-info.java similarity index 74% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/package-info.java rename to src/main/java/com/jozufozu/flywheel/backend/source/parse/package-info.java index 12567386d..c3951a07e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/package-info.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/parse/package-info.java @@ -1,6 +1,6 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -package com.jozufozu.flywheel.backend.pipeline.parse; +package com.jozufozu.flywheel.backend.source.parse; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/span/CharPos.java b/src/main/java/com/jozufozu/flywheel/backend/source/span/CharPos.java similarity index 87% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/span/CharPos.java rename to src/main/java/com/jozufozu/flywheel/backend/source/span/CharPos.java index ab2b2de87..0fd47d76d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/span/CharPos.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/span/CharPos.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.pipeline.span; +package com.jozufozu.flywheel.backend.source.span; /** * A position in a file. diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/span/ErrorSpan.java b/src/main/java/com/jozufozu/flywheel/backend/source/span/ErrorSpan.java similarity index 84% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/span/ErrorSpan.java rename to src/main/java/com/jozufozu/flywheel/backend/source/span/ErrorSpan.java index 8afdcb7bd..c73ba195e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/span/ErrorSpan.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/span/ErrorSpan.java @@ -1,6 +1,6 @@ -package com.jozufozu.flywheel.backend.pipeline.span; +package com.jozufozu.flywheel.backend.source.span; -import com.jozufozu.flywheel.backend.pipeline.SourceFile; +import com.jozufozu.flywheel.backend.source.SourceFile; /** * Represents a (syntactically) malformed segment of code. diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/span/Span.java b/src/main/java/com/jozufozu/flywheel/backend/source/span/Span.java similarity index 95% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/span/Span.java rename to src/main/java/com/jozufozu/flywheel/backend/source/span/Span.java index f8355c16f..fdac16a4d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/span/Span.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/span/Span.java @@ -1,8 +1,8 @@ -package com.jozufozu.flywheel.backend.pipeline.span; +package com.jozufozu.flywheel.backend.source.span; import java.util.regex.Matcher; -import com.jozufozu.flywheel.backend.pipeline.SourceFile; +import com.jozufozu.flywheel.backend.source.SourceFile; /** * A span of code in a {@link SourceFile}. diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/span/StringSpan.java b/src/main/java/com/jozufozu/flywheel/backend/source/span/StringSpan.java similarity index 82% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/span/StringSpan.java rename to src/main/java/com/jozufozu/flywheel/backend/source/span/StringSpan.java index 4eb14b8b1..788a62a08 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/span/StringSpan.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/span/StringSpan.java @@ -1,6 +1,6 @@ -package com.jozufozu.flywheel.backend.pipeline.span; +package com.jozufozu.flywheel.backend.source.span; -import com.jozufozu.flywheel.backend.pipeline.SourceFile; +import com.jozufozu.flywheel.backend.source.SourceFile; public class StringSpan extends Span { diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/span/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/source/span/package-info.java new file mode 100644 index 000000000..069d24f8d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/span/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.source.span; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/Contexts.java b/src/main/java/com/jozufozu/flywheel/core/Contexts.java index a9ce0b778..dd8e53d29 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Contexts.java +++ b/src/main/java/com/jozufozu/flywheel/core/Contexts.java @@ -2,7 +2,7 @@ package com.jozufozu.flywheel.core; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.FileResolution; +import com.jozufozu.flywheel.backend.source.FileResolution; import com.jozufozu.flywheel.backend.ResourceUtil; import com.jozufozu.flywheel.backend.SpecMetaRegistry; import com.jozufozu.flywheel.backend.pipeline.IShaderPipeline; @@ -40,8 +40,8 @@ public class Contexts { IShaderPipeline crumblingPipeline = new WorldShaderPipeline<>(backend.sources, CrumblingProgram::new, InstancingTemplate.INSTANCE, crumblingBuiltins); IShaderPipeline worldPipeline = new WorldShaderPipeline<>(backend.sources, WorldProgram::new, InstancingTemplate.INSTANCE, worldBuiltins); - CRUMBLING = backend.register(new WorldContext<>(backend, crumblingPipeline).withName(Names.CRUMBLING)); - WORLD = backend.register(new WorldContext<>(backend, worldPipeline).withName(Names.WORLD)); + CRUMBLING = backend.register(WorldContext.builder(backend, Names.CRUMBLING).build(crumblingPipeline)); + WORLD = backend.register(WorldContext.builder(backend, Names.WORLD).build(worldPipeline)); } public static class Names { diff --git a/src/main/java/com/jozufozu/flywheel/core/WorldContext.java b/src/main/java/com/jozufozu/flywheel/core/WorldContext.java index 390bbd71b..f7c156bea 100644 --- a/src/main/java/com/jozufozu/flywheel/core/WorldContext.java +++ b/src/main/java/com/jozufozu/flywheel/core/WorldContext.java @@ -1,40 +1,33 @@ package com.jozufozu.flywheel.core; +import java.util.HashMap; +import java.util.Map; import java.util.function.Supplier; import java.util.stream.Stream; import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.ShaderContext; +import com.jozufozu.flywheel.backend.IShaderContext; import com.jozufozu.flywheel.backend.material.MaterialSpec; import com.jozufozu.flywheel.backend.pipeline.IShaderPipeline; +import com.jozufozu.flywheel.core.shader.IMultiProgram; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; import net.minecraft.util.ResourceLocation; -public class WorldContext

extends ShaderContext

{ - protected ResourceLocation name; - protected Supplier> specStream; +public class WorldContext

implements IShaderContext

{ + public final Backend backend; + protected final Map> programs = new HashMap<>(); + protected final ResourceLocation name; + protected final Supplier> specStream; public final IShaderPipeline

pipeline; - public WorldContext(Backend backend, IShaderPipeline

factory) { - super(backend); - this.pipeline = factory; - - specStream = () -> backend.allMaterials() - .stream() - .map(MaterialSpec::getProgramName); - } - - public WorldContext

withName(ResourceLocation name) { + public WorldContext(Backend backend, ResourceLocation name, Supplier> specStream, IShaderPipeline

pipeline) { + this.backend = backend; this.name = name; - return this; - } - - public WorldContext

withSpecStream(Supplier> specStream) { this.specStream = specStream; - return this; + this.pipeline = pipeline; } @Override @@ -59,4 +52,45 @@ public class WorldContext

extends ShaderContext

{ backend.sources.notifyError(); } } + + @Override + public Supplier

getProgramSupplier(ResourceLocation spec) { + return programs.get(spec); + } + + @Override + public void delete() { + programs.values() + .forEach(IMultiProgram::delete); + programs.clear(); + } + + public static Builder builder(Backend backend, ResourceLocation name) { + return new Builder(backend, name); + } + + public static class Builder { + private final Backend backend; + private final ResourceLocation name; + private Supplier> specStream; + + public Builder(Backend backend, ResourceLocation name) { + this.backend = backend; + this.name = name; + } + + public Builder setSpecStream(Supplier> specStream) { + this.specStream = specStream; + return this; + } + + public

WorldContext

build(IShaderPipeline

pipeline) { + if (specStream == null) { + specStream = () -> backend.allMaterials() + .stream() + .map(MaterialSpec::getProgramName); + } + return new WorldContext<>(backend, name, specStream, pipeline); + } + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/spec/ProgramSpec.java b/src/main/java/com/jozufozu/flywheel/core/shader/spec/ProgramSpec.java index 3ae7d9430..03319d6bd 100644 --- a/src/main/java/com/jozufozu/flywheel/core/shader/spec/ProgramSpec.java +++ b/src/main/java/com/jozufozu/flywheel/core/shader/spec/ProgramSpec.java @@ -1,56 +1,54 @@ package com.jozufozu.flywheel.core.shader.spec; -import java.util.ArrayList; import java.util.Collections; import java.util.List; +import com.jozufozu.flywheel.backend.source.SourceFile; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.util.ResourceLocation; +/** + * An object describing a shader program that can be loaded by flywheel. + * + *

+ * These are defined through json. All ProgramSpecs in assets/modid/flywheel/programs are parsed and + * processed. One ProgramSpec typically specifies one "material" that can be used in game to render things. + *

+ *

+ * All shader source files in assets/modid/flywheel/shaders are completely loaded and parsed into + * {@link SourceFile SourceFiles}, but not compiled until one of them is + * referenced by a ProgramSpec. + *

+ */ public class ProgramSpec { // TODO: Block model style inheritance? public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - ResourceLocation.CODEC.fieldOf("vert") - .forGetter(ProgramSpec::getVert), - ResourceLocation.CODEC.fieldOf("frag") - .forGetter(ProgramSpec::getFrag), + ResourceLocation.CODEC.fieldOf("source") + .forGetter(ProgramSpec::getSource), ProgramState.CODEC.listOf() .optionalFieldOf("states", Collections.emptyList()) .forGetter(ProgramSpec::getStates)) .apply(instance, ProgramSpec::new)); public ResourceLocation name; - public final ResourceLocation vert; - public final ResourceLocation frag; + public final ResourceLocation source; public final List states; - public ProgramSpec(ResourceLocation vert, ResourceLocation frag, List states) { - this.vert = vert; - this.frag = frag; + public ProgramSpec(ResourceLocation source, List states) { + this.source = source; this.states = states; } - public ProgramSpec(ResourceLocation name, ResourceLocation vert, ResourceLocation frag) { - this.name = name; - this.vert = vert; - this.frag = frag; - this.states = new ArrayList<>(); - } - public void setName(ResourceLocation name) { this.name = name; } - public ResourceLocation getVert() { - return vert; - } - - public ResourceLocation getFrag() { - return frag; + public ResourceLocation getSource() { + return source; } public List getStates() { diff --git a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java index be51b342c..2215abca1 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java @@ -95,6 +95,9 @@ public class RenderHooksMixin { // Instancing + /** + * This gets called when a block is marked for rerender by vanilla. + */ @Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;)V") private void checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) { InstancedRenderDispatcher.getTiles(level) diff --git a/src/main/resources/assets/flywheel/flywheel/programs/model.json b/src/main/resources/assets/flywheel/flywheel/programs/model.json index b30bb0fe2..7ba27d10b 100644 --- a/src/main/resources/assets/flywheel/flywheel/programs/model.json +++ b/src/main/resources/assets/flywheel/flywheel/programs/model.json @@ -1,6 +1,5 @@ { - "vert": "flywheel:model.vert", - "frag": "flywheel:block.frag", + "source": "flywheel:model.vert", "states": [ { "when": { diff --git a/src/main/resources/assets/flywheel/flywheel/programs/oriented.json b/src/main/resources/assets/flywheel/flywheel/programs/oriented.json index 0340b5602..32399f7af 100644 --- a/src/main/resources/assets/flywheel/flywheel/programs/oriented.json +++ b/src/main/resources/assets/flywheel/flywheel/programs/oriented.json @@ -1,6 +1,5 @@ { - "vert": "flywheel:oriented.vert", - "frag": "flywheel:block.frag", + "source": "flywheel:oriented.vert", "states": [ { "when": {