mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-28 16:06:28 +01:00
GL32 shaders and errors
- Update shaders to glsl 150 - Objectfy errors
This commit is contained in:
parent
5ce960c5d5
commit
0663218b67
27 changed files with 404 additions and 257 deletions
|
@ -1,8 +1,12 @@
|
|||
package com.jozufozu.flywheel.backend.gl;
|
||||
|
||||
public enum GLSLVersion {
|
||||
@Deprecated
|
||||
V110(110),
|
||||
@Deprecated
|
||||
V120(120),
|
||||
V150(150),
|
||||
V330(330),
|
||||
;
|
||||
|
||||
public final int version;
|
||||
|
|
|
@ -29,14 +29,14 @@ public class InstancingProgramMetaData {
|
|||
Optional<ShaderFunction> vertexFunc = file.findFunction("vertex");
|
||||
Optional<ShaderFunction> fragmentFunc = file.findFunction("fragment");
|
||||
|
||||
if (!fragmentFunc.isPresent()) {
|
||||
if (fragmentFunc.isEmpty()) {
|
||||
ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined");
|
||||
}
|
||||
if (!vertexFunc.isPresent()) {
|
||||
if (vertexFunc.isEmpty()) {
|
||||
ErrorReporter.generateFileError(file, "could not find \"vertex\" function");
|
||||
}
|
||||
|
||||
if (!fragmentFunc.isPresent() || !vertexFunc.isPresent()) {
|
||||
if (fragmentFunc.isEmpty() || vertexFunc.isEmpty()) {
|
||||
throw new ShaderLoadingException();
|
||||
}
|
||||
|
||||
|
@ -64,19 +64,19 @@ public class InstancingProgramMetaData {
|
|||
Optional<ShaderStruct> maybeVertex = file.findStruct(vertexName);
|
||||
Optional<ShaderStruct> maybeInstance = file.findStruct(instanceName);
|
||||
|
||||
if (!maybeVertex.isPresent()) {
|
||||
if (maybeVertex.isEmpty()) {
|
||||
ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined");
|
||||
}
|
||||
|
||||
if (!maybeInterpolant.isPresent()) {
|
||||
if (maybeInterpolant.isEmpty()) {
|
||||
ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined");
|
||||
}
|
||||
|
||||
if (!maybeInstance.isPresent()) {
|
||||
if (maybeInstance.isEmpty()) {
|
||||
ErrorReporter.generateMissingStruct(file, instanceName, "struct not defined");
|
||||
}
|
||||
|
||||
if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent() || !maybeInstance.isPresent()) {
|
||||
if (maybeVertex.isEmpty() || maybeInterpolant.isEmpty() || maybeInstance.isEmpty()) {
|
||||
throw new ShaderLoadingException();
|
||||
}
|
||||
|
||||
|
|
|
@ -37,9 +37,9 @@ public class InstancingTemplate extends Template<InstancingProgramMetaData> {
|
|||
public void vertexFooter(StringBuilder template, SourceFile file) {
|
||||
InstancingProgramMetaData data = getMetadata(file);
|
||||
|
||||
Template.prefixFields(template, data.vertex, "attribute", "a_v_");
|
||||
Template.prefixFields(template, data.instance, "attribute", "a_i_");
|
||||
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
|
||||
Template.prefixFields(template, data.vertex, "in", "a_v_");
|
||||
Template.prefixFields(template, data.instance, "in", "a_i_");
|
||||
Template.prefixFields(template, data.interpolant, "out", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.vertexName)
|
||||
|
@ -63,7 +63,7 @@ public class InstancingTemplate extends Template<InstancingProgramMetaData> {
|
|||
public void fragmentFooter(StringBuilder template, SourceFile file) {
|
||||
InstancingProgramMetaData data = getMetadata(file);
|
||||
|
||||
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
|
||||
Template.prefixFields(template, data.interpolant, "in", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.interpolantName)
|
||||
|
|
|
@ -26,15 +26,15 @@ public class OneShotProgramMetaData {
|
|||
Optional<ShaderFunction> maybeVertexMain = file.findFunction("vertex");
|
||||
Optional<ShaderFunction> maybeFragmentMain = file.findFunction("fragment");
|
||||
|
||||
if (!maybeVertexMain.isPresent()) {
|
||||
if (maybeVertexMain.isEmpty()) {
|
||||
ErrorReporter.generateFileError(file, "could not find \"vertex\" function");
|
||||
}
|
||||
|
||||
if (!maybeFragmentMain.isPresent()) {
|
||||
if (maybeFragmentMain.isEmpty()) {
|
||||
ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined");
|
||||
}
|
||||
|
||||
if (!maybeVertexMain.isPresent() || !maybeFragmentMain.isPresent()) {
|
||||
if (maybeVertexMain.isEmpty() || maybeFragmentMain.isEmpty()) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
|
@ -62,13 +62,13 @@ public class OneShotProgramMetaData {
|
|||
Optional<ShaderStruct> maybeInterpolant = file.findStruct(interpolantName);
|
||||
Optional<ShaderStruct> maybeVertex = file.findStruct(vertexName);
|
||||
|
||||
if (!maybeVertex.isPresent())
|
||||
if (maybeVertex.isEmpty())
|
||||
ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined");
|
||||
|
||||
if (!maybeInterpolant.isPresent())
|
||||
if (maybeInterpolant.isEmpty())
|
||||
ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined");
|
||||
|
||||
if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent()) {
|
||||
if (maybeVertex.isEmpty() || maybeInterpolant.isEmpty()) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@ public class OneShotTemplate extends Template<OneShotProgramMetaData> {
|
|||
public void vertexFooter(StringBuilder template, SourceFile file) {
|
||||
OneShotProgramMetaData data = getMetadata(file);
|
||||
|
||||
Template.prefixFields(template, data.vertex, "attribute", "a_v_");
|
||||
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
|
||||
Template.prefixFields(template, data.vertex, "in", "a_v_");
|
||||
Template.prefixFields(template, data.interpolant, "out", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.vertexName)
|
||||
|
@ -53,7 +53,7 @@ public class OneShotTemplate extends Template<OneShotProgramMetaData> {
|
|||
public void fragmentFooter(StringBuilder template, SourceFile file) {
|
||||
OneShotProgramMetaData data = getMetadata(file);
|
||||
|
||||
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
|
||||
Template.prefixFields(template, data.interpolant, "in", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.interpolant.name)
|
||||
|
|
|
@ -25,10 +25,10 @@ public abstract class Template<D> {
|
|||
|
||||
private final Map<SourceFile, D> metadata = new HashMap<>();
|
||||
|
||||
private final Function<SourceFile, D> parser;
|
||||
private final Function<SourceFile, D> reader;
|
||||
|
||||
protected Template(Function<SourceFile, D> parser) {
|
||||
this.parser = parser;
|
||||
protected Template(Function<SourceFile, D> reader) {
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,11 +46,12 @@ public abstract class Template<D> {
|
|||
public abstract Collection<ShaderInput> getShaderInputs(SourceFile file);
|
||||
|
||||
public D getMetadata(SourceFile file) {
|
||||
return metadata.computeIfAbsent(file, parser);
|
||||
// lazily read files, cache results
|
||||
return metadata.computeIfAbsent(file, reader);
|
||||
}
|
||||
|
||||
public GLSLVersion getVersion() {
|
||||
return GLSLVersion.V120;
|
||||
return GLSLVersion.V150;
|
||||
}
|
||||
|
||||
public static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) {
|
||||
|
|
|
@ -57,7 +57,7 @@ public class WorldShader {
|
|||
.append(template.getVersion())
|
||||
.append('\n')
|
||||
.append("#define ")
|
||||
.append(type.define)
|
||||
.append(type.define) // special case shader type declaration
|
||||
.append('\n')
|
||||
.append(defines != null ? defines : "")
|
||||
.append(header)
|
||||
|
|
|
@ -67,11 +67,10 @@ public class FileResolution {
|
|||
try {
|
||||
file = sources.findSource(fileLoc);
|
||||
} catch (RuntimeException error) {
|
||||
ErrorBuilder builder = new ErrorBuilder();
|
||||
builder.error(String.format("could not find source for file %s", fileLoc));
|
||||
ErrorBuilder builder = ErrorBuilder.error(String.format("could not find source for file %s", fileLoc));
|
||||
// print the location of all places where this file was referenced
|
||||
for (Span span : foundSpans) {
|
||||
builder.in(span.getSourceFile())
|
||||
builder.pointAtFile(span.getSourceFile())
|
||||
.pointAt(span, 2);
|
||||
}
|
||||
Backend.log.error(builder.build());
|
||||
|
|
|
@ -27,8 +27,8 @@ public class Index {
|
|||
Collection<SourceFile> files = sources.values();
|
||||
|
||||
for (SourceFile file : files) {
|
||||
file.getStructs().forEach(knownStructs::put);
|
||||
file.getFunctions().forEach(knownFunctions::put);
|
||||
file.structs.forEach(knownStructs::put);
|
||||
file.functions.forEach(knownFunctions::put);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,80 +13,65 @@ import com.google.common.collect.ImmutableMap;
|
|||
import com.jozufozu.flywheel.backend.source.parse.Import;
|
||||
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
|
||||
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
|
||||
import com.jozufozu.flywheel.backend.source.span.CharPos;
|
||||
import com.jozufozu.flywheel.backend.source.span.ErrorSpan;
|
||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||
import com.jozufozu.flywheel.backend.source.span.StringSpan;
|
||||
import com.jozufozu.flywheel.util.StringUtil;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Immutable class representing a shader file.
|
||||
*
|
||||
* <p>
|
||||
* This class parses shader files and generates what is effectively a high level AST of the source.
|
||||
* </p>
|
||||
*/
|
||||
public class SourceFile {
|
||||
private static final Pattern includePattern = Pattern.compile("#use \"(.*)\"");
|
||||
|
||||
private static final Pattern newLine = Pattern.compile("(\\r\\n|\\r|\\n)");
|
||||
|
||||
// https://regexr.com/60n3d
|
||||
public static final Pattern functionDeclaration = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{");
|
||||
|
||||
public final ResourceLocation name;
|
||||
|
||||
public final ShaderSources parent;
|
||||
private final String source;
|
||||
private final CharSequence elided;
|
||||
private final ImmutableList<String> lines;
|
||||
public final String source;
|
||||
/**
|
||||
* Sections of the source that must be trimmed for compilation. Currently, it only contains the spans of imports.
|
||||
*/
|
||||
public final String elided;
|
||||
|
||||
private final IntList lineStarts;
|
||||
public final SourceLines lines;
|
||||
|
||||
// Function name -> Function object
|
||||
private final ImmutableMap<String, ShaderFunction> functions;
|
||||
private final ImmutableMap<String, ShaderStruct> structs;
|
||||
/**
|
||||
* Function lookup by name.
|
||||
*/
|
||||
public final ImmutableMap<String, ShaderFunction> functions;
|
||||
|
||||
// Includes ordered as defined in the source
|
||||
private final ImmutableList<Import> imports;
|
||||
/**
|
||||
* Struct lookup by name.
|
||||
*/
|
||||
public final ImmutableMap<String, ShaderStruct> structs;
|
||||
|
||||
// Sections of the source that must be trimmed for compilation.
|
||||
private final List<Span> elisions = new ArrayList<>();
|
||||
/**
|
||||
* Includes ordered as defined in the source.
|
||||
*/
|
||||
public final ImmutableList<Import> imports;
|
||||
|
||||
public SourceFile(ShaderSources parent, ResourceLocation name, String source) {
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
this.source = source;
|
||||
|
||||
this.lineStarts = createLineStarts();
|
||||
this.lines = createLineList(lineStarts);
|
||||
this.lines = new SourceLines(source);
|
||||
|
||||
this.imports = parseImports();
|
||||
List<Span> elisions = new ArrayList<>();
|
||||
|
||||
this.imports = parseImports(elisions);
|
||||
this.functions = parseFunctions();
|
||||
this.structs = parseStructs();
|
||||
|
||||
this.elided = createElidedSource();
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public CharSequence getElidedSource() {
|
||||
return elided;
|
||||
}
|
||||
|
||||
public ShaderSources getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public ImmutableMap<String, ShaderFunction> getFunctions() {
|
||||
return functions;
|
||||
}
|
||||
|
||||
public ImmutableMap<String, ShaderStruct> getStructs() {
|
||||
return structs;
|
||||
}
|
||||
|
||||
public ImmutableList<Import> getIncludes() {
|
||||
return imports;
|
||||
this.elided = elideSource(source, elisions).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,11 +81,11 @@ public class SourceFile {
|
|||
* @return null if no definition matches the name.
|
||||
*/
|
||||
public Optional<ShaderStruct> findStruct(CharSequence name) {
|
||||
ShaderStruct struct = getStructs().get(name.toString());
|
||||
ShaderStruct struct = structs.get(name.toString());
|
||||
|
||||
if (struct != null) return Optional.of(struct);
|
||||
|
||||
for (Import include : getIncludes()) {
|
||||
for (Import include : imports) {
|
||||
Optional<ShaderStruct> externalStruct = include.getOptional()
|
||||
.flatMap(file -> file.findStruct(name));
|
||||
|
||||
|
@ -117,11 +102,11 @@ public class SourceFile {
|
|||
* @return Optional#empty() if no definition matches the name.
|
||||
*/
|
||||
public Optional<ShaderFunction> findFunction(CharSequence name) {
|
||||
ShaderFunction local = getFunctions().get(name.toString());
|
||||
ShaderFunction local = functions.get(name.toString());
|
||||
|
||||
if (local != null) return Optional.of(local);
|
||||
|
||||
for (Import include : getIncludes()) {
|
||||
for (Import include : imports) {
|
||||
Optional<ShaderFunction> external = include.getOptional()
|
||||
.flatMap(file -> file.findFunction(name));
|
||||
|
||||
|
@ -142,29 +127,12 @@ public class SourceFile {
|
|||
}
|
||||
|
||||
public void generateFinalSource(StringBuilder source) {
|
||||
for (Import include : getIncludes()) {
|
||||
for (Import include : imports) {
|
||||
SourceFile file = include.getFile();
|
||||
|
||||
if (file != null) file.generateFinalSource(source);
|
||||
}
|
||||
source.append(getElidedSource());
|
||||
}
|
||||
|
||||
public CharPos getCharPos(int charPos) {
|
||||
int lineNo = 0;
|
||||
for (; lineNo < lineStarts.size(); lineNo++) {
|
||||
int ls = lineStarts.getInt(lineNo);
|
||||
|
||||
if (charPos < ls) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lineNo -= 1;
|
||||
|
||||
int lineStart = lineStarts.getInt(lineNo);
|
||||
|
||||
return new CharPos(charPos, lineNo, charPos - lineStart);
|
||||
source.append(elided);
|
||||
}
|
||||
|
||||
public String printSource() {
|
||||
|
@ -172,18 +140,13 @@ public class SourceFile {
|
|||
|
||||
builder.append("Source for shader '")
|
||||
.append(name)
|
||||
.append("':\n");
|
||||
int i = 1;
|
||||
for (String s : lines) {
|
||||
builder.append(String.format("%1$4s: ", i++))
|
||||
.append(s)
|
||||
.append('\n');
|
||||
}
|
||||
.append("':\n")
|
||||
.append(lines.printLinesWithNumbers());
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private CharSequence createElidedSource() {
|
||||
private static CharSequence elideSource(String source, List<Span> elisions) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
|
||||
int lastEnd = 0;
|
||||
|
@ -199,34 +162,6 @@ public class SourceFile {
|
|||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the source for line breaks, recording the position of the first character of each line.
|
||||
*/
|
||||
private IntList createLineStarts() {
|
||||
IntList l = new IntArrayList();
|
||||
l.add(0); // first line is always at position 0
|
||||
|
||||
Matcher matcher = newLine.matcher(source);
|
||||
|
||||
while (matcher.find()) {
|
||||
l.add(matcher.end());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
private ImmutableList<String> createLineList(IntList lines) {
|
||||
ImmutableList.Builder<String> builder = ImmutableList.builder();
|
||||
|
||||
for (int i = 1; i < lines.size(); i++) {
|
||||
int start = lines.getInt(i - 1);
|
||||
int end = lines.getInt(i);
|
||||
|
||||
builder.add(StringUtil.trimEnd(source.substring(start, end)));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
|
||||
*/
|
||||
|
@ -284,8 +219,9 @@ public class SourceFile {
|
|||
/**
|
||||
* Scan the source for <code>#use "..."</code> directives.
|
||||
* Records the contents of the directive into an {@link Import} object, and marks the directive for elision.
|
||||
* @param elisions
|
||||
*/
|
||||
private ImmutableList<Import> parseImports() {
|
||||
private ImmutableList<Import> parseImports(List<Span> elisions) {
|
||||
Matcher uses = includePattern.matcher(source);
|
||||
|
||||
List<Import> imports = new ArrayList<>();
|
||||
|
@ -321,11 +257,4 @@ public class SourceFile {
|
|||
return -1;
|
||||
}
|
||||
|
||||
public int getLineCount() {
|
||||
return lines.size();
|
||||
}
|
||||
|
||||
public CharSequence getLine(int lineNo) {
|
||||
return lines.get(lineNo);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
package com.jozufozu.flywheel.backend.source;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.backend.source.span.CharPos;
|
||||
import com.jozufozu.flywheel.util.StringUtil;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
public class SourceLines {
|
||||
|
||||
private static final Pattern newLine = Pattern.compile("(\\r\\n|\\r|\\n)");
|
||||
|
||||
/**
|
||||
* 0-indexed line to char pos mapping.
|
||||
*/
|
||||
private final IntList lineStarts;
|
||||
|
||||
/**
|
||||
* 0-indexed line lookup
|
||||
*/
|
||||
private final ImmutableList<String> lines;
|
||||
|
||||
public SourceLines(String source) {
|
||||
this.lineStarts = createLineLookup(source);
|
||||
this.lines = getLines(source, lineStarts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the source for line breaks, recording the position of the first character of each line.
|
||||
* @param source
|
||||
*/
|
||||
private static IntList createLineLookup(String source) {
|
||||
IntList l = new IntArrayList();
|
||||
l.add(0); // first line is always at position 0
|
||||
|
||||
Matcher matcher = newLine.matcher(source);
|
||||
|
||||
while (matcher.find()) {
|
||||
l.add(matcher.end());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
private static ImmutableList<String> getLines(String source, IntList lines) {
|
||||
ImmutableList.Builder<String> builder = ImmutableList.builder();
|
||||
|
||||
for (int i = 1; i < lines.size(); i++) {
|
||||
int start = lines.getInt(i - 1);
|
||||
int end = lines.getInt(i);
|
||||
|
||||
builder.add(StringUtil.trimEnd(source.substring(start, end)));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public CharPos getCharPos(int charPos) {
|
||||
int lineNo = 0;
|
||||
for (; lineNo < lineStarts.size(); lineNo++) {
|
||||
int ls = lineStarts.getInt(lineNo);
|
||||
|
||||
if (charPos < ls) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lineNo -= 1;
|
||||
|
||||
int lineStart = lineStarts.getInt(lineNo);
|
||||
|
||||
return new CharPos(charPos, lineNo, charPos - lineStart);
|
||||
}
|
||||
|
||||
public int getLineCount() {
|
||||
return lines.size();
|
||||
}
|
||||
|
||||
public String getLine(int lineNo) {
|
||||
return lines.get(lineNo);
|
||||
}
|
||||
|
||||
public String printLinesWithNumbers() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (int i = 0, linesSize = lines.size(); i < linesSize; i++) {
|
||||
builder.append(String.format("%1$4s: ", i + 1))
|
||||
.append(lines.get(i))
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
|
@ -1,95 +1,79 @@
|
|||
package com.jozufozu.flywheel.backend.source.error;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.source.SourceLines;
|
||||
import com.jozufozu.flywheel.backend.source.error.lines.ErrorLine;
|
||||
import com.jozufozu.flywheel.backend.source.error.lines.FileLine;
|
||||
import com.jozufozu.flywheel.backend.source.error.lines.HeaderLine;
|
||||
import com.jozufozu.flywheel.backend.source.error.lines.SourceLine;
|
||||
import com.jozufozu.flywheel.backend.source.error.lines.SpanHighlightLine;
|
||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||
import com.jozufozu.flywheel.util.FlwUtil;
|
||||
|
||||
public class ErrorBuilder {
|
||||
|
||||
private final StringBuilder internal = new StringBuilder();
|
||||
private final List<ErrorLine> lines = new ArrayList<>();
|
||||
|
||||
public ErrorBuilder error(CharSequence msg) {
|
||||
internal.append("error: ")
|
||||
.append(msg);
|
||||
return endLine();
|
||||
private final Level level;
|
||||
|
||||
public ErrorBuilder(Level level, CharSequence msg) {
|
||||
this.level = level;
|
||||
|
||||
lines.add(new HeaderLine(level.toString(), msg));
|
||||
}
|
||||
|
||||
public ErrorBuilder note(CharSequence msg) {
|
||||
internal.append("note: ")
|
||||
.append(msg);
|
||||
return endLine();
|
||||
public static ErrorBuilder error(CharSequence msg) {
|
||||
return new ErrorBuilder(Level.ERROR, msg);
|
||||
}
|
||||
|
||||
public ErrorBuilder hint(CharSequence msg) {
|
||||
internal.append("hint: ")
|
||||
.append(msg);
|
||||
return endLine();
|
||||
public static ErrorBuilder warn(CharSequence msg) {
|
||||
return new ErrorBuilder(Level.WARN, msg);
|
||||
}
|
||||
|
||||
public ErrorBuilder in(SourceFile file) {
|
||||
internal.append("--> ")
|
||||
.append(file.name);
|
||||
return endLine();
|
||||
}
|
||||
|
||||
public ErrorBuilder numberedLine(int no, CharSequence content) {
|
||||
return line(String.valueOf(no), content);
|
||||
}
|
||||
|
||||
public ErrorBuilder numberedLine(int no, int width, CharSequence content) {
|
||||
String fmt = "%1$" + width + 's';
|
||||
return line(String.format(fmt, no), content);
|
||||
}
|
||||
|
||||
public ErrorBuilder line(CharSequence leftColumn, CharSequence rightColumn) {
|
||||
|
||||
internal.append(leftColumn)
|
||||
.append(" | ")
|
||||
.append(rightColumn);
|
||||
|
||||
return endLine();
|
||||
}
|
||||
|
||||
public ErrorBuilder endLine() {
|
||||
internal.append('\n');
|
||||
public ErrorBuilder pointAtFile(SourceFile file) {
|
||||
lines.add(new FileLine(file.name.toString()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorBuilder hintIncludeFor(@Nullable Span span, CharSequence msg) {
|
||||
if (span == null) return this;
|
||||
|
||||
hint("add " + span.getSourceFile().importStatement() + " " + msg)
|
||||
.in(span.getSourceFile())
|
||||
.pointAt(span, 1);
|
||||
String builder = "add " + span.getSourceFile()
|
||||
.importStatement() + ' ' + msg;
|
||||
|
||||
return this;
|
||||
lines.add(new HeaderLine("hint", builder));
|
||||
|
||||
return this.pointAtFile(span.getSourceFile())
|
||||
.pointAt(span, 1);
|
||||
}
|
||||
|
||||
public ErrorBuilder pointAt(Span span, int ctxLines) {
|
||||
|
||||
SourceFile file = span.getSourceFile();
|
||||
|
||||
if (span.lines() == 1) {
|
||||
SourceLines lines = span.getSourceFile().lines;
|
||||
|
||||
int spanLine = span.firstLine();
|
||||
|
||||
int firstLine = Math.max(0, spanLine - ctxLines);
|
||||
int lastLine = Math.min(file.getLineCount(), spanLine + ctxLines);
|
||||
int lastLine = Math.min(lines.getLineCount(), spanLine + ctxLines);
|
||||
|
||||
int digits = FlwUtil.numDigits(lastLine);
|
||||
|
||||
int firstCol = span.getStart().getCol();
|
||||
int lastCol = span.getEnd().getCol();
|
||||
int firstCol = span.getStart()
|
||||
.col();
|
||||
int lastCol = span.getEnd()
|
||||
.col();
|
||||
|
||||
for (int i = firstLine; i <= lastLine; i++) {
|
||||
CharSequence line = file.getLine(i);
|
||||
CharSequence line = lines.getLine(i);
|
||||
|
||||
numberedLine(i + 1, digits, line);
|
||||
this.lines.add(SourceLine.numbered(i + 1, line.toString()));
|
||||
|
||||
if (i == spanLine) {
|
||||
line(FlwUtil.repeatChar(' ', digits), generateUnderline(firstCol, lastCol));
|
||||
this.lines.add(new SpanHighlightLine(firstCol, lastCol));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,19 +82,23 @@ public class ErrorBuilder {
|
|||
}
|
||||
|
||||
public CharSequence build() {
|
||||
return internal;
|
||||
|
||||
int maxLength = -1;
|
||||
for (ErrorLine line : lines) {
|
||||
int length = line.neededMargin();
|
||||
|
||||
if (length > maxLength) maxLength = length;
|
||||
}
|
||||
|
||||
public CharSequence generateUnderline(int firstCol, int lastCol) {
|
||||
StringBuilder line = new StringBuilder(lastCol);
|
||||
for (int i = 0; i < firstCol; i++) {
|
||||
line.append(' ');
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (ErrorLine line : lines) {
|
||||
int length = line.neededMargin();
|
||||
|
||||
builder.append(FlwUtil.repeatChar(' ', maxLength - length))
|
||||
.append(line.build())
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
for (int i = firstCol; i < lastCol; i++) {
|
||||
line.append('^');
|
||||
}
|
||||
|
||||
return line;
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,8 @@ public class ErrorReporter {
|
|||
public static void generateSpanError(Span span, String message) {
|
||||
SourceFile file = span.getSourceFile();
|
||||
|
||||
ErrorBuilder builder = new ErrorBuilder();
|
||||
|
||||
CharSequence error = builder.error(message)
|
||||
.in(file)
|
||||
CharSequence error = ErrorBuilder.error(message)
|
||||
.pointAtFile(file)
|
||||
.pointAt(span, 2)
|
||||
.build();
|
||||
|
||||
|
@ -25,10 +23,8 @@ public class ErrorReporter {
|
|||
|
||||
public static void generateFileError(SourceFile file, String message) {
|
||||
|
||||
ErrorBuilder builder = new ErrorBuilder();
|
||||
|
||||
CharSequence error = builder.error(message)
|
||||
.in(file)
|
||||
CharSequence error = ErrorBuilder.error(message)
|
||||
.pointAtFile(file)
|
||||
.build();
|
||||
|
||||
Backend.log.error(error);
|
||||
|
@ -43,10 +39,9 @@ public class ErrorReporter {
|
|||
.stream()
|
||||
.findFirst()
|
||||
.map(ShaderStruct::getName);
|
||||
ErrorBuilder builder = new ErrorBuilder();
|
||||
|
||||
ErrorBuilder error = builder.error(msg)
|
||||
.in(file)
|
||||
ErrorBuilder error = ErrorBuilder.error(msg)
|
||||
.pointAtFile(file)
|
||||
.pointAt(vertexName, 2)
|
||||
.hintIncludeFor(span.orElse(null), hint);
|
||||
|
||||
|
@ -62,10 +57,9 @@ public class ErrorReporter {
|
|||
.stream()
|
||||
.findFirst()
|
||||
.map(ShaderFunction::getName);
|
||||
ErrorBuilder builder = new ErrorBuilder();
|
||||
|
||||
ErrorBuilder error = builder.error(msg)
|
||||
.in(file)
|
||||
ErrorBuilder error = ErrorBuilder.error(msg)
|
||||
.pointAtFile(file)
|
||||
.hintIncludeFor(span.orElse(null), hint);
|
||||
|
||||
Backend.log.error(error.build());
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.jozufozu.flywheel.backend.source.error;
|
||||
|
||||
public enum Level {
|
||||
WARN("warn"),
|
||||
ERROR("error"),
|
||||
;
|
||||
|
||||
private final String error;
|
||||
|
||||
Level(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return error;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||
|
||||
public enum Divider {
|
||||
BAR(" | "),
|
||||
ARROW("-->"),
|
||||
;
|
||||
|
||||
private final String s;
|
||||
|
||||
Divider(String s) {
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return s;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||
|
||||
public interface ErrorLine {
|
||||
|
||||
default int neededMargin() {
|
||||
return left().length();
|
||||
}
|
||||
|
||||
default Divider divider() {
|
||||
return Divider.BAR;
|
||||
}
|
||||
|
||||
default String build() {
|
||||
return left() + divider() + right();
|
||||
}
|
||||
|
||||
String left();
|
||||
String right();
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||
|
||||
public record FileLine(String fileName) implements ErrorLine {
|
||||
|
||||
@Override
|
||||
public String left() {
|
||||
return "--";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Divider divider() {
|
||||
return Divider.ARROW;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String right() {
|
||||
return fileName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||
|
||||
import com.jozufozu.flywheel.backend.source.error.Level;
|
||||
|
||||
public record HeaderLine(String level, CharSequence message) implements ErrorLine {
|
||||
|
||||
@Override
|
||||
public int neededMargin() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String build() {
|
||||
return level + ": " + message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String left() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String right() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||
|
||||
public record SourceLine(String number, String line) implements ErrorLine {
|
||||
|
||||
public static SourceLine numbered(int number, String line) {
|
||||
return new SourceLine(Integer.toString(number), line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String left() {
|
||||
return number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String right() {
|
||||
return line;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||
|
||||
public class SpanHighlightLine implements ErrorLine {
|
||||
private final String line;
|
||||
|
||||
public SpanHighlightLine(int firstCol, int lastCol) {
|
||||
line = generateUnderline(firstCol, lastCol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String left() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String right() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public static String generateUnderline(int firstCol, int lastCol) {
|
||||
return " ".repeat(Math.max(0, firstCol)) + "^".repeat(Math.max(0, lastCol - firstCol));
|
||||
}
|
||||
}
|
|
@ -3,27 +3,5 @@ package com.jozufozu.flywheel.backend.source.span;
|
|||
/**
|
||||
* A position in a file.
|
||||
*/
|
||||
public class CharPos {
|
||||
|
||||
private final int idx;
|
||||
private final int line;
|
||||
private final int col;
|
||||
|
||||
public CharPos(int idx, int line, int col) {
|
||||
this.idx = idx;
|
||||
this.line = line;
|
||||
this.col = col;
|
||||
}
|
||||
|
||||
public int getPos() {
|
||||
return idx;
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public int getCol() {
|
||||
return col;
|
||||
}
|
||||
public record CharPos(int pos, int line, int col) {
|
||||
}
|
||||
|
|
|
@ -5,7 +5,11 @@ import java.util.regex.Matcher;
|
|||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
|
||||
/**
|
||||
* A span of code in a {@link SourceFile}.
|
||||
* A segment of code in a {@link SourceFile}.
|
||||
*
|
||||
* <p>
|
||||
* Spans are used for pretty-printing errors.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class Span implements CharSequence {
|
||||
|
||||
|
@ -14,7 +18,7 @@ public abstract class Span implements CharSequence {
|
|||
protected final CharPos end;
|
||||
|
||||
public Span(SourceFile in, int start, int end) {
|
||||
this(in, in.getCharPos(start), in.getCharPos(end));
|
||||
this(in, in.lines.getCharPos(start), in.lines.getCharPos(end));
|
||||
}
|
||||
|
||||
public Span(SourceFile in, CharPos start, CharPos end) {
|
||||
|
@ -42,14 +46,14 @@ public abstract class Span implements CharSequence {
|
|||
* @return the string index at the (inclusive) beginning of this code segment.
|
||||
*/
|
||||
public int getStartPos() {
|
||||
return start.getPos();
|
||||
return start.pos();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the string index at the (exclusive) end of this code segment.
|
||||
*/
|
||||
public int getEndPos() {
|
||||
return end.getPos();
|
||||
return end.pos();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,11 +67,11 @@ public abstract class Span implements CharSequence {
|
|||
* @return how many lines this span spans.
|
||||
*/
|
||||
public int lines() {
|
||||
return end.getLine() - start.getLine() + 1;
|
||||
return end.line() - start.line() + 1;
|
||||
}
|
||||
|
||||
public int firstLine() {
|
||||
return start.getLine();
|
||||
return start.line();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +93,7 @@ public abstract class Span implements CharSequence {
|
|||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return in.getSource().charAt(start.getPos() + index);
|
||||
return in.source.charAt(start.pos() + index);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -14,13 +14,13 @@ public class StringSpan extends Span {
|
|||
|
||||
@Override
|
||||
public Span subSpan(int from, int to) {
|
||||
return new StringSpan(in, start.getPos() + from, start.getPos() + to);
|
||||
return new StringSpan(in, start.pos() + from, start.pos() + to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
return in.getSource()
|
||||
.substring(start.getPos(), end.getPos());
|
||||
return in.source
|
||||
.substring(start.pos(), end.pos());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,6 +26,8 @@ void FLWFinalizeWorldPos(inout vec4 worldPos) {
|
|||
|
||||
#elif defined(FRAGMENT_SHADER)
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
vec4 FLWBlockTexture(vec2 texCoords) {
|
||||
vec4 cr = texture2D(uCrumbling, texCoords * uTextureScale);
|
||||
float diffuseAlpha = texture2D(uBlockAtlas, texCoords).a;
|
||||
|
@ -42,7 +44,11 @@ void FLWFinalizeColor(vec4 color) {
|
|||
color.a = a;
|
||||
#endif
|
||||
|
||||
gl_FragColor = color;
|
||||
if (color.a < 0.1) {
|
||||
discard;
|
||||
}
|
||||
|
||||
fragColor = color;
|
||||
}
|
||||
|
||||
vec4 FLWLight(vec2 lightCoords) {
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
varying float FragDistance;
|
||||
#if defined(VERTEX_SHADER)
|
||||
out float FragDistance;
|
||||
#elif defined(FRAGMENT_SHADER)
|
||||
in float FragDistance;
|
||||
#endif
|
||||
uniform vec4 uFogColor;
|
||||
uniform vec2 uFogRange;
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ void FLWFinalizeWorldPos(inout vec4 worldPos) {
|
|||
//#endif
|
||||
//#endif
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
vec4 FLWBlockTexture(vec2 texCoords) {
|
||||
return texture2D(uBlockAtlas, texCoords);
|
||||
}
|
||||
|
@ -53,7 +55,7 @@ void FLWFinalizeColor(vec4 color) {
|
|||
}
|
||||
#endif
|
||||
|
||||
gl_FragColor = color;
|
||||
fragColor = color;
|
||||
}
|
||||
|
||||
vec4 FLWLight(vec2 lightCoords) {
|
||||
|
|
Loading…
Reference in a new issue