From ef512d8cf97138d0d411801161063138af9303b5 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 7 Aug 2021 01:00:32 -0700 Subject: [PATCH] Almost there - WorldContext sort of uses a shader pipeline interface - Smarter import resolution - Reorganize shader sources - Rewritten shader templating - Still need builtin support --- .../flywheel/backend/FileResolution.java | 46 +++++ .../flywheel/backend/ShaderContext.java | 56 +---- .../flywheel/backend/ShaderSources.java | 194 +++++++----------- .../flywheel/backend/gl/shader/GlProgram.java | 6 +- .../flywheel/backend/gl/shader/GlShader.java | 16 +- .../flywheel/backend/loading/Program.java | 10 +- .../flywheel/backend/pipeline/GlShader.java | 39 ---- .../backend/pipeline/IShaderPipeline.java | 11 + .../flywheel/backend/pipeline/Includer.java | 10 +- .../backend/pipeline/LegacyPipeline.java | 137 +++++++++++++ .../pipeline/PipelineProgramBuilder.java | 34 --- .../flywheel/backend/pipeline/Shader.java | 26 +-- .../backend/pipeline/ShaderBuilder.java | 79 +++++++ .../flywheel/backend/pipeline/SourceFile.java | 34 ++- .../flywheel/backend/pipeline/Template.java | 123 +++++++++++ .../backend/pipeline/WorldShaderPipeline.java | 72 +++++-- .../backend/pipeline/parse/Import.java | 54 +++++ .../backend/pipeline/parse/Include.java | 58 ------ .../pipeline/parse/ShaderFunction.java | 32 +-- .../backend/pipeline/parse/ShaderStruct.java | 9 + .../backend/pipeline/parse/StructField.java | 2 +- .../backend/pipeline/parse/Variable.java | 7 +- .../com/jozufozu/flywheel/core/Contexts.java | 3 + .../jozufozu/flywheel/core/WorldContext.java | 68 +----- .../core/crumbling/CrumblingProgram.java | 6 +- .../core/shader/ExtensibleGlProgram.java | 13 +- .../flywheel/core/shader/WorldProgram.java | 6 +- .../jozufozu/flywheel/util/StreamUtil.java | 60 ++++++ 28 files changed, 740 insertions(+), 471 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/FileResolution.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/GlShader.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/IShaderPipeline.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/LegacyPipeline.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/PipelineProgramBuilder.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderBuilder.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Import.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Include.java create mode 100644 src/main/java/com/jozufozu/flywheel/util/StreamUtil.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/FileResolution.java b/src/main/java/com/jozufozu/flywheel/backend/FileResolution.java new file mode 100644 index 000000000..4b60c8d80 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/FileResolution.java @@ -0,0 +1,46 @@ +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.ErrorReporter; +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) { + ErrorReporter.generateSpanError(foundSpans.get(0), "could not find source"); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/ShaderContext.java b/src/main/java/com/jozufozu/flywheel/backend/ShaderContext.java index a265b1999..f1e4ff2c1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/ShaderContext.java +++ b/src/main/java/com/jozufozu/flywheel/backend/ShaderContext.java @@ -14,6 +14,7 @@ import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.backend.loading.Program; import com.jozufozu.flywheel.backend.loading.Shader; +import com.jozufozu.flywheel.backend.pipeline.SourceFile; import com.jozufozu.flywheel.core.shader.IMultiProgram; import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; import com.jozufozu.flywheel.core.shader.spec.ProgramState; @@ -35,65 +36,10 @@ public abstract class ShaderContext

implements IShaderConte return programs.get(spec); } - public Program loadAndLink(ProgramSpec spec, @Nullable ProgramState state) { - Shader vertexFile = getSource(ShaderType.VERTEX, spec.vert); - Shader fragmentFile = getSource(ShaderType.FRAGMENT, spec.frag); - - if (state != null) { - vertexFile.defineAll(state.getDefines()); - fragmentFile.defineAll(state.getDefines()); - } - - Program linked = link(buildProgram(spec.name, vertexFile, fragmentFile)); - - String descriptor = linked.program + ": " + spec.name; - - if (state != null) - descriptor += "#" + state; - - Backend.log.debug(descriptor); - - return linked; - } - - protected Shader getSource(ShaderType type, ResourceLocation name) { - return backend.sources.source(name, type); - } - - protected Program link(Program program) { - return program.link().deleteLinkedShaders(); - } - @Override public void delete() { programs.values() .forEach(IMultiProgram::delete); programs.clear(); } - - /** - * Ingests the given shaders, compiling them and linking them together after applying the transformer to the source. - * - * @param name What should we call this program if something goes wrong? - * @param shaders What are the different shader stages that should be linked together? - * @return A program with all provided shaders attached - */ - protected static Program buildProgram(ResourceLocation name, Shader... shaders) { - List compiled = new ArrayList<>(shaders.length); - try { - Program builder = new Program(name); - - for (Shader shader : shaders) { - GlShader sh = new GlShader(shader); - compiled.add(sh); - - builder.attachShader(shader, sh); - } - - return builder; - } finally { - compiled.forEach(GlObject::delete); - } - } - } diff --git a/src/main/java/com/jozufozu/flywheel/backend/ShaderSources.java b/src/main/java/com/jozufozu/flywheel/backend/ShaderSources.java index a81a8f520..2e5f0a3cc 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/ShaderSources.java +++ b/src/main/java/com/jozufozu/flywheel/backend/ShaderSources.java @@ -1,27 +1,16 @@ package com.jozufozu.flywheel.backend; -import java.io.BufferedReader; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.channels.Channels; -import java.nio.channels.FileChannel; -import java.nio.channels.ReadableByteChannel; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Predicate; -import java.util.stream.Stream; import javax.annotation.Nonnull; -import org.lwjgl.system.MemoryUtil; - import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -35,9 +24,10 @@ import com.jozufozu.flywheel.backend.pipeline.SourceFile; import com.jozufozu.flywheel.backend.pipeline.WorldShaderPipeline; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; +import com.jozufozu.flywheel.core.shader.extension.IProgramExtension; import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; import com.jozufozu.flywheel.event.GatherContextEvent; -import com.mojang.blaze3d.systems.RenderSystem; +import com.jozufozu.flywheel.util.StreamUtil; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; @@ -62,6 +52,8 @@ public class ShaderSources implements ISelectiveResourceReloadListener { private final Map shaderSource = new HashMap<>(); private final Map shaderSources = new HashMap<>(); + private final Map resolutions = new HashMap<>(); + private boolean shouldCrash; private final Backend backend; @@ -73,6 +65,42 @@ public class ShaderSources implements ISelectiveResourceReloadListener { } } + public SourceFile source(ResourceLocation name) { + SourceFile source = shaderSources.get(name); + + if (source == null) { + throw new ShaderLoadingException(String.format("shader '%s' does not exist", name)); + } + + return source; + } + + public FileResolution resolveFile(ResourceLocation fileLoc) { + return resolutions.computeIfAbsent(fileLoc, FileResolution::new); + } + + @Deprecated + public Shader source(ResourceLocation name, ShaderType type) { + return new Shader(this, type, name, getShaderSource(name)); + } + + @Deprecated + public void notifyError() { + shouldCrash = true; + } + + @Deprecated + @Nonnull + public String getShaderSource(ResourceLocation loc) { + String source = shaderSource.get(loc); + + if (source == null) { + throw new ShaderLoadingException(String.format("shader '%s' does not exist", loc)); + } + + return source; + } + @Override public void onResourceManagerReload(IResourceManager manager, Predicate predicate) { if (predicate.test(VanillaResourceType.SHADERS)) { @@ -87,15 +115,20 @@ public class ShaderSources implements ISelectiveResourceReloadListener { ModLoader.get() .postEvent(new GatherContextEvent(backend)); + resolutions.clear(); + loadProgramSpecs(manager); loadShaderSources(manager); - shaderSources.values().forEach(SourceFile::resolveIncludes); + for (FileResolution resolution : resolutions.values()) { + resolution.resolve(this); + } - WorldShaderPipeline pl = new WorldShaderPipeline<>(this); + WorldShaderPipeline pl = new WorldShaderPipeline<>(this, WorldProgram::new); - SourceFile source = source(new ResourceLocation(Flywheel.ID, "model.glsl")); - pl.compile(source, Collections.emptyList()); +// ResourceLocation name = new ResourceLocation(Flywheel.ID, "model.glsl"); +// SourceFile source = source(name); +// pl.compile(name, source, Collections.emptyList()); for (IShaderContext context : backend.allContexts()) { context.load(); @@ -120,6 +153,31 @@ public class ShaderSources implements ISelectiveResourceReloadListener { } } + private void loadShaderSources(IResourceManager manager) { + Collection allShaders = manager.listResources(SHADER_DIR, s -> { + for (String ext : EXTENSIONS) { + if (s.endsWith(ext)) return true; + } + return false; + }); + + for (ResourceLocation location : allShaders) { + try { + IResource resource = manager.getResource(location); + + String source = StreamUtil.readToString(resource.getInputStream()); + + ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR); + + shaderSource.put(name, source); + shaderSources.put(name, new SourceFile(this, name, source)); + } catch (IOException e) { + + } + } + } + + private void loadProgramSpecs(IResourceManager manager) { Collection programSpecs = manager.listResources(PROGRAM_DIR, s -> s.endsWith(".json")); @@ -127,7 +185,7 @@ public class ShaderSources implements ISelectiveResourceReloadListener { try { IResource file = manager.getResource(location); - String s = readToString(file.getInputStream()); + String s = StreamUtil.readToString(file.getInputStream()); ResourceLocation specName = ResourceUtil.trim(location, PROGRAM_DIR, ".json"); @@ -145,104 +203,4 @@ public class ShaderSources implements ISelectiveResourceReloadListener { } } } - - @Deprecated - public void notifyError() { - shouldCrash = true; - } - - @Deprecated - @Nonnull - public String getShaderSource(ResourceLocation loc) { - String source = shaderSource.get(loc); - - if (source == null) { - throw new ShaderLoadingException(String.format("shader '%s' does not exist", loc)); - } - - return source; - } - - private void loadShaderSources(IResourceManager manager) { - Collection allShaders = manager.listResources(SHADER_DIR, s -> { - for (String ext : EXTENSIONS) { - if (s.endsWith(ext)) return true; - } - return false; - }); - - for (ResourceLocation location : allShaders) { - try { - IResource resource = manager.getResource(location); - - String source = readToString(resource.getInputStream()); - - ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR); - - shaderSource.put(name, source); - shaderSources.put(name, new SourceFile(this, name, source)); - } catch (IOException e) { - - } - } - } - - @Deprecated - public Shader source(ResourceLocation name, ShaderType type) { - return new Shader(this, type, name, getShaderSource(name)); - } - - public SourceFile source(ResourceLocation name) { - SourceFile source = shaderSources.get(name); - - if (source == null) { - throw new ShaderLoadingException(String.format("shader '%s' does not exist", name)); - } - - return source; - } - - public String readToString(InputStream is) { - RenderSystem.assertThread(RenderSystem::isOnRenderThread); - ByteBuffer bytebuffer = null; - - try { - bytebuffer = readToBuffer(is); - int i = bytebuffer.position(); - ((Buffer) bytebuffer).rewind(); - return MemoryUtil.memASCII(bytebuffer, i); - } catch (IOException e) { - - } finally { - if (bytebuffer != null) { - MemoryUtil.memFree(bytebuffer); - } - - } - - return null; - } - - public ByteBuffer readToBuffer(InputStream is) throws IOException { - ByteBuffer bytebuffer; - if (is instanceof FileInputStream) { - FileInputStream fileinputstream = (FileInputStream) is; - FileChannel filechannel = fileinputstream.getChannel(); - bytebuffer = MemoryUtil.memAlloc((int) filechannel.size() + 1); - - while (filechannel.read(bytebuffer) != -1) { - } - } else { - bytebuffer = MemoryUtil.memAlloc(8192); - ReadableByteChannel readablebytechannel = Channels.newChannel(is); - - while (readablebytechannel.read(bytebuffer) != -1) { - if (bytebuffer.remaining() == 0) { - bytebuffer = MemoryUtil.memRealloc(bytebuffer, bytebuffer.capacity() * 2); - } - } - } - - return bytebuffer; - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java index 73aadabff..8b7a9f8e6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java @@ -18,9 +18,9 @@ public abstract class GlProgram extends GlObject { public final ResourceLocation name; - protected GlProgram(Program program) { - setHandle(program.program); - this.name = program.name; + protected GlProgram(ResourceLocation name, int handle) { + this.name = name; + setHandle(handle); } public void bind() { 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 835e00540..43e7b39e0 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 @@ -14,24 +14,24 @@ public class GlShader extends GlObject { public final ResourceLocation name; public final ShaderType type; - public GlShader(Shader shader) { - this.type = shader.type; - this.name = shader.name; - int handle = GL20.glCreateShader(shader.type.glEnum); + public GlShader(ResourceLocation name, ShaderType type, CharSequence source) { + this.name = name; + this.type = type; + int handle = GL20.glCreateShader(type.glEnum); - GlCompat.safeShaderSource(handle, shader.getSource()); + GlCompat.safeShaderSource(handle, source); GL20.glCompileShader(handle); String log = GL20.glGetShaderInfoLog(handle); if (!log.isEmpty()) { - Backend.log.error("Shader compilation log for " + shader.name + ": " + log); - Backend.log.error(shader.printSource()); + Backend.log.error("Shader compilation log for " + name + ": " + log); + Backend.log.error(source); } //Backend.log.debug(shader.printSource()); if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) { - throw new RuntimeException("Could not compile " + shader.name + ". See log for details."); + throw new RuntimeException("Could not compile " + name + ". See log for details."); } setHandle(handle); diff --git a/src/main/java/com/jozufozu/flywheel/backend/loading/Program.java b/src/main/java/com/jozufozu/flywheel/backend/loading/Program.java index c5a8d9d96..d1748467a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/loading/Program.java +++ b/src/main/java/com/jozufozu/flywheel/backend/loading/Program.java @@ -24,17 +24,16 @@ import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.util.ResourceLocation; public class Program { - public final ResourceLocation name; public final int program; + public ResourceLocation name; private int attributeIndex; public final Map attached; private final IntList shaders; - public Program(ResourceLocation name) { - this.name = name; + public Program() { this.program = glCreateProgram(); attached = new EnumMap<>(ShaderType.class); shaders = new IntArrayList(2); @@ -57,13 +56,14 @@ public class Program { /** * Links the attached shaders to this program. */ - public Program link() { + public Program link(ResourceLocation name) { + this.name = name; glLinkProgram(this.program); String log = glGetProgramInfoLog(this.program); if (!log.isEmpty()) { - Backend.log.debug("Program link log for " + this.name + ": " + log); + Backend.log.debug("Program link log for " + name + ": " + log); } int result = glGetProgrami(this.program, GL_LINK_STATUS); diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/GlShader.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/GlShader.java deleted file mode 100644 index d92ac9c60..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/GlShader.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import org.lwjgl.opengl.GL20; - -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.gl.GlObject; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; - -public class GlShader extends GlObject { - - public GlShader(ShaderType type, CharSequence source) { - int handle = GL20.glCreateShader(type.glEnum); - - GlCompat.safeShaderSource(handle, source); - - GL20.glCompileShader(handle); - - String log = GL20.glGetShaderInfoLog(handle); - - if (!log.isEmpty()) { - Backend.log.error("Shader compilation log for " + "DUBUG"+ ": " + log); - Backend.log.error(source); - } - //Backend.log.debug(shader.printSource()); - - if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) { - throw new RuntimeException("Could not compile " + "DEBUG" + ". See log for details."); - } - - - setHandle(handle); - } - - @Override - protected void deleteInternal(int handle) { - - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/IShaderPipeline.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/IShaderPipeline.java new file mode 100644 index 000000000..36eff8e28 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/IShaderPipeline.java @@ -0,0 +1,11 @@ +package com.jozufozu.flywheel.backend.pipeline; + +import com.jozufozu.flywheel.core.shader.IMultiProgram; +import com.jozufozu.flywheel.core.shader.WorldProgram; +import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; + +public interface IShaderPipeline

{ + + IMultiProgram

compile(ProgramSpec spec); + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Includer.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Includer.java index 3da3da6a0..d76bb2327 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Includer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Includer.java @@ -4,11 +4,9 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.Stack; import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.ShaderSources; -import com.jozufozu.flywheel.backend.pipeline.parse.Include; +import com.jozufozu.flywheel.backend.pipeline.parse.Import; import net.minecraft.util.ResourceLocation; @@ -27,11 +25,11 @@ public class Includer { } private static void process(Set seen, List out, SourceFile source) { - ImmutableList includes = source.getIncludes(); + ImmutableList imports = source.getIncludes(); - for (Include include : includes) { + for (Import use : imports) { - SourceFile file = include.getTarget(); + SourceFile file = use.getFile(); if (file != null && seen.add(file.name)) { process(seen, out, file); diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/LegacyPipeline.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/LegacyPipeline.java new file mode 100644 index 000000000..c19324483 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/LegacyPipeline.java @@ -0,0 +1,137 @@ +package com.jozufozu.flywheel.backend.pipeline; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.ShaderSources; +import com.jozufozu.flywheel.backend.gl.GlObject; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.backend.loading.Program; +import com.jozufozu.flywheel.backend.loading.ProgramTemplate; +import com.jozufozu.flywheel.backend.loading.Shader; +import com.jozufozu.flywheel.backend.loading.ShaderLoadingException; +import com.jozufozu.flywheel.backend.loading.ShaderTransformer; +import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram; +import com.jozufozu.flywheel.core.shader.GameStateProgram; +import com.jozufozu.flywheel.core.shader.IMultiProgram; +import com.jozufozu.flywheel.core.shader.WorldProgram; +import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; +import com.jozufozu.flywheel.core.shader.spec.ProgramState; + +public class LegacyPipeline

implements IShaderPipeline

{ + + private static final String declaration = "#flwbuiltins"; + private static final Pattern builtinPattern = Pattern.compile(declaration); + + + private final ShaderSources sources; + protected ShaderTransformer transformer; + private final ProgramTemplate template; + private final ExtensibleGlProgram.Factory

factory; + private final Map builtinSources; + + + public LegacyPipeline(ShaderSources sources, ProgramTemplate template, ExtensibleGlProgram.Factory

factory, Map builtinSources) { + this.sources = sources; + this.template = template; + this.factory = factory; + + transformer = new ShaderTransformer().pushStage(this::injectBuiltins) + .pushStage(Shader::processIncludes) + .pushStage(template) + .pushStage(Shader::processIncludes); + this.builtinSources = builtinSources; + } + + @Override + public IMultiProgram

compile(ProgramSpec spec) { + GameStateProgram.Builder

builder = GameStateProgram.builder(compile(spec, null)); + + for (ProgramState state : spec.states) { + + builder.withVariant(state.getContext(), compile(spec, state)); + } + + return builder.build(); + } + + /** + * Ingests the given shaders, compiling them and linking them together after applying the transformer to the source. + * + * @param shaders What are the different shader stages that should be linked together? + * @return A program with all provided shaders attached + */ + protected static Program buildProgram(Shader... shaders) { + List compiled = new ArrayList<>(shaders.length); + try { + Program builder = new Program(); + + for (Shader shader : shaders) { + GlShader sh = new GlShader(shader.name, shader.type, shader.getSource()); + compiled.add(sh); + + builder.attachShader(shader, sh); + } + + return builder; + } finally { + compiled.forEach(GlObject::delete); + } + } + + public Program loadAndLink(ProgramSpec spec, @Nullable ProgramState state) { + Shader vertexFile = sources.source(spec.vert, ShaderType.VERTEX); + Shader fragmentFile = sources.source(spec.frag, ShaderType.FRAGMENT); + + transformer.transformSource(vertexFile); + transformer.transformSource(fragmentFile); + + if (state != null) { + vertexFile.defineAll(state.getDefines()); + fragmentFile.defineAll(state.getDefines()); + } + + Program program = buildProgram(vertexFile, fragmentFile); + template.attachAttributes(program); + + program.link(spec.name).deleteLinkedShaders(); + + String descriptor = program.program + ": " + spec.name; + + if (state != null) + descriptor += "#" + state; + + Backend.log.debug(descriptor); + + return program; + } + + private P compile(ProgramSpec spec, @Nullable ProgramState state) { + if (state != null) { + Program program = loadAndLink(spec, state); + return factory.create(program.name, program.program, state.getExtensions()); + } else { + Program program = loadAndLink(spec, null); + return factory.create(program.name, program.program, null); + } + } + + /** + * Replace #flwbuiltins with whatever expansion this context provides for the given shader. + */ + public void injectBuiltins(Shader shader) { + Matcher matcher = builtinPattern.matcher(shader.getSource()); + + if (matcher.find()) shader.setSource(matcher.replaceFirst(builtinSources.get(shader.type))); + else + throw new ShaderLoadingException(String.format("%s is missing %s, cannot use in World Context", shader.type.name, declaration)); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/PipelineProgramBuilder.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/PipelineProgramBuilder.java deleted file mode 100644 index 041e532ed..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/PipelineProgramBuilder.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -public class PipelineProgramBuilder { - - private final List sources = new ArrayList<>(); - - public PipelineProgramBuilder() { - - } - - public PipelineProgramBuilder include(SourceFile file) { - sources.add(file); - return this; - } - - public PipelineProgramBuilder includeAll(Collection files) { - sources.addAll(files); - return this; - } - - public CharSequence build() { - StringBuilder builder = new StringBuilder(); - - for (SourceFile source : sources) { - builder.append(source.getElidedSource()); - } - - return builder; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Shader.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Shader.java index 2f44f774f..d45368f52 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Shader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Shader.java @@ -1,31 +1,11 @@ package com.jozufozu.flywheel.backend.pipeline; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import net.minecraft.util.ResourceLocation; + public class Shader { - private final GLSLVersion version; - private final CharSequence source; - public Shader(GLSLVersion version, CharSequence source) { - this.version = version; - this.source = source; - } - - public GlShader create(ShaderType type) { - - StringBuilder source = new StringBuilder(); - - source.append("#version ") - .append(version.version) - .append('\n'); - - source.append("#define ") - .append(type.define) - .append('\n'); - - source.append(this.source); - - return new GlShader(type, source); - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderBuilder.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderBuilder.java new file mode 100644 index 000000000..35cd5e174 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderBuilder.java @@ -0,0 +1,79 @@ +package com.jozufozu.flywheel.backend.pipeline; + +import java.util.ArrayList; +import java.util.List; + +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; + +import net.minecraft.util.ResourceLocation; + +public class ShaderBuilder { + + public final ResourceLocation name; + public final Template template; + + private SourceFile mainFile; + private GLSLVersion version; + + private StringBuilder source; + private StringBuilder defines; + private CharSequence footer; + + public ShaderBuilder(ResourceLocation name, Template template) { + this.name = name; + this.template = template; + } + + public ShaderBuilder setVersion(GLSLVersion version) { + this.version = version; + return this; + } + + public ShaderBuilder setDefines(List defs) { + defines = new StringBuilder(); + + for (String def : defs) { + defines.append("#define ") + .append(def) + .append('\n'); + } + return this; + } + + public ShaderBuilder setFooter(CharSequence footer) { + this.footer = footer; + return this; + } + + public ShaderBuilder setMainSource(SourceFile file) { + if (mainFile == file) return this; + + mainFile = file; + source = new StringBuilder(); + + for (SourceFile includeFile : Includer.recurseIncludes(file)) { + source.append(includeFile.getElidedSource()); + } + source.append(file.getElidedSource()); + + return this; + } + + public GlShader compile(ResourceLocation name, ShaderType type) { + + StringBuilder finalSource = new StringBuilder(); + + finalSource.append("#version ") + .append(version) + .append('\n') + .append("#define ") + .append(type.define) + .append('\n') + .append(defines != null ? defines : "") + .append(source) + .append(template.footer(type, mainFile)); + + return new GlShader(name, type, finalSource); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/SourceFile.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/SourceFile.java index 73b60d52c..618316ffb 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/SourceFile.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/SourceFile.java @@ -10,9 +10,7 @@ import java.util.regex.Pattern; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.jozufozu.flywheel.backend.ShaderSources; -import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter; -import com.jozufozu.flywheel.backend.pipeline.parse.AbstractShaderElement; -import com.jozufozu.flywheel.backend.pipeline.parse.Include; +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; @@ -47,7 +45,7 @@ public class SourceFile { private final ImmutableMap structs; // Includes ordered as defined in the source - private final ImmutableList includes; + private final ImmutableList imports; // Sections of the source that must be trimmed for compilation. private final List elisions = new ArrayList<>(); @@ -60,7 +58,7 @@ public class SourceFile { this.lineStarts = createLineStarts(); this.lines = createLineList(lineStarts); - this.includes = parseIncludes(); + this.imports = parseImports(); this.functions = parseFunctions(); this.structs = parseStructs(); @@ -83,14 +81,12 @@ public class SourceFile { return functions; } - public ImmutableList getIncludes() { - return includes; + public ImmutableMap getStructs() { + return structs; } - public void resolveIncludes() { - for (Include include : includes) { - include.resolve(); - } + public ImmutableList getIncludes() { + return imports; } public CharPos getCharPos(int charPos) { @@ -210,7 +206,7 @@ public class SourceFile { private ImmutableMap parseStructs() { Matcher matcher = ShaderStruct.struct.matcher(source); - ImmutableMap.Builder functions = ImmutableMap.builder(); + ImmutableMap.Builder structs = ImmutableMap.builder(); while (matcher.find()) { Span self = Span.fromMatcher(this, matcher); Span name = Span.fromMatcher(this, matcher, 1); @@ -218,31 +214,31 @@ public class SourceFile { ShaderStruct shaderStruct = new ShaderStruct(self, name, body); - functions.put(body.get(), shaderStruct); + structs.put(name.get(), shaderStruct); } - return functions.build(); + return structs.build(); } /** * Scan the source for #use "..." directives. - * Records the contents of the directive into an {@link Include} object, and marks the directive for elision. + * Records the contents of the directive into an {@link Import} object, and marks the directive for elision. */ - private ImmutableList parseIncludes() { + private ImmutableList parseImports() { Matcher uses = includePattern.matcher(source); - List includes = new ArrayList<>(); + List imports = new ArrayList<>(); while (uses.find()) { Span use = Span.fromMatcher(this, uses); Span file = Span.fromMatcher(this, uses, 1); - includes.add(new Include(parent, use, file)); + imports.add(new Import(parent, use, file)); elisions.add(use); // we have to trim that later } - return ImmutableList.copyOf(includes); + return ImmutableList.copyOf(imports); } /** 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..176443918 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java @@ -0,0 +1,123 @@ +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.pipeline.parse.ShaderFunction; +import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct; +import com.jozufozu.flywheel.backend.pipeline.parse.StructField; +import com.jozufozu.flywheel.backend.pipeline.parse.Variable; + +public class Template { + + public CharSequence footer(ShaderType type, SourceFile file) { + switch (type) { + case VERTEX: + return vertexFooter(file); + case FRAGMENT: + return fragmentFooter(file); + } + + return ""; + } + + public CharSequence vertexFooter(SourceFile file) { + ShaderFunction vertexMain = file.getFunctions() + .get("vertex"); + + ImmutableList parameters = vertexMain.getParameters(); + + ShaderStruct interpolant = file.getStructs() + .get(vertexMain.returnType()); + + ShaderStruct vertex = file.getStructs() + .get(parameters.get(0) + .typeName() + .get()); + + ShaderStruct instance = file.getStructs() + .get(parameters.get(1) + .typeName() + .get()); + + StringBuilder template = new StringBuilder(); + + prefixFields(template, vertex, "attribute", "a_v_"); + prefixFields(template, instance, "attribute", "a_i_"); + prefixFields(template, interpolant, "varying", "v2f_"); + + template.append("void main() {\n"); + template.append(vertex.name) + .append(" v;\n"); + assignFields(template, vertex, "v.", "a_v_"); + + template.append(instance.name) + .append(" i;\n"); + assignFields(template, instance, "i.", "a_i_"); + + template.append(interpolant.name) + .append(" o = ") + .append(vertexMain.call("v", "i")) + .append(";\n"); + + assignFields(template, interpolant, "v2f_", "o."); + + template.append('}'); + + return template; + } + + public CharSequence fragmentFooter(SourceFile file) { + ShaderFunction fragmentMain = file.getFunctions() + .get("fragment"); + + ImmutableList parameters = fragmentMain.getParameters(); + + ShaderStruct interpolant = file.getStructs() + .get(parameters.get(0) + .typeName() + .get()); + + StringBuilder template = new StringBuilder(); + + prefixFields(template, interpolant, "varying", "v2f_"); + + template.append("void main() {\n"); + template.append(interpolant.name) + .append(" o;\n"); + assignFields(template, interpolant, "o.", "v2f_"); + + template.append(fragmentMain.call("o")) + .append(";\n"); + + template.append('}'); + + return template; + } + + 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/pipeline/WorldShaderPipeline.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShaderPipeline.java index ca7f3a074..9f98d5c69 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShaderPipeline.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShaderPipeline.java @@ -1,45 +1,91 @@ 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.ShaderSources; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.backend.loading.Program; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram; +import com.jozufozu.flywheel.core.shader.GameStateProgram; import com.jozufozu.flywheel.core.shader.IMultiProgram; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; import com.jozufozu.flywheel.core.shader.spec.ProgramState; +import net.minecraft.util.ResourceLocation; + public class WorldShaderPipeline

{ private final ShaderSources sources; - public WorldShaderPipeline(ShaderSources sources) { + private final ExtensibleGlProgram.Factory

factory; + + public WorldShaderPipeline(ShaderSources sources, ExtensibleGlProgram.Factory

factory) { this.sources = sources; + this.factory = factory; } - @Nullable // TODO: temporary null return public IMultiProgram

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

compile(SourceFile file, List variants) { - PipelineProgramBuilder builder = new PipelineProgramBuilder(); + public IMultiProgram

compile(ResourceLocation name, SourceFile file, List variants) { + ShaderBuilder shader = new ShaderBuilder(name, new Template()) + .setMainSource(file) + .setVersion(GLSLVersion.V120); - builder.includeAll(Includer.recurseIncludes(file)); + GameStateProgram.Builder

builder = GameStateProgram.builder(compile(shader, name, null)); - builder.include(file); + for (ProgramState variant : variants) { + builder.withVariant(variant.getContext(), compile(shader, name, variant)); + } - CharSequence output = builder.build(); + return builder.build(); + } - //Program program = new Program() + private P compile(ShaderBuilder shader, ResourceLocation name, @Nullable ProgramState variant) { - return null; + if (variant != null) { + shader.setDefines(variant.getDefines()); + } + + GlShader vertex = shader.compile(name, ShaderType.VERTEX); + GlShader fragment = shader.compile(name, ShaderType.FRAGMENT); + + int program = GL20.glCreateProgram(); + + GL20.glAttachShader(program, vertex.handle()); + GL20.glAttachShader(program, fragment.handle()); + + String log = glGetProgramInfoLog(program); + + if (!log.isEmpty()) { + Backend.log.debug("Program link log for " + name + ": " + log); + } + + int result = glGetProgrami(program, GL_LINK_STATUS); + + if (result != GL_TRUE) { + throw new RuntimeException("Shader program linking failed, see log for details"); + } + + if (variant != null) { + return factory.create(name, program, variant.getExtensions()); + } else { + return factory.create(name, program, null); + } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Import.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Import.java new file mode 100644 index 000000000..ecfbfc564 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Import.java @@ -0,0 +1,54 @@ +package com.jozufozu.flywheel.backend.pipeline.parse; + +import java.util.ArrayList; +import java.util.List; + +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 net.minecraft.util.ResourceLocation; + +public class Import extends AbstractShaderElement { + + public static final List IMPORTS = new ArrayList<>(); + + private final Span file; + + private final FileResolution resolution; + private final ResourceLocation fileLoc; + + public Import(ShaderSources parent, Span self, Span file) { + super(self); + this.file = file; + + fileLoc = toRL(file); + resolution = parent.resolveFile(fileLoc); + resolution.addSpan(file); + + IMPORTS.add(this); + } + + private ResourceLocation toRL(Span file) { + try { + return new ResourceLocation(file.get()); + } catch (RuntimeException error) { + ErrorReporter.generateSpanError(file, "malformed source name"); + } + + return new ResourceLocation(""); + } + + @Nullable + public SourceFile getFile() { + return resolution.getFile(); + } + + public ResourceLocation getFileLoc() { + return resolution.getFileLoc(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Include.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Include.java deleted file mode 100644 index 497b99240..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Include.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline.parse; - -import java.util.Optional; - -import javax.annotation.Nullable; - -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 net.minecraft.util.ResourceLocation; - -public class Include extends AbstractShaderElement { - - private final ShaderSources sources; - private Span file; - - private ResourceLocation fileLoc; - private SourceFile resolution; - - - public Include(ShaderSources sources, Span self, Span file) { - super(self); - this.sources = sources; - this.file = file; - - try { - fileLoc = new ResourceLocation(file.get()); - } catch (RuntimeException error) { - ErrorReporter.generateSpanError(file, "malformed source name"); - } - } - - public boolean isResolved() { - return resolution != null; - } - - @Nullable - public SourceFile getTarget() { - return resolution; - } - - public ResourceLocation getFile() { - return fileLoc; - } - - public void resolve() { - - if (fileLoc == null) return; - - try { - resolution = sources.source(fileLoc); - } catch (RuntimeException error) { - ErrorReporter.generateSpanError(file, "could not find source"); - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderFunction.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderFunction.java index 4c4276e55..2bfe7648e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderFunction.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderFunction.java @@ -1,12 +1,10 @@ package com.jozufozu.flywheel.backend.pipeline.parse; -import java.util.ArrayList; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter; +import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.backend.pipeline.span.Span; public class ShaderFunction extends AbstractShaderElement { @@ -19,7 +17,7 @@ public class ShaderFunction extends AbstractShaderElement { private final Span args; private final Span body; - private final List parameters; + private final ImmutableList parameters; public ShaderFunction(Span self, Span type, Span name, Span args, Span body) { super(self); @@ -28,34 +26,44 @@ public class ShaderFunction extends AbstractShaderElement { this.args = args; this.body = body; - this.parameters = new ArrayList<>(); - - parseArguments(); + this.parameters = parseArguments(); } public String call(String... args) { - return name + "(" + String.join(", ", args) + ");"; + return name + "(" + String.join(", ", args) + ")"; } - protected void parseArguments() { - if (args.isErr() || args.isEmpty()) return; + public ImmutableList getParameters() { + return parameters; + } + + public String returnType() { + return type.get(); + } + + protected ImmutableList parseArguments() { + if (args.isErr() || args.isEmpty()) return ImmutableList.of(); Matcher arguments = argument.matcher(args.get()); + ImmutableList.Builder builder = ImmutableList.builder(); + while (arguments.find()) { Span self = Span.fromMatcher(args, arguments); Span type = Span.fromMatcher(args, arguments, 1); Span name = Span.fromMatcher(args, arguments, 2); - parameters.add(new Variable(self, type, name)); + builder.add(new Variable(self, type, name)); } + + return builder.build(); } @Override public String toString() { String p = parameters.stream() - .map(Variable::getType) + .map(Variable::typeName) .map(Span::get) .collect(Collectors.joining(",")); diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderStruct.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderStruct.java index 42c8a6570..74643c005 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderStruct.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/ShaderStruct.java @@ -30,6 +30,10 @@ public class ShaderStruct extends AbstractShaderElement { this.fields2Types = createTypeLookup(); } + public ImmutableList getFields() { + return fields; + } + private ImmutableMap createTypeLookup() { ImmutableMap.Builder lookup = ImmutableMap.builder(); for (StructField field : fields) { @@ -62,4 +66,9 @@ public class ShaderStruct extends AbstractShaderElement { 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/pipeline/parse/StructField.java index 391592fc7..fcd7f6954 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/StructField.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/StructField.java @@ -27,6 +27,6 @@ public class StructField extends AbstractShaderElement { @Override public String toString() { - return "TaggedField{" + "name='" + name + '\'' + ", type='" + type + '\'' + '}'; + return type + " " + name; } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Variable.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Variable.java index b081499ee..723607005 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Variable.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/parse/Variable.java @@ -14,11 +14,16 @@ public class Variable extends AbstractShaderElement { this.name = name; } - public Span getType() { + public Span typeName() { return type; } public Span getName() { return name; } + + @Override + public String toString() { + return type + " " + name; + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/Contexts.java b/src/main/java/com/jozufozu/flywheel/core/Contexts.java index 1752bba54..8767824d9 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Contexts.java +++ b/src/main/java/com/jozufozu/flywheel/core/Contexts.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.core; +import java.util.List; + import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.SpecMetaRegistry; @@ -7,6 +9,7 @@ import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.core.crumbling.CrumblingProgram; import com.jozufozu.flywheel.core.shader.WorldFog; import com.jozufozu.flywheel.core.shader.WorldProgram; +import com.jozufozu.flywheel.core.shader.extension.IProgramExtension; import com.jozufozu.flywheel.core.shader.gamestate.FogStateProvider; import com.jozufozu.flywheel.core.shader.gamestate.NormalDebugStateProvider; import com.jozufozu.flywheel.event.GatherContextEvent; diff --git a/src/main/java/com/jozufozu/flywheel/core/WorldContext.java b/src/main/java/com/jozufozu/flywheel/core/WorldContext.java index 895208703..382efc173 100644 --- a/src/main/java/com/jozufozu/flywheel/core/WorldContext.java +++ b/src/main/java/com/jozufozu/flywheel/core/WorldContext.java @@ -3,38 +3,26 @@ package com.jozufozu.flywheel.core; import java.util.EnumMap; import java.util.Map; import java.util.function.Supplier; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Stream; -import javax.annotation.Nullable; - import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.ResourceUtil; import com.jozufozu.flywheel.backend.ShaderContext; import com.jozufozu.flywheel.backend.ShaderSources; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.backend.loading.InstancedArraysTemplate; -import com.jozufozu.flywheel.backend.loading.Program; import com.jozufozu.flywheel.backend.loading.ProgramTemplate; -import com.jozufozu.flywheel.backend.loading.Shader; import com.jozufozu.flywheel.backend.loading.ShaderLoadingException; -import com.jozufozu.flywheel.backend.loading.ShaderTransformer; import com.jozufozu.flywheel.backend.material.MaterialSpec; +import com.jozufozu.flywheel.backend.pipeline.IShaderPipeline; +import com.jozufozu.flywheel.backend.pipeline.LegacyPipeline; import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram; -import com.jozufozu.flywheel.core.shader.GameStateProgram; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; -import com.jozufozu.flywheel.core.shader.spec.ProgramState; -import com.jozufozu.flywheel.util.WorldAttached; import net.minecraft.util.ResourceLocation; public class WorldContext

extends ShaderContext

{ - - private static final String declaration = "#flwbuiltins"; - private static final Pattern builtinPattern = Pattern.compile(declaration); - protected ResourceLocation name; protected Supplier> specStream; protected TemplateFactory templateFactory; @@ -44,6 +32,8 @@ public class WorldContext

extends ShaderContext

{ private final ExtensibleGlProgram.Factory

factory; + public IShaderPipeline

pipeline; + public WorldContext(Backend backend, ExtensibleGlProgram.Factory

factory) { super(backend); this.factory = factory; @@ -79,9 +69,6 @@ public class WorldContext

extends ShaderContext

{ return this; } - protected ShaderTransformer transformer; - protected ProgramTemplate template; - @Override public void load() { @@ -97,53 +84,17 @@ public class WorldContext

extends ShaderContext

{ return; } - template = templateFactory.create(backend.sources); - transformer = new ShaderTransformer().pushStage(this::injectBuiltins) - .pushStage(Shader::processIncludes) - .pushStage(template) - .pushStage(Shader::processIncludes); + pipeline = new LegacyPipeline<>(backend.sources, templateFactory.create(backend.sources), factory, builtinSources); specStream.get() .map(backend::getSpec) .forEach(this::loadSpec); } - @Override - protected Shader getSource(ShaderType type, ResourceLocation name) { - Shader source = super.getSource(type, name); - transformer.transformSource(source); - return source; - } - - @Override - protected Program link(Program program) { - template.attachAttributes(program); - - return super.link(program); - } - - /** - * Replace #flwbuiltins with whatever expansion this context provides for the given shader. - */ - public void injectBuiltins(Shader shader) { - Matcher matcher = builtinPattern.matcher(shader.getSource()); - - if (matcher.find()) shader.setSource(matcher.replaceFirst(builtinSources.get(shader.type))); - else - throw new ShaderLoadingException(String.format("%s is missing %s, cannot use in World Context", shader.type.name, declaration)); - } - private void loadSpec(ProgramSpec spec) { try { - GameStateProgram.Builder

builder = GameStateProgram.builder(compile(spec, null)); - - for (ProgramState state : spec.states) { - - builder.withVariant(state.getContext(), compile(spec, state)); - } - - programs.put(spec.name, builder.build()); + programs.put(spec.name, pipeline.compile(spec)); Backend.log.debug("Loaded program {}", spec.name); } catch (Exception e) { @@ -152,13 +103,6 @@ public class WorldContext

extends ShaderContext

{ } } - private P compile(ProgramSpec spec, @Nullable ProgramState state) { - if (state != null) - return factory.create(loadAndLink(spec, state), state.getExtensions()); - else - return factory.create(loadAndLink(spec, null)); - } - public interface TemplateFactory { ProgramTemplate create(ShaderSources loader); } diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java index 58278ddb9..6cf73dfdc 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java @@ -4,7 +4,6 @@ import static org.lwjgl.opengl.GL20.glUniform2f; import java.util.List; -import com.jozufozu.flywheel.backend.loading.Program; import com.jozufozu.flywheel.core.atlas.AtlasInfo; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.core.shader.extension.IProgramExtension; @@ -13,13 +12,14 @@ import net.minecraft.client.renderer.model.ModelBakery; import net.minecraft.client.renderer.texture.AtlasTexture; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.inventory.container.PlayerContainer; +import net.minecraft.util.ResourceLocation; public class CrumblingProgram extends WorldProgram { protected final int uTextureScale; protected int uCrumbling; - public CrumblingProgram(Program program, List extensions) { - super(program, extensions); + public CrumblingProgram(ResourceLocation name, int handle, List extensions) { + super(name, handle, extensions); uTextureScale = getUniformLocation("uTextureScale"); } diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/ExtensibleGlProgram.java b/src/main/java/com/jozufozu/flywheel/core/shader/ExtensibleGlProgram.java index b3a93c33f..9c80ae081 100644 --- a/src/main/java/com/jozufozu/flywheel/core/shader/ExtensibleGlProgram.java +++ b/src/main/java/com/jozufozu/flywheel/core/shader/ExtensibleGlProgram.java @@ -8,10 +8,11 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.backend.loading.Program; import com.jozufozu.flywheel.core.shader.extension.IExtensionInstance; import com.jozufozu.flywheel.core.shader.extension.IProgramExtension; +import net.minecraft.util.ResourceLocation; + /** * A shader program that be arbitrarily "extended". This class can take in any number of program extensions, and * will initialize them and then call their {@link IExtensionInstance#bind() bind} function every subsequent time this @@ -25,8 +26,8 @@ public class ExtensibleGlProgram extends GlProgram { protected final List extensions; - public ExtensibleGlProgram(Program program, @Nullable List extensions) { - super(program); + public ExtensibleGlProgram(ResourceLocation name, int handle, @Nullable List extensions) { + super(name, handle); if (extensions != null) { List list = new ArrayList<>(); @@ -71,10 +72,6 @@ public class ExtensibleGlProgram extends GlProgram { public interface Factory

{ @Nonnull - P create(Program program, @Nullable List extensions); - - default P create(Program program) { - return create(program, null); - } + P create(ResourceLocation name, int handle, @Nullable List extensions); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/WorldProgram.java b/src/main/java/com/jozufozu/flywheel/core/shader/WorldProgram.java index 97981d23a..37268ded0 100644 --- a/src/main/java/com/jozufozu/flywheel/core/shader/WorldProgram.java +++ b/src/main/java/com/jozufozu/flywheel/core/shader/WorldProgram.java @@ -6,12 +6,12 @@ import static org.lwjgl.opengl.GL20.glUniform3f; import java.util.List; -import com.jozufozu.flywheel.backend.loading.Program; import com.jozufozu.flywheel.core.shader.extension.IProgramExtension; import com.jozufozu.flywheel.util.AnimationTickHolder; import net.minecraft.client.MainWindow; import net.minecraft.client.Minecraft; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.vector.Matrix4f; public class WorldProgram extends ExtensibleGlProgram { @@ -23,8 +23,8 @@ public class WorldProgram extends ExtensibleGlProgram { protected int uBlockAtlas; protected int uLightMap; - public WorldProgram(Program program, List extensions) { - super(program, extensions); + public WorldProgram(ResourceLocation name, int handle, List extensions) { + super(name, handle, extensions); super.bind(); registerSamplers(); diff --git a/src/main/java/com/jozufozu/flywheel/util/StreamUtil.java b/src/main/java/com/jozufozu/flywheel/util/StreamUtil.java new file mode 100644 index 000000000..c2dcd844a --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/StreamUtil.java @@ -0,0 +1,60 @@ +package com.jozufozu.flywheel.util; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; + +import org.lwjgl.system.MemoryUtil; + +import com.mojang.blaze3d.systems.RenderSystem; + +public class StreamUtil { + public static String readToString(InputStream is) { + RenderSystem.assertThread(RenderSystem::isOnRenderThread); + ByteBuffer bytebuffer = null; + + try { + bytebuffer = readToBuffer(is); + int i = bytebuffer.position(); + ((Buffer) bytebuffer).rewind(); + return MemoryUtil.memASCII(bytebuffer, i); + } catch (IOException e) { + + } finally { + if (bytebuffer != null) { + MemoryUtil.memFree(bytebuffer); + } + + } + + return null; + } + + public static ByteBuffer readToBuffer(InputStream is) throws IOException { + ByteBuffer bytebuffer; + if (is instanceof FileInputStream) { + FileInputStream fileinputstream = (FileInputStream) is; + FileChannel filechannel = fileinputstream.getChannel(); + bytebuffer = MemoryUtil.memAlloc((int) filechannel.size() + 1); + + while (filechannel.read(bytebuffer) != -1) { + } + } else { + bytebuffer = MemoryUtil.memAlloc(8192); + ReadableByteChannel readablebytechannel = Channels.newChannel(is); + + while (readablebytechannel.read(bytebuffer) != -1) { + if (bytebuffer.remaining() == 0) { + bytebuffer = MemoryUtil.memRealloc(bytebuffer, bytebuffer.capacity() * 2); + } + } + } + + return bytebuffer; + } +}