From 7a080a55387ad965ee01ab87b866dba1745809e5 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 11 Oct 2022 20:16:15 -0700 Subject: [PATCH] Fix* the errors - *make the errors understandable without actually solving them - Straighten out shader compilation/error generation - Interpret errors in generated code - Compilation returns a result type instead of throwing an exception - Still need to reimplement source checks? - Make error messages prettier --- .../java/com/jozufozu/flywheel/Flywheel.java | 2 - .../com/jozufozu/flywheel/backend/Loader.java | 4 + .../flywheel/backend/gl/shader/GlShader.java | 50 +----- .../instancing/compile/Compilation.java | 149 +++++++++++++++ .../instancing/compile/CompilationResult.java | 29 +++ .../instancing/compile/FailedCompilation.java | 81 +++++++++ .../instancing/compile/FlwCompiler.java | 170 +++++++----------- .../compile/ShaderCompilationException.java | 32 ---- .../instancing/compile/ShaderCompiler.java | 99 ++++++++++ .../core/source/CompilationContext.java | 95 ---------- .../flywheel/core/source/SourceFile.java | 8 +- .../flywheel/core/source/SourceLines.java | 37 +++- .../core/source/error/ErrorBuilder.java | 125 +++++++------ .../core/source/error/ErrorLevel.java | 9 +- .../core/source/error/ErrorReporter.java | 18 +- .../core/source/error/lines/Divider.java | 2 +- .../core/source/error/lines/FileLine.java | 2 +- .../core/source/error/lines/HeaderLine.java | 2 +- .../core/source/generate/GlslBuilder.java | 27 ++- .../core/source/generate/GlslStmt.java | 2 +- .../jozufozu/flywheel/util/ConsoleColors.java | 77 ++++++++ .../com/jozufozu/flywheel/util/FlwUtil.java | 9 - .../jozufozu/flywheel/util/StringUtil.java | 13 ++ 23 files changed, 647 insertions(+), 395 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/compile/Compilation.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CompilationResult.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FailedCompilation.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/compile/ShaderCompilationException.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/compile/ShaderCompiler.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/source/CompilationContext.java create mode 100644 src/main/java/com/jozufozu/flywheel/util/ConsoleColors.java diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java index ae55cd5de..5c0e809c7 100644 --- a/src/main/java/com/jozufozu/flywheel/Flywheel.java +++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java @@ -8,7 +8,6 @@ import com.jozufozu.flywheel.backend.RenderWork; import com.jozufozu.flywheel.backend.ShadersModHandler; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer; -import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler; import com.jozufozu.flywheel.config.BackendTypeArgument; import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwConfig; @@ -80,7 +79,6 @@ public class Flywheel { forgeEventBus.addListener(FlwCommands::registerClientCommands); forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onReloadRenderers); - forgeEventBus.addListener(FlwCompiler::onReloadRenderers); forgeEventBus.addListener(Models::onReloadRenderers); forgeEventBus.addListener(DrawBuffer::onReloadRenderers); diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index 5287e135c..07c940c10 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -39,6 +39,10 @@ public class Loader implements ResourceManagerReloadListener { var errorReporter = new ErrorReporter(); ShaderSources sources = new ShaderSources(errorReporter, manager); + if (FlwCompiler.INSTANCE != null) { + FlwCompiler.INSTANCE.delete(); + } + FlwCompiler.INSTANCE = new FlwCompiler(sources); ClientLevel level = Minecraft.getInstance().level; 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 0ffaa3485..a637b8298 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java @@ -1,38 +1,17 @@ package com.jozufozu.flywheel.backend.gl.shader; -import java.io.File; -import java.io.FileWriter; -import java.util.List; -import java.util.stream.Collectors; - import org.lwjgl.opengl.GL20; -import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlObject; -import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; -import com.jozufozu.flywheel.backend.instancing.compile.ShaderCompilationException; - -import net.minecraft.client.Minecraft; -import net.minecraft.resources.ResourceLocation; public class GlShader extends GlObject { public final ShaderType type; - private final List parts; + private final String name; - public GlShader(String source, ShaderType type, List parts) throws ShaderCompilationException { - this.parts = parts; + public GlShader(int handle, ShaderType type, String name) { this.type = type; - int handle = GL20.glCreateShader(type.glEnum); - - GlCompat.safeShaderSource(handle, source); - GL20.glCompileShader(handle); - - dumpSource(source, type); - - if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) { - throw new ShaderCompilationException("Could not compile " + getName(), handle); - } + this.name = name; setHandle(handle); } @@ -42,26 +21,9 @@ public class GlShader extends GlObject { GL20.glDeleteShader(handle); } - public String getName() { - return parts.stream() - .map(ResourceLocation::toString) - .map(s -> s.replaceAll("/", "_") - .replaceAll(":", "\\$")) - .collect(Collectors.joining(";")); + @Override + public String toString() { + return "GlShader{" + type.name + handle() + " " + name + "}"; } - private void dumpSource(String source, ShaderType type) { - if (!Backend.DUMP_SHADER_SOURCE) { - return; - } - - File dir = new File(Minecraft.getInstance().gameDirectory, "flywheel_sources"); - dir.mkdirs(); - File file = new File(dir, type.getFileName(getName())); - try (FileWriter writer = new FileWriter(file)) { - writer.write(source); - } catch (Exception e) { - e.printStackTrace(); - } - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/Compilation.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/Compilation.java new file mode 100644 index 000000000..edb474865 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/Compilation.java @@ -0,0 +1,149 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.io.File; +import java.io.FileWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.NotNull; +import org.lwjgl.opengl.GL20; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; +import com.jozufozu.flywheel.core.SourceComponent; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.util.StringUtil; + +import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceLocation; + +/** + * Builder style class for compiling shaders. + *

+ * Keeps track of the source files and components used to compile a shader, + * and interprets/pretty prints any errors that occur. + */ +public class Compilation { + private final List files = new ArrayList<>(); + private final List componentNames = new ArrayList<>(); + private final StringBuilder generatedSource; + private final StringBuilder fullSource; + private final GLSLVersion glslVersion; + private final ShaderType shaderType; + private int generatedLines = 0; + + public Compilation(GLSLVersion glslVersion, ShaderType shaderType) { + this.generatedSource = new StringBuilder(); + this.fullSource = new StringBuilder(CompileUtil.generateHeader(glslVersion, shaderType)); + this.glslVersion = glslVersion; + this.shaderType = shaderType; + } + + @NotNull + public CompilationResult compile() { + int handle = GL20.glCreateShader(shaderType.glEnum); + var source = fullSource.toString(); + + GlCompat.safeShaderSource(handle, source); + GL20.glCompileShader(handle); + + var shaderName = buildShaderName(); + dumpSource(source, shaderType.getFileName(shaderName)); + + if (compiledSuccessfully(handle)) { + return CompilationResult.success(new GlShader(handle, shaderType, shaderName)); + } + + var errorLog = GL20.glGetShaderInfoLog(handle); + GL20.glDeleteShader(handle); + return CompilationResult.failure(new FailedCompilation(shaderName, files, generatedSource.toString(), errorLog)); + } + + public void enableExtension(String ext) { + fullSource.append("#extension ") + .append(ext) + .append(" : enable\n"); + } + + public void addComponentName(ResourceLocation name) { + componentNames.add(name); + } + + public void appendComponent(SourceComponent component) { + var source = component.source(); + + if (component instanceof SourceFile file) { + fullSource.append(sourceHeader(file)); + } else { + fullSource.append(generatedHeader(source, component.name() + .toString())); + } + + fullSource.append(source); + } + + private String sourceHeader(SourceFile sourceFile) { + return "#line " + 0 + ' ' + getOrCreateFileID(sourceFile) + " // " + sourceFile.name + '\n'; + } + + private String generatedHeader(String generatedCode, String comment) { + generatedSource.append(generatedCode); + int lines = StringUtil.countLines(generatedCode); + + // all generated code is put in file 0, + var out = "#line " + generatedLines + ' ' + 0; + + generatedLines += lines; + + return out + " // (generated) " + comment + '\n'; + } + + /** + * Returns an arbitrary file ID for use this compilation context, or generates one if missing. + * + * @param sourceFile The file to retrieve the ID for. + * @return A file ID unique to the given sourceFile. + */ + private int getOrCreateFileID(SourceFile sourceFile) { + int i = files.indexOf(sourceFile); + if (i != -1) { + return i + 1; + } + + files.add(sourceFile); + return files.size(); + } + + @NotNull + private String buildShaderName() { + var components = componentNames.stream() + .map(ResourceLocation::toString) + .map(s -> s.replaceAll("/", "_") + .replaceAll(":", "\\$")) + .collect(Collectors.joining(";")); + return shaderType.name + glslVersion + ';' + components; + } + + private static void dumpSource(String source, String fileName) { + if (!Backend.DUMP_SHADER_SOURCE) { + return; + } + + File dir = new File(Minecraft.getInstance().gameDirectory, "flywheel_sources"); + dir.mkdirs(); + File file = new File(dir, fileName); + try (FileWriter writer = new FileWriter(file)) { + writer.write(source); + } catch (Exception e) { + Backend.LOGGER.error("Could not dump source.", e); + } + } + + private static boolean compiledSuccessfully(int handle) { + return GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) == GL20.GL_TRUE; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CompilationResult.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CompilationResult.java new file mode 100644 index 000000000..a5d352f73 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/CompilationResult.java @@ -0,0 +1,29 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import org.jetbrains.annotations.Nullable; + +import com.jozufozu.flywheel.backend.gl.shader.GlShader; + +public sealed interface CompilationResult { + @Nullable + default GlShader unwrap() { + if (this instanceof Success s) { + return s.shader(); + } + return null; + } + + record Success(GlShader shader) implements CompilationResult { + } + + record Failure(FailedCompilation failure) implements CompilationResult { + } + + static CompilationResult success(GlShader program) { + return new Success(program); + } + + static CompilationResult failure(FailedCompilation failure) { + return new Failure(failure); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FailedCompilation.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FailedCompilation.java new file mode 100644 index 000000000..8ef84b185 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FailedCompilation.java @@ -0,0 +1,81 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.jetbrains.annotations.NotNull; + +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.core.source.SourceLines; +import com.jozufozu.flywheel.core.source.error.ErrorBuilder; +import com.jozufozu.flywheel.core.source.span.Span; +import com.jozufozu.flywheel.util.ConsoleColors; + +public class FailedCompilation { + private static final Pattern ERROR_LINE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)"); + private final List files; + private final SourceLines generatedSource; + private final String errorLog; + private final String shaderName; + + public FailedCompilation(String shaderName, List files, String generatedSource, String errorLog) { + this.shaderName = shaderName; + this.files = files; + this.generatedSource = new SourceLines(generatedSource); + this.errorLog = errorLog; + } + + public String getMessage() { + return ConsoleColors.RED_BOLD_BRIGHT + "Failed to compile " + shaderName + ":\n" + errorString(); + } + + public String errorString() { + return errorStream().map(ErrorBuilder::build) + .collect(Collectors.joining("\n")); + } + + @NotNull + private Stream errorStream() { + return errorLog.lines() + .map(this::interpretErrorLine); + } + + private ErrorBuilder interpretErrorLine(String s) { + Matcher matcher = ERROR_LINE.matcher(s); + + if (matcher.find()) { + int fileId = Integer.parseInt(matcher.group(1)); + int lineNo = Integer.parseInt(matcher.group(2)); + var msg = matcher.group(3); + + if (fileId == 0) { + return interpretGeneratedError(lineNo, msg); + } else { + return interpretSourceError(fileId, lineNo, msg); + } + } + return ErrorBuilder.create() + .error(s); + } + + private ErrorBuilder interpretSourceError(int fileId, int lineNo, String msg) { + var sourceFile = files.get(fileId - 1); + Span span = sourceFile.getLineSpanNoWhitespace(lineNo); + + return ErrorBuilder.create() + .error(msg) + .pointAtFile(sourceFile) + .pointAt(span, 1); + } + + private ErrorBuilder interpretGeneratedError(int lineNo, String msg) { + return ErrorBuilder.create() + .error(msg) + .pointAtFile("[in generated source]") + .pointAtLine(generatedSource, lineNo, 1) + .note("This generally indicates a bug in Flywheel, not your shader code."); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FlwCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FlwCompiler.java index 1b98f52b4..ab4370f58 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FlwCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/FlwCompiler.java @@ -2,12 +2,10 @@ package com.jozufozu.flywheel.backend.instancing.compile; import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; - -import org.jetbrains.annotations.NotNull; +import java.util.stream.Collectors; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; @@ -20,7 +18,6 @@ import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GLSLVersion; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.backend.instancing.indirect.IndirectComponent; import com.jozufozu.flywheel.core.BackendTypes; @@ -29,85 +26,73 @@ import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.Pipelines; import com.jozufozu.flywheel.core.SourceComponent; import com.jozufozu.flywheel.core.pipeline.SimplePipeline; -import com.jozufozu.flywheel.core.source.CompilationContext; import com.jozufozu.flywheel.core.source.ShaderLoadingException; import com.jozufozu.flywheel.core.source.ShaderSources; -import com.jozufozu.flywheel.core.source.SourceFile; -import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.util.StringUtil; -import net.minecraft.resources.ResourceLocation; - public class FlwCompiler { public static FlwCompiler INSTANCE; - public static void onReloadRenderers(ReloadRenderersEvent t) { - - } - - final Map pipelinePrograms = new HashMap<>(); - final Map, GlProgram> cullingPrograms = new HashMap<>(); - - boolean needsCrash = false; - final long compileStart = System.nanoTime(); - final Multimap, PipelineContext> uniformProviderGroups = ArrayListMultimap.create(); - final List pipelineContexts = new ArrayList<>(); - private final ShaderSources sources; private final VertexMaterialComponent vertexMaterialComponent; private final FragmentMaterialComponent fragmentMaterialComponent; + private final List pipelineContexts; + + + final ShaderCompiler shaderCompiler; + final Multimap, PipelineContext> uniformProviderGroups = ArrayListMultimap.create(); + final Map pipelinePrograms = new HashMap<>(); + final Map, GlProgram> cullingPrograms = new HashMap<>(); + final List errors = new ArrayList<>(); public FlwCompiler(ShaderSources sources) { + this.shaderCompiler = new ShaderCompiler(errors::add); this.sources = sources; this.vertexMaterialComponent = new VertexMaterialComponent(sources, ComponentRegistry.materials.vertexSources()); this.fragmentMaterialComponent = new FragmentMaterialComponent(sources, ComponentRegistry.materials.fragmentSources()); - for (SimplePipeline pipelineShader : BackendTypes.availablePipelineShaders()) { - for (StructType structType : ComponentRegistry.structTypes) { - for (VertexType vertexType : ComponentRegistry.vertexTypes) { - // TODO: context ubershaders, or not? - pipelineContexts.add(new PipelineContext(vertexType, structType, Components.WORLD, pipelineShader)); - } - } - } + this.pipelineContexts = buildPipelineSet(); // TODO: analyze uniform providers and group them into sets; break up this ctor for (PipelineContext context : pipelineContexts) { - try { - var glProgram = compilePipelineContext(context); - pipelinePrograms.put(context, glProgram); - } catch (ShaderCompilationException e) { - needsCrash = true; - Backend.LOGGER.error(e.errors); - } + compilePipelineContext(context); } for (StructType type : ComponentRegistry.structTypes) { - try { - var glProgram = compileComputeCuller(type); - cullingPrograms.put(type, glProgram); - } catch (ShaderCompilationException e) { - needsCrash = true; - Backend.LOGGER.error(e.errors); - } + compileComputeCuller(type); } finish(); } - public void finish() { + private void finish() { long compileEnd = System.nanoTime(); + int programCount = pipelineContexts.size() + ComponentRegistry.structTypes.size(); + int shaderCount = shaderCompiler.shaderCount(); + int errorCount = errors.size(); + var elapsed = StringUtil.formatTime(compileEnd - compileStart); - Backend.LOGGER.info("Compiled " + pipelineContexts.size() + " programs in " + StringUtil.formatTime(compileEnd - compileStart)); + Backend.LOGGER.info("Compiled " + programCount + " programs and " + shaderCount + " shaders in " + elapsed + " with " + errorCount + " errors."); - if (needsCrash) { - throw new ShaderLoadingException("Compilation failed"); + if (errorCount > 0) { + var details = errors.stream() + .map(FailedCompilation::getMessage) + .collect(Collectors.joining("\n")); + throw new ShaderLoadingException("Compilation failed.\n" + details); } } + public void delete() { + pipelinePrograms.values() + .forEach(GlProgram::delete); + cullingPrograms.values() + .forEach(GlProgram::delete); + shaderCompiler.delete(); + } + public GlProgram getPipelineProgram(VertexType vertexType, StructType structType, ContextShader contextShader, SimplePipeline pipelineShader) { return pipelinePrograms.get(new PipelineContext(vertexType, structType, contextShader, pipelineShader)); } @@ -116,28 +101,36 @@ public class FlwCompiler { return cullingPrograms.get(structType); } - protected GlProgram compilePipelineContext(PipelineContext ctx) throws ShaderCompilationException { - + private void compilePipelineContext(PipelineContext ctx) { var glslVersion = ctx.pipelineShader() .glslVersion(); - var vertex = compileShader(glslVersion, ShaderType.VERTEX, getVertexComponents(ctx)); - var fragment = compileShader(glslVersion, ShaderType.FRAGMENT, getFragmentComponents(ctx)); + var vertex = shaderCompiler.compile(glslVersion, ShaderType.VERTEX, getVertexComponents(ctx)); + var fragment = shaderCompiler.compile(glslVersion, ShaderType.FRAGMENT, getFragmentComponents(ctx)); - return ctx.contextShader() + if (vertex == null || fragment == null) { + return; + } + + pipelinePrograms.put(ctx, ctx.contextShader() .factory() .create(new ProgramAssembler().attachShader(vertex) .attachShader(fragment) - .link()); + .link())); } - protected GlProgram compileComputeCuller(StructType structType) { + private void compileComputeCuller(StructType structType) { + var result = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, getComputeComponents(structType)); - return new GlProgram(new ProgramAssembler().attachShader(compileShader(GLSLVersion.V460, ShaderType.COMPUTE, getComputeComponents(structType))) - .link()); + if (result == null) { + return; + } + + cullingPrograms.put(structType, new GlProgram(new ProgramAssembler().attachShader(result) + .link())); } - ImmutableList getVertexComponents(PipelineContext ctx) { + private ImmutableList getVertexComponents(PipelineContext ctx) { var instanceAssembly = ctx.pipelineShader() .assemble(new Pipeline.InstanceAssemblerContext(sources, ctx.vertexType(), ctx.structType())); @@ -157,7 +150,7 @@ public class FlwCompiler { return ImmutableList.of(vertexMaterialComponent, instanceAssembly, layout, instance, context, pipeline); } - ImmutableList getFragmentComponents(PipelineContext ctx) { + private ImmutableList getFragmentComponents(PipelineContext ctx) { var context = sources.find(ctx.contextShader() .fragmentShader() .resourceLocation()); @@ -167,7 +160,7 @@ public class FlwCompiler { return ImmutableList.of(fragmentMaterialComponent, context, pipeline); } - @NotNull ImmutableList getComputeComponents(StructType structType) { + private ImmutableList getComputeComponents(StructType structType) { var instanceAssembly = new IndirectComponent(sources, structType); var instance = sources.find(structType.getInstanceShader() .resourceLocation()); @@ -176,55 +169,16 @@ public class FlwCompiler { return ImmutableList.of(instanceAssembly, instance, pipeline); } - protected GlShader compileShader(GLSLVersion glslVersion, ShaderType shaderType, ImmutableList sourceComponents) { - StringBuilder finalSource = new StringBuilder(CompileUtil.generateHeader(glslVersion, shaderType)); - finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n"); - finalSource.append("#extension GL_ARB_conservative_depth : enable\n"); - - var ctx = new CompilationContext(); - var names = ImmutableList.builder(); - - for (var include : depthFirstInclude(sourceComponents)) { - appendFinalSource(finalSource, ctx, include); + private static List buildPipelineSet() { + ImmutableList.Builder builder = ImmutableList.builder(); + for (SimplePipeline pipelineShader : BackendTypes.availablePipelineShaders()) { + for (StructType structType : ComponentRegistry.structTypes) { + for (VertexType vertexType : ComponentRegistry.vertexTypes) { + // TODO: context ubershaders, or not? + builder.add(new PipelineContext(vertexType, structType, Components.WORLD, pipelineShader)); + } + } } - - for (var component : sourceComponents) { - appendFinalSource(finalSource, ctx, component); - names.add(component.name()); - } - - try { - return new GlShader(finalSource.toString(), shaderType, names.build()); - } catch (ShaderCompilationException e) { - throw e.withErrorLog(ctx); - } - } - - private static void appendFinalSource(StringBuilder finalSource, CompilationContext ctx, SourceComponent component) { - var source = component.source(); - - if (component instanceof SourceFile file) { - finalSource.append(ctx.sourceHeader(file)); - } else { - finalSource.append(ctx.generatedHeader(source, component.name() - .toString())); - } - - finalSource.append(source); - } - - protected static Set depthFirstInclude(ImmutableList root) { - var included = new LinkedHashSet(); // linked to preserve order - for (var component : root) { - recursiveDepthFirstInclude(included, component); - } - return included; - } - - protected static void recursiveDepthFirstInclude(Set included, SourceComponent component) { - for (var include : component.included()) { - recursiveDepthFirstInclude(included, include); - } - included.addAll(component.included()); + return builder.build(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/ShaderCompilationException.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/ShaderCompilationException.java deleted file mode 100644 index e66a6a2a2..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/ShaderCompilationException.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.compile; - -import org.lwjgl.opengl.GL20; - -import com.jozufozu.flywheel.core.source.CompilationContext; -import com.jozufozu.flywheel.core.source.ShaderLoadingException; - -public class ShaderCompilationException extends ShaderLoadingException { - - private final int shaderHandle; - - public String errors = ""; - - public ShaderCompilationException(String message, int shaderHandle) { - super(message); - this.shaderHandle = shaderHandle; - } - - public ShaderLoadingException withErrorLog(CompilationContext ctx) { - if (this.shaderHandle == -1) - return this; - - this.errors = ctx.parseErrors(GL20.glGetShaderInfoLog(this.shaderHandle)); - - return this; - } - - @Override - public String getMessage() { - return super.getMessage() + '\n' + this.errors; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/ShaderCompiler.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/ShaderCompiler.java new file mode 100644 index 000000000..a2a6e014a --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/compile/ShaderCompiler.java @@ -0,0 +1,99 @@ +package com.jozufozu.flywheel.backend.instancing.compile; + +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.SourceComponent; + +public class ShaderCompiler { + private final Map shaderCache = new HashMap<>(); + private final Consumer errorConsumer; + + public ShaderCompiler(Consumer errorConsumer) { + this.errorConsumer = errorConsumer; + } + + public int shaderCount() { + return shaderCache.size(); + } + + @Nullable + public GlShader compile(GLSLVersion glslVersion, ShaderType shaderType, ImmutableList sourceComponents) { + var key = new ShaderKey(glslVersion, shaderType, sourceComponents); + var cached = shaderCache.get(key); + if (cached != null) { + return cached.unwrap(); + } + + CompilationResult out = compileUncached(glslVersion, shaderType, sourceComponents); + shaderCache.put(key, out); + return unwrapAndReportError(out); + } + + public void delete() { + shaderCache.values() + .stream() + .map(CompilationResult::unwrap) + .filter(Objects::nonNull) + .forEach(GlShader::delete); + } + + @Nullable + private GlShader unwrapAndReportError(CompilationResult result) { + if (result instanceof CompilationResult.Success s) { + return s.shader(); + } else if (result instanceof CompilationResult.Failure f) { + errorConsumer.accept(f.failure()); + } + return null; + } + + @NotNull + private static CompilationResult compileUncached(GLSLVersion glslVersion, ShaderType shaderType, ImmutableList sourceComponents) { + var ctx = new Compilation(glslVersion, shaderType); + ctx.enableExtension("GL_ARB_explicit_attrib_location"); + ctx.enableExtension("GL_ARB_conservative_depth"); + + for (var include : depthFirstInclude(sourceComponents)) { + ctx.appendComponent(include); + } + + for (var component : sourceComponents) { + ctx.appendComponent(component); + ctx.addComponentName(component.name()); + } + + return ctx.compile(); + } + + private static Set depthFirstInclude(ImmutableList root) { + var included = new LinkedHashSet(); // linked to preserve order + for (var component : root) { + recursiveDepthFirstInclude(included, component); + } + return included; + } + + private static void recursiveDepthFirstInclude(Set included, SourceComponent component) { + for (var include : component.included()) { + recursiveDepthFirstInclude(included, include); + } + included.addAll(component.included()); + } + + private record ShaderKey(GLSLVersion glslVersion, ShaderType shaderType, + ImmutableList sourceComponents) { + + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/CompilationContext.java b/src/main/java/com/jozufozu/flywheel/core/source/CompilationContext.java deleted file mode 100644 index 00af5da27..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/source/CompilationContext.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.jozufozu.flywheel.core.source; - -import java.util.ArrayList; -import java.util.List; - -import org.jetbrains.annotations.Nullable; - -import com.jozufozu.flywheel.core.source.error.ErrorBuilder; -import com.jozufozu.flywheel.core.source.span.Span; - -public class CompilationContext { - public final List files = new ArrayList<>(); - - private String generatedSource = ""; - private int generatedLines = 0; - - - public String sourceHeader(SourceFile sourceFile) { - return "#line " + 0 + ' ' + getOrCreateFileID(sourceFile) + " // " + sourceFile.name + '\n'; - } - - public String generatedHeader(String generatedCode, @Nullable String comment) { - generatedSource += generatedCode; - int lines = generatedCode.split("\n").length; - - var out = "#line " + generatedLines + ' ' + 0; - - generatedLines += lines; - - if (comment == null) { - comment = ""; - } - - return out + " // (generated) " + comment + '\n'; - } - - public boolean contains(SourceFile sourceFile) { - return files.contains(sourceFile); - } - - /** - * Returns an arbitrary file ID for use this compilation context, or generates one if missing. - * - * @param sourceFile The file to retrieve the ID for. - * @return A file ID unique to the given sourceFile. - */ - private int getOrCreateFileID(SourceFile sourceFile) { - int i = files.indexOf(sourceFile); - if (i != -1) { - return i + 1; - } - - files.add(sourceFile); - return files.size(); - } - - public Span getLineSpan(int fileId, int lineNo) { - if (fileId == 0) { - // TODO: Valid spans for generated code. - return null; - } - return getFile(fileId).getLineSpanNoWhitespace(lineNo); - } - - private SourceFile getFile(int fileId) { - return files.get(fileId - 1); - } - - public String parseErrors(String log) { - List lines = log.lines() - .toList(); - - StringBuilder errors = new StringBuilder(); - for (String line : lines) { - ErrorBuilder builder = parseCompilerError(line); - - if (builder != null) { - errors.append(builder.build()); - } else { - errors.append(line).append('\n'); - } - } - return errors.toString(); - } - - @Nullable - private ErrorBuilder parseCompilerError(String line) { - try { - return ErrorBuilder.fromLogLine(this, line); - } catch (Exception ignored) { - } - - return null; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java index fea1383af..9e72e0776 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java @@ -98,15 +98,15 @@ public class SourceFile implements SourceComponent { } public Span getLineSpan(int lineNo) { - int begin = source.getLineStart(lineNo); - int end = begin + source.getLine(lineNo) + int begin = source.lineStartIndex(lineNo); + int end = begin + source.lineString(lineNo) .length(); return new StringSpan(this, source.getCharPos(begin), source.getCharPos(end)); } public Span getLineSpanNoWhitespace(int line) { - int begin = source.getLineStart(line); - int end = begin + source.getLine(line) + int begin = source.lineStartIndex(line); + int end = begin + source.lineString(line) .length(); while (begin < end && Character.isWhitespace(source.charAt(begin))) { diff --git a/src/main/java/com/jozufozu/flywheel/core/source/SourceLines.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceLines.java index 75dfd151c..e4f1eada8 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/SourceLines.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceLines.java @@ -11,6 +11,7 @@ import com.jozufozu.flywheel.util.StringUtil; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntLists; public class SourceLines implements CharSequence { @@ -33,15 +34,15 @@ public class SourceLines implements CharSequence { this.lines = getLines(raw, lineStarts); } - public int getLineCount() { + public int count() { return lines.size(); } - public String getLine(int lineNo) { + public String lineString(int lineNo) { return lines.get(lineNo); } - public int getLineStart(int lineNo) { + public int lineStartIndex(int lineNo) { return lineStarts.getInt(lineNo); } @@ -76,9 +77,12 @@ public class SourceLines implements CharSequence { /** * Scan the source for line breaks, recording the position of the first character of each line. - * @param source */ private static IntList createLineLookup(String source) { + if (source.isEmpty()) { + return IntLists.emptyList(); + } + IntList l = new IntArrayList(); l.add(0); // first line is always at position 0 @@ -87,6 +91,7 @@ public class SourceLines implements CharSequence { while (matcher.find()) { l.add(matcher.end()); } + return l; } @@ -121,4 +126,28 @@ public class SourceLines implements CharSequence { public int length() { return raw.length(); } + + public int lineStartCol(int spanLine) { + return 0; + } + + public int lineWidth(int spanLine) { + return lines.get(spanLine) + .length(); + } + + public int lineStartColTrimmed(final int line) { + final var lineString = lineString(line); + final int end = lineString.length(); + + int col = 0; + while (col < end && Character.isWhitespace(charAt(col))) { + col++; + } + return col; + } + + public int lineStartPosTrimmed(final int line) { + return lineStartIndex(line) + lineStartColTrimmed(line); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorBuilder.java b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorBuilder.java index 925b9dbb9..a234d9433 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorBuilder.java @@ -2,12 +2,9 @@ package com.jozufozu.flywheel.core.source.error; import java.util.ArrayList; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.jetbrains.annotations.Nullable; -import com.jozufozu.flywheel.core.source.CompilationContext; import com.jozufozu.flywheel.core.source.SourceFile; import com.jozufozu.flywheel.core.source.SourceLines; import com.jozufozu.flywheel.core.source.error.lines.ErrorLine; @@ -17,72 +14,61 @@ import com.jozufozu.flywheel.core.source.error.lines.SourceLine; import com.jozufozu.flywheel.core.source.error.lines.SpanHighlightLine; import com.jozufozu.flywheel.core.source.error.lines.TextLine; import com.jozufozu.flywheel.core.source.span.Span; -import com.jozufozu.flywheel.util.FlwUtil; +import com.jozufozu.flywheel.util.ConsoleColors; +import com.jozufozu.flywheel.util.StringUtil; public class ErrorBuilder { - private static final Pattern ERROR_LINE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)"); - private final List lines = new ArrayList<>(); - public ErrorBuilder() { + private ErrorBuilder() { } - public static ErrorBuilder error(CharSequence msg) { - return new ErrorBuilder() - .header(ErrorLevel.ERROR, msg); + public static ErrorBuilder create() { + return new ErrorBuilder(); } - public static ErrorBuilder compError(CharSequence msg) { - return new ErrorBuilder() - .extra(msg); + public ErrorBuilder error(String msg) { + return header(ErrorLevel.ERROR, msg); } - public static ErrorBuilder warn(CharSequence msg) { - return new ErrorBuilder() - .header(ErrorLevel.WARN, msg); + public ErrorBuilder warn(String msg) { + return header(ErrorLevel.WARN, msg); } - @Nullable - public static ErrorBuilder fromLogLine(CompilationContext env, String s) { - Matcher matcher = ERROR_LINE.matcher(s); - - if (matcher.find()) { - String fileId = matcher.group(1); - String lineNo = matcher.group(2); - String msg = matcher.group(3); - Span span = env.getLineSpan(Integer.parseInt(fileId), Integer.parseInt(lineNo)); - - if (span == null) { - return ErrorBuilder.compError("Error in generated code"); - } - - return ErrorBuilder.compError(msg) - .pointAtFile(span.getSourceFile()) - .pointAt(span, 1); - } else { - return null; - } + public ErrorBuilder hint(String msg) { + return header(ErrorLevel.HINT, msg); } - public ErrorBuilder header(ErrorLevel level, CharSequence msg) { + public ErrorBuilder note(String msg) { + return header(ErrorLevel.NOTE, msg); + } + + public ErrorBuilder header(ErrorLevel level, String msg) { lines.add(new HeaderLine(level.toString(), msg)); return this; } - public ErrorBuilder extra(CharSequence msg) { - lines.add(new TextLine(msg.toString())); + public ErrorBuilder extra(String msg) { + lines.add(new TextLine(msg)); return this; } public ErrorBuilder pointAtFile(SourceFile file) { - lines.add(new FileLine(file.name.toString())); + return pointAtFile(file.name.toString()); + } + + public ErrorBuilder pointAtFile(String file) { + lines.add(new FileLine(file)); return this; } - public ErrorBuilder hintIncludeFor(@Nullable Span span, CharSequence msg) { - if (span == null) return this; + public ErrorBuilder hintIncludeFor(@Nullable Span span, String msg) { + if (span == null) { + return this; + } + SourceFile sourceFile = span.getSourceFile(); String builder = "add " + sourceFile.importStatement() + ' ' + msg + "\n defined here:"; @@ -98,28 +84,37 @@ public class ErrorBuilder { } public ErrorBuilder pointAt(Span span, int ctxLines) { - if (span.lines() == 1) { SourceLines lines = span.getSourceFile().source; int spanLine = span.firstLine(); + int firstCol = span.getStart() + .col(); + int lastCol = span.getEnd() + .col(); - int firstLine = Math.max(0, spanLine - ctxLines); - int lastLine = Math.min(lines.getLineCount(), spanLine + ctxLines); + pointAtLine(lines, spanLine, ctxLines, firstCol, lastCol); + } - int firstCol = span.getStart() - .col(); - int lastCol = span.getEnd() - .col(); + return this; + } - for (int i = firstLine; i <= lastLine; i++) { - CharSequence line = lines.getLine(i); + public ErrorBuilder pointAtLine(SourceLines lines, int spanLine, int ctxLines) { + return pointAtLine(lines, spanLine, ctxLines, lines.lineStartColTrimmed(spanLine), lines.lineWidth(spanLine)); + } - this.lines.add(SourceLine.numbered(i + 1, line.toString())); + public ErrorBuilder pointAtLine(SourceLines lines, int spanLine, int ctxLines, int firstCol, int lastCol) { + int firstLine = Math.max(0, spanLine - ctxLines); + int lastLine = Math.min(lines.count(), spanLine + ctxLines); - if (i == spanLine) { - this.lines.add(new SpanHighlightLine(firstCol, lastCol)); - } + + for (int i = firstLine; i <= lastLine; i++) { + CharSequence line = lines.lineString(i); + + this.lines.add(SourceLine.numbered(i + 1, line.toString())); + + if (i == spanLine) { + this.lines.add(new SpanHighlightLine(firstCol, lastCol)); } } @@ -127,23 +122,25 @@ public class ErrorBuilder { } public String build() { - - int maxLength = -1; + int maxMargin = -1; for (ErrorLine line : lines) { - int length = line.neededMargin(); + int neededMargin = line.neededMargin(); - if (length > maxLength) { - maxLength = length; + if (neededMargin > maxMargin) { + maxMargin = neededMargin; } } StringBuilder builder = new StringBuilder(); - builder.append('\n'); for (ErrorLine line : lines) { - int length = line.neededMargin(); + int neededMargin = line.neededMargin(); - builder.append(FlwUtil.repeatChar(' ', maxLength - length)) - .append(line.build()) + if (neededMargin >= 0) { + builder.append(StringUtil.repeatChar(' ', maxMargin - neededMargin)); + } + + builder.append(line.build()) + .append(ConsoleColors.RESET) .append('\n'); } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorLevel.java b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorLevel.java index e45345124..316e3ceff 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorLevel.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorLevel.java @@ -1,9 +1,12 @@ package com.jozufozu.flywheel.core.source.error; +import com.jozufozu.flywheel.util.ConsoleColors; + public enum ErrorLevel { - WARN("warn"), - ERROR("error"), - HINT("hint"), + WARN(ConsoleColors.YELLOW + "warn"), + ERROR(ConsoleColors.RED + "error"), + HINT(ConsoleColors.WHITE_BRIGHT + "hint"), + NOTE(ConsoleColors.WHITE_BRIGHT + "note"), ; private final String error; diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java index 607b8831b..b287f9627 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java @@ -9,6 +9,7 @@ import com.jozufozu.flywheel.core.source.ShaderLoadingException; import com.jozufozu.flywheel.core.source.SourceFile; import com.jozufozu.flywheel.core.source.span.Span; import com.jozufozu.flywheel.util.FlwUtil; +import com.jozufozu.flywheel.util.StringUtil; public class ErrorReporter { @@ -60,18 +61,17 @@ public class ErrorReporter { public ErrorBuilder generateSpanError(Span span, String message) { SourceFile file = span.getSourceFile(); - return error(message) - .pointAtFile(file) + return error(message).pointAtFile(file) .pointAt(span, 2); } public ErrorBuilder generateFileError(SourceFile file, String message) { - return error(message) - .pointAtFile(file); + return error(message).pointAtFile(file); } - public ErrorBuilder error(CharSequence msg) { - var out = ErrorBuilder.error(msg); + public ErrorBuilder error(String msg) { + var out = ErrorBuilder.create() + .error(msg); reportedErrors.add(out); return out; } @@ -88,9 +88,7 @@ public class ErrorReporter { return new ShaderLoadingException(allErrors); } - public static void printLines(CharSequence source) { - String string = source.toString(); - + public static void printLines(String string) { List lines = string.lines() .toList(); @@ -103,7 +101,7 @@ public class ErrorReporter { for (int i = 0; i < size; i++) { builder.append(i) - .append(FlwUtil.repeatChar(' ', maxWidth - FlwUtil.numDigits(i))) + .append(StringUtil.repeatChar(' ', maxWidth - FlwUtil.numDigits(i))) .append("| ") .append(lines.get(i)) .append('\n'); diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/Divider.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/Divider.java index c9206c740..bca13fdc4 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/Divider.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/Divider.java @@ -2,7 +2,7 @@ package com.jozufozu.flywheel.core.source.error.lines; public enum Divider { BAR(" | "), - ARROW("-->"), + ARROW("-> "), ; private final String s; diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/FileLine.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/FileLine.java index f1facc2d5..3bcf9cbe9 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/FileLine.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/FileLine.java @@ -4,7 +4,7 @@ public record FileLine(String fileName) implements ErrorLine { @Override public String left() { - return "--"; + return "-"; } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/HeaderLine.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/HeaderLine.java index e806cc399..25cd63783 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/HeaderLine.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/HeaderLine.java @@ -4,7 +4,7 @@ public record HeaderLine(String level, CharSequence message) implements ErrorLin @Override public int neededMargin() { - return 0; + return -1; } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslBuilder.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslBuilder.java index ef31652f9..45006833e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslBuilder.java @@ -8,8 +8,6 @@ import java.util.stream.Collectors; import com.jozufozu.flywheel.util.Pair; public class GlslBuilder { - public static final String INDENT = " "; - private final List elements = new ArrayList<>(); public void define(String name, String value) { @@ -39,16 +37,12 @@ public class GlslBuilder { public String build() { return elements.stream() - .map(GlslRootElement::minPrint) + .map(GlslRootElement::prettyPrint) .collect(Collectors.joining("\n")); } - public static String indent(int indentation) { - return INDENT.repeat(indentation); - } - public interface GlslRootElement { - String minPrint(); + String prettyPrint(); } public enum Separators implements GlslRootElement { @@ -62,14 +56,14 @@ public class GlslBuilder { } @Override - public String minPrint() { + public String prettyPrint() { return separator; } } public record Define(String name, String value) implements GlslRootElement { @Override - public String minPrint() { + public String prettyPrint() { return "#define " + name + " " + value; } } @@ -96,7 +90,7 @@ public class GlslBuilder { } @Override - public String minPrint() { + public String prettyPrint() { return "layout(location = " + binding + ") in " + type + " " + name + ";"; } } @@ -116,16 +110,16 @@ public class GlslBuilder { private String buildFields() { return fields.stream() - .map(p -> INDENT + p.first() + ' ' + p.second() + ';') + .map(p -> p.first() + ' ' + p.second() + ';') .collect(Collectors.joining("\n")); } - public String minPrint() { + public String prettyPrint() { return """ struct %s { %s }; - """.formatted(name, buildFields()); + """.formatted(name, buildFields().indent(4)); } } @@ -160,12 +154,13 @@ public class GlslBuilder { return this; } - public String minPrint() { + public String prettyPrint() { return """ %s %s(%s) { %s } - """.formatted(returnType, name, buildArguments(), body.prettyPrint()); + """.formatted(returnType, name, buildArguments(), body.prettyPrint() + .indent(4)); } private String buildArguments() { diff --git a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslStmt.java b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslStmt.java index 241184112..89a3a9dc8 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslStmt.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/generate/GlslStmt.java @@ -54,7 +54,7 @@ public interface GlslStmt extends LangItem { .prettyPrint(); return """ case %s: - %s""".formatted(variant, block); + %s""".formatted(variant, block.indent(4)); } } } diff --git a/src/main/java/com/jozufozu/flywheel/util/ConsoleColors.java b/src/main/java/com/jozufozu/flywheel/util/ConsoleColors.java new file mode 100644 index 000000000..9a67f6d00 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/ConsoleColors.java @@ -0,0 +1,77 @@ +package com.jozufozu.flywheel.util; + +// https://stackoverflow.com/a/45444716 +public class ConsoleColors { + // Reset + public static final String RESET = "\033[0m"; // Text Reset + + // Regular Colors + public static final String BLACK = "\033[0;30m"; // BLACK + public static final String RED = "\033[0;31m"; // RED + public static final String GREEN = "\033[0;32m"; // GREEN + public static final String YELLOW = "\033[0;33m"; // YELLOW + public static final String BLUE = "\033[0;34m"; // BLUE + public static final String PURPLE = "\033[0;35m"; // PURPLE + public static final String CYAN = "\033[0;36m"; // CYAN + public static final String WHITE = "\033[0;37m"; // WHITE + + // Bold + public static final String BLACK_BOLD = "\033[1;30m"; // BLACK + public static final String RED_BOLD = "\033[1;31m"; // RED + public static final String GREEN_BOLD = "\033[1;32m"; // GREEN + public static final String YELLOW_BOLD = "\033[1;33m"; // YELLOW + public static final String BLUE_BOLD = "\033[1;34m"; // BLUE + public static final String PURPLE_BOLD = "\033[1;35m"; // PURPLE + public static final String CYAN_BOLD = "\033[1;36m"; // CYAN + public static final String WHITE_BOLD = "\033[1;37m"; // WHITE + + // Underline + public static final String BLACK_UNDERLINED = "\033[4;30m"; // BLACK + public static final String RED_UNDERLINED = "\033[4;31m"; // RED + public static final String GREEN_UNDERLINED = "\033[4;32m"; // GREEN + public static final String YELLOW_UNDERLINED = "\033[4;33m"; // YELLOW + public static final String BLUE_UNDERLINED = "\033[4;34m"; // BLUE + public static final String PURPLE_UNDERLINED = "\033[4;35m"; // PURPLE + public static final String CYAN_UNDERLINED = "\033[4;36m"; // CYAN + public static final String WHITE_UNDERLINED = "\033[4;37m"; // WHITE + + // Background + public static final String BLACK_BACKGROUND = "\033[40m"; // BLACK + public static final String RED_BACKGROUND = "\033[41m"; // RED + public static final String GREEN_BACKGROUND = "\033[42m"; // GREEN + public static final String YELLOW_BACKGROUND = "\033[43m"; // YELLOW + public static final String BLUE_BACKGROUND = "\033[44m"; // BLUE + public static final String PURPLE_BACKGROUND = "\033[45m"; // PURPLE + public static final String CYAN_BACKGROUND = "\033[46m"; // CYAN + public static final String WHITE_BACKGROUND = "\033[47m"; // WHITE + + // High Intensity + public static final String BLACK_BRIGHT = "\033[0;90m"; // BLACK + public static final String RED_BRIGHT = "\033[0;91m"; // RED + public static final String GREEN_BRIGHT = "\033[0;92m"; // GREEN + public static final String YELLOW_BRIGHT = "\033[0;93m"; // YELLOW + public static final String BLUE_BRIGHT = "\033[0;94m"; // BLUE + public static final String PURPLE_BRIGHT = "\033[0;95m"; // PURPLE + public static final String CYAN_BRIGHT = "\033[0;96m"; // CYAN + public static final String WHITE_BRIGHT = "\033[0;97m"; // WHITE + + // Bold High Intensity + public static final String BLACK_BOLD_BRIGHT = "\033[1;90m"; // BLACK + public static final String RED_BOLD_BRIGHT = "\033[1;91m"; // RED + public static final String GREEN_BOLD_BRIGHT = "\033[1;92m"; // GREEN + public static final String YELLOW_BOLD_BRIGHT = "\033[1;93m";// YELLOW + public static final String BLUE_BOLD_BRIGHT = "\033[1;94m"; // BLUE + public static final String PURPLE_BOLD_BRIGHT = "\033[1;95m";// PURPLE + public static final String CYAN_BOLD_BRIGHT = "\033[1;96m"; // CYAN + public static final String WHITE_BOLD_BRIGHT = "\033[1;97m"; // WHITE + + // High Intensity backgrounds + public static final String BLACK_BACKGROUND_BRIGHT = "\033[0;100m";// BLACK + public static final String RED_BACKGROUND_BRIGHT = "\033[0;101m";// RED + public static final String GREEN_BACKGROUND_BRIGHT = "\033[0;102m";// GREEN + public static final String YELLOW_BACKGROUND_BRIGHT = "\033[0;103m";// YELLOW + public static final String BLUE_BACKGROUND_BRIGHT = "\033[0;104m";// BLUE + public static final String PURPLE_BACKGROUND_BRIGHT = "\033[0;105m"; // PURPLE + public static final String CYAN_BACKGROUND_BRIGHT = "\033[0;106m"; // CYAN + public static final String WHITE_BACKGROUND_BRIGHT = "\033[0;107m"; // WHITE +} diff --git a/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java b/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java index be55e6518..ed3df090d 100644 --- a/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java +++ b/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.util; -import java.util.Arrays; import java.util.Map; import java.util.stream.Stream; @@ -22,14 +21,6 @@ public class FlwUtil { return ((BlockEntityRenderDispatcherAccessor) mc.getBlockEntityRenderDispatcher()).flywheel$getRenderers(); } - public static String repeatChar(char c, int n) { - char[] arr = new char[n]; - - Arrays.fill(arr, c); - - return new String(arr); - } - public static PoseStack copyPoseStack(PoseStack stack) { PoseStack copy = new PoseStack(); copy.last().pose().load(stack.last().pose()); diff --git a/src/main/java/com/jozufozu/flywheel/util/StringUtil.java b/src/main/java/com/jozufozu/flywheel/util/StringUtil.java index 120af758a..46c9e3d25 100644 --- a/src/main/java/com/jozufozu/flywheel/util/StringUtil.java +++ b/src/main/java/com/jozufozu/flywheel/util/StringUtil.java @@ -23,6 +23,11 @@ public class StringUtil { private static final NumberFormat THREE_DECIMAL_PLACES = new DecimalFormat("#0.000"); + public static int countLines(String s) { + return (int) s.lines() + .count(); + } + public static String formatBytes(long bytes) { if (bytes < 1024) { return bytes + " B"; @@ -114,4 +119,12 @@ public class StringUtil { } return bytebuffer; } + + public static String repeatChar(char c, int n) { + char[] arr = new char[n]; + + Arrays.fill(arr, c); + + return new String(arr); + } }