mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-28 05:44:59 +01:00
Compile everything on the fly
- Simplify game state system - Need some way to re-add errors on load. - Streamline shader compilation, reduce map lookups - Move pipeline package from backend to core - Simplify interfaces and remove unnecessary classes
This commit is contained in:
parent
aca216cf9a
commit
1b09c2c1eb
40 changed files with 269 additions and 514 deletions
|
@ -1,15 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.shader;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a vertex format agnostic shader.
|
|
||||||
*/
|
|
||||||
public interface FlexibleShader<P extends GlProgram> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a version of this shader that accepts the given VertexType as input.
|
|
||||||
*/
|
|
||||||
P get(VertexType type);
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
package com.jozufozu.flywheel.backend;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.shader.FlexibleShader;
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||||
|
|
||||||
|
@ -8,12 +7,7 @@ import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public interface ShaderContext<P extends GlProgram> {
|
public interface ShaderContext<P extends GlProgram> {
|
||||||
|
|
||||||
default P getProgram(ResourceLocation loc, VertexType inputType) {
|
P getProgram(ResourceLocation loc, VertexType vertexType);
|
||||||
return this.getProgramSupplier(loc)
|
|
||||||
.get(inputType);
|
|
||||||
}
|
|
||||||
|
|
||||||
FlexibleShader<P> getProgramSupplier(ResourceLocation loc);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load all programs associated with this context. This might be just one, if the context is very specialized.
|
* Load all programs associated with this context. This might be just one, if the context is very specialized.
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.shader;
|
package com.jozufozu.flywheel.backend.gl.shader;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
import org.lwjgl.opengl.GL20;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
import com.jozufozu.flywheel.backend.gl.GlObject;
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.ShaderCompiler;
|
|
||||||
import com.jozufozu.flywheel.backend.source.ShaderLoadingException;
|
import com.jozufozu.flywheel.backend.source.ShaderLoadingException;
|
||||||
import com.jozufozu.flywheel.backend.source.error.ErrorBuilder;
|
import com.jozufozu.flywheel.core.pipeline.ShaderCompiler;
|
||||||
import com.jozufozu.flywheel.backend.source.error.ErrorReporter;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
@ -30,27 +25,7 @@ public class GlShader extends GlObject {
|
||||||
String log = GL20.glGetShaderInfoLog(handle);
|
String log = GL20.glGetShaderInfoLog(handle);
|
||||||
|
|
||||||
if (!log.isEmpty()) {
|
if (!log.isEmpty()) {
|
||||||
List<String> lines = log.lines()
|
env.printShaderInfoLog(source, log, this.name);
|
||||||
.toList();
|
|
||||||
|
|
||||||
boolean needsSourceDump = false;
|
|
||||||
|
|
||||||
StringBuilder errors = new StringBuilder();
|
|
||||||
for (String line : lines) {
|
|
||||||
ErrorBuilder builder = env.parseCompilerError(line);
|
|
||||||
|
|
||||||
if (builder != null) {
|
|
||||||
errors.append(builder.build());
|
|
||||||
} else {
|
|
||||||
errors.append(line).append('\n');
|
|
||||||
needsSourceDump = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Backend.LOGGER.error("Errors compiling '" + name + "': \n" + errors);
|
|
||||||
if (needsSourceDump) {
|
|
||||||
// TODO: generated code gets its own "file"
|
|
||||||
ErrorReporter.printLines(source);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) {
|
if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) {
|
||||||
|
@ -64,4 +39,5 @@ public class GlShader extends GlObject {
|
||||||
protected void deleteInternal(int handle) {
|
protected void deleteInternal(int handle) {
|
||||||
GL20.glDeleteShader(handle);
|
GL20.glDeleteShader(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import java.util.stream.Stream;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.MaterialGroup;
|
import com.jozufozu.flywheel.api.MaterialGroup;
|
||||||
import com.jozufozu.flywheel.api.shader.FlexibleShader;
|
|
||||||
import com.jozufozu.flywheel.backend.RenderLayer;
|
import com.jozufozu.flywheel.backend.RenderLayer;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
||||||
|
@ -24,7 +23,6 @@ import net.minecraft.client.Camera;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
public class InstancingEngine<P extends WorldProgram> implements Engine {
|
public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
|
@ -126,10 +124,6 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlexibleShader<P> getProgram(ResourceLocation name) {
|
|
||||||
return context.getProgramSupplier(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vec3i getOriginCoordinate() {
|
public Vec3i getOriginCoordinate() {
|
||||||
return originCoordinate;
|
return originCoordinate;
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.shader.FlexibleShader;
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
|
||||||
import com.jozufozu.flywheel.core.shader.ContextAwareProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
|
||||||
|
|
||||||
public class LazyCompiler<P extends WorldProgram> implements FlexibleShader<P> {
|
|
||||||
|
|
||||||
private final ShaderPipeline<P> pipeline;
|
|
||||||
private final ProgramSpec spec;
|
|
||||||
|
|
||||||
private final Map<VertexType, ContextAwareProgram<P>> cache = new HashMap<>();
|
|
||||||
|
|
||||||
public LazyCompiler(ShaderPipeline<P> pipeline, ProgramSpec spec) {
|
|
||||||
|
|
||||||
this.pipeline = pipeline;
|
|
||||||
this.spec = spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
cache.values().forEach(ContextAwareProgram::delete);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public P get(VertexType type) {
|
|
||||||
return cache.computeIfAbsent(type, t -> pipeline.compile(spec, t)).get();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
|
||||||
|
|
||||||
public class Shader {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
|
|
||||||
import com.jozufozu.flywheel.backend.source.parse.StructField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A single input to a shader.
|
|
||||||
*/
|
|
||||||
public class ShaderInput {
|
|
||||||
public final CharSequence name;
|
|
||||||
public final int attribCount;
|
|
||||||
|
|
||||||
public ShaderInput(CharSequence name, int attribCount) {
|
|
||||||
this.name = name;
|
|
||||||
this.attribCount = attribCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderInput withPrefix(CharSequence prefix) {
|
|
||||||
return new ShaderInput(prefix.toString() + name, attribCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Collection<ShaderInput> fromStruct(ShaderStruct struct, String prefix) {
|
|
||||||
return struct.getFields()
|
|
||||||
.stream()
|
|
||||||
.map(ShaderInput::from)
|
|
||||||
.map(a -> a.withPrefix(prefix))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ShaderInput from(StructField field) {
|
|
||||||
int attributeCount = TypeHelper.getAttributeCount(field.type);
|
|
||||||
|
|
||||||
return new ShaderInput(field.name, attributeCount);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
|
||||||
import com.jozufozu.flywheel.core.shader.ContextAwareProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main interface for compiling usable shaders from program specs.
|
|
||||||
* @param <P> the type of the program that this pipeline compiles.
|
|
||||||
*/
|
|
||||||
public interface ShaderPipeline<P extends WorldProgram> {
|
|
||||||
|
|
||||||
ContextAwareProgram<P> compile(ProgramSpec spec, VertexType vertexType);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
|
||||||
import com.jozufozu.flywheel.backend.source.FileResolution;
|
|
||||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
|
||||||
import com.jozufozu.flywheel.core.shader.ContextAwareProgram;
|
|
||||||
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;
|
|
||||||
|
|
||||||
public class WorldShaderPipeline<P extends WorldProgram> implements ShaderPipeline<P> {
|
|
||||||
|
|
||||||
private final ExtensibleGlProgram.Factory<P> factory;
|
|
||||||
|
|
||||||
private final Template<?> template;
|
|
||||||
private final FileResolution header;
|
|
||||||
|
|
||||||
public WorldShaderPipeline(ExtensibleGlProgram.Factory<P> factory, Template<?> template, FileResolution header) {
|
|
||||||
this.factory = factory;
|
|
||||||
this.template = template;
|
|
||||||
this.header = header;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContextAwareProgram<P> compile(ProgramSpec spec, VertexType vertexType) {
|
|
||||||
|
|
||||||
SourceFile file = spec.getSource().getFile();
|
|
||||||
|
|
||||||
ShaderCompiler shader = new ShaderCompiler(spec.name, file, template, header, vertexType);
|
|
||||||
|
|
||||||
GameStateProgram.Builder<P> builder = GameStateProgram.builder(shader.compile(this.factory));
|
|
||||||
for (ProgramState variant : spec.getStates()) {
|
|
||||||
shader.setVariant(variant);
|
|
||||||
builder.withVariant(variant.context(), shader.compile(this.factory));
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,8 +3,6 @@ package com.jozufozu.flywheel.backend.source;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.source.error.ErrorBuilder;
|
import com.jozufozu.flywheel.backend.source.error.ErrorBuilder;
|
||||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||||
|
@ -37,7 +35,6 @@ public class FileResolution {
|
||||||
return fileLoc;
|
return fileLoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public SourceFile getFile() {
|
public SourceFile getFile() {
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
@ -73,6 +70,7 @@ public class FileResolution {
|
||||||
.pointAt(span, 1);
|
.pointAt(span, 1);
|
||||||
}
|
}
|
||||||
Backend.LOGGER.error(builder.build());
|
Backend.LOGGER.error(builder.build());
|
||||||
|
throw new ShaderLoadingException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,13 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.ShaderCompiler;
|
|
||||||
import com.jozufozu.flywheel.backend.source.parse.Import;
|
import com.jozufozu.flywheel.backend.source.parse.Import;
|
||||||
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
|
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
|
||||||
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
|
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
|
||||||
import com.jozufozu.flywheel.backend.source.span.ErrorSpan;
|
import com.jozufozu.flywheel.backend.source.span.ErrorSpan;
|
||||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||||
import com.jozufozu.flywheel.backend.source.span.StringSpan;
|
import com.jozufozu.flywheel.backend.source.span.StringSpan;
|
||||||
|
import com.jozufozu.flywheel.core.pipeline.ShaderCompiler;
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
@ -138,12 +138,6 @@ public class SourceFile {
|
||||||
return "#use " + '"' + name + '"';
|
return "#use " + '"' + name + '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
public CharSequence generateFinalSource(ShaderCompiler env) {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
generateFinalSource(env, builder);
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generateFinalSource(ShaderCompiler env, StringBuilder source) {
|
public void generateFinalSource(ShaderCompiler env, StringBuilder source) {
|
||||||
for (Import include : imports) {
|
for (Import include : imports) {
|
||||||
SourceFile file = include.getFile();
|
SourceFile file = include.getFile();
|
||||||
|
@ -151,24 +145,16 @@ public class SourceFile {
|
||||||
if (file != null) file.generateFinalSource(env, source);
|
if (file != null) file.generateFinalSource(env, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = env.allocateFile(this);
|
|
||||||
source.append("#line ")
|
source.append("#line ")
|
||||||
.append(0)
|
.append(0)
|
||||||
.append(' ')
|
.append(' ')
|
||||||
.append(i)
|
.append(env.getFileID(this))
|
||||||
.append('\n');
|
.append('\n');
|
||||||
source.append(elided);
|
source.append(elided);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String printSource() {
|
public String printSource() {
|
||||||
StringBuilder builder = new StringBuilder();
|
return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers();
|
||||||
|
|
||||||
builder.append("Source for shader '")
|
|
||||||
.append(name)
|
|
||||||
.append("':\n")
|
|
||||||
.append(lines.printLinesWithNumbers());
|
|
||||||
|
|
||||||
return builder.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CharSequence elideSource(String source, List<Span> elisions) {
|
private static CharSequence elideSource(String source, List<Span> elisions) {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.pipeline.ShaderCompiler;
|
|
||||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||||
import com.jozufozu.flywheel.backend.source.SourceLines;
|
import com.jozufozu.flywheel.backend.source.SourceLines;
|
||||||
import com.jozufozu.flywheel.backend.source.error.lines.ErrorLine;
|
import com.jozufozu.flywheel.backend.source.error.lines.ErrorLine;
|
||||||
|
@ -17,6 +16,7 @@ import com.jozufozu.flywheel.backend.source.error.lines.SourceLine;
|
||||||
import com.jozufozu.flywheel.backend.source.error.lines.SpanHighlightLine;
|
import com.jozufozu.flywheel.backend.source.error.lines.SpanHighlightLine;
|
||||||
import com.jozufozu.flywheel.backend.source.error.lines.TextLine;
|
import com.jozufozu.flywheel.backend.source.error.lines.TextLine;
|
||||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||||
|
import com.jozufozu.flywheel.core.pipeline.ShaderCompiler;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
|
|
||||||
public class ErrorBuilder {
|
public class ErrorBuilder {
|
||||||
|
|
|
@ -3,11 +3,11 @@ package com.jozufozu.flywheel.core;
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.GameStateRegistry;
|
import com.jozufozu.flywheel.backend.GameStateRegistry;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.ShaderPipeline;
|
|
||||||
import com.jozufozu.flywheel.backend.pipeline.WorldShaderPipeline;
|
|
||||||
import com.jozufozu.flywheel.backend.source.FileResolution;
|
import com.jozufozu.flywheel.backend.source.FileResolution;
|
||||||
import com.jozufozu.flywheel.backend.source.Resolver;
|
import com.jozufozu.flywheel.backend.source.Resolver;
|
||||||
import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
|
import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
|
||||||
|
import com.jozufozu.flywheel.core.pipeline.PipelineCompiler;
|
||||||
|
import com.jozufozu.flywheel.core.pipeline.WorldCompiler;
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||||
import com.jozufozu.flywheel.core.shader.gamestate.NormalDebugStateProvider;
|
import com.jozufozu.flywheel.core.shader.gamestate.NormalDebugStateProvider;
|
||||||
import com.jozufozu.flywheel.event.GatherContextEvent;
|
import com.jozufozu.flywheel.event.GatherContextEvent;
|
||||||
|
@ -31,8 +31,8 @@ public class Contexts {
|
||||||
FileResolution crumblingBuiltins = Resolver.INSTANCE.findShader(ResourceUtil.subPath(Names.CRUMBLING, ".glsl"));
|
FileResolution crumblingBuiltins = Resolver.INSTANCE.findShader(ResourceUtil.subPath(Names.CRUMBLING, ".glsl"));
|
||||||
FileResolution worldBuiltins = Resolver.INSTANCE.findShader(ResourceUtil.subPath(Names.WORLD, ".glsl"));
|
FileResolution worldBuiltins = Resolver.INSTANCE.findShader(ResourceUtil.subPath(Names.WORLD, ".glsl"));
|
||||||
|
|
||||||
ShaderPipeline<CrumblingProgram> crumblingPipeline = new WorldShaderPipeline<>(CrumblingProgram::new, Templates.INSTANCING, crumblingBuiltins);
|
PipelineCompiler<CrumblingProgram> crumblingPipeline = new WorldCompiler<>(CrumblingProgram::new, Templates.INSTANCING, crumblingBuiltins);
|
||||||
ShaderPipeline<WorldProgram> worldPipeline = new WorldShaderPipeline<>(WorldProgram::new, Templates.INSTANCING, worldBuiltins);
|
PipelineCompiler<WorldProgram> worldPipeline = new WorldCompiler<>(WorldProgram::new, Templates.INSTANCING, worldBuiltins);
|
||||||
|
|
||||||
CRUMBLING = backend.register(WorldContext.builder(backend, Names.CRUMBLING).build(crumblingPipeline));
|
CRUMBLING = backend.register(WorldContext.builder(backend, Names.CRUMBLING).build(crumblingPipeline));
|
||||||
WORLD = backend.register(WorldContext.builder(backend, Names.WORLD).build(worldPipeline));
|
WORLD = backend.register(WorldContext.builder(backend, Names.WORLD).build(worldPipeline));
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package com.jozufozu.flywheel.core;
|
package com.jozufozu.flywheel.core;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
|
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.InstancingTemplateData;
|
import com.jozufozu.flywheel.core.pipeline.InstancingTemplateData;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.OneShotTemplateData;
|
import com.jozufozu.flywheel.core.pipeline.OneShotTemplateData;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.Template;
|
import com.jozufozu.flywheel.core.pipeline.Template;
|
||||||
|
|
||||||
public class Templates {
|
public class Templates {
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,13 @@ import java.util.Objects;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.shader.FlexibleShader;
|
|
||||||
import com.jozufozu.flywheel.api.struct.Instanced;
|
import com.jozufozu.flywheel.api.struct.Instanced;
|
||||||
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.ShaderContext;
|
import com.jozufozu.flywheel.backend.ShaderContext;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.LazyCompiler;
|
|
||||||
import com.jozufozu.flywheel.backend.pipeline.ShaderPipeline;
|
|
||||||
import com.jozufozu.flywheel.backend.source.ShaderLoadingException;
|
import com.jozufozu.flywheel.backend.source.ShaderLoadingException;
|
||||||
|
import com.jozufozu.flywheel.core.pipeline.CachingCompiler;
|
||||||
|
import com.jozufozu.flywheel.core.pipeline.PipelineCompiler;
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||||
|
|
||||||
|
@ -20,17 +20,16 @@ import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public class WorldContext<P extends WorldProgram> implements ShaderContext<P> {
|
public class WorldContext<P extends WorldProgram> implements ShaderContext<P> {
|
||||||
public final Backend backend;
|
public final Backend backend;
|
||||||
protected final Map<ResourceLocation, LazyCompiler<P>> programs = new HashMap<>();
|
protected final Map<ResourceLocation, ProgramSpec> programs = new HashMap<>();
|
||||||
protected final ResourceLocation name;
|
protected final ResourceLocation name;
|
||||||
protected final Supplier<Stream<ResourceLocation>> specStream;
|
protected final Supplier<Stream<ResourceLocation>> specStream;
|
||||||
|
private final CachingCompiler<P> programCache;
|
||||||
|
|
||||||
public final ShaderPipeline<P> pipeline;
|
public WorldContext(Backend backend, ResourceLocation name, Supplier<Stream<ResourceLocation>> specStream, PipelineCompiler<P> pipeline) {
|
||||||
|
|
||||||
public WorldContext(Backend backend, ResourceLocation name, Supplier<Stream<ResourceLocation>> specStream, ShaderPipeline<P> pipeline) {
|
|
||||||
this.backend = backend;
|
this.backend = backend;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.specStream = specStream;
|
this.specStream = specStream;
|
||||||
this.pipeline = pipeline;
|
this.programCache = new CachingCompiler<>(pipeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -46,7 +45,7 @@ public class WorldContext<P extends WorldProgram> implements ShaderContext<P> {
|
||||||
private void loadSpec(ProgramSpec spec) {
|
private void loadSpec(ProgramSpec spec) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
programs.put(spec.name, new LazyCompiler<>(pipeline, spec));
|
programs.put(spec.name, spec);
|
||||||
|
|
||||||
Backend.LOGGER.debug("Loaded program {}", spec.name);
|
Backend.LOGGER.debug("Loaded program {}", spec.name);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -59,14 +58,19 @@ public class WorldContext<P extends WorldProgram> implements ShaderContext<P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FlexibleShader<P> getProgramSupplier(ResourceLocation spec) {
|
public P getProgram(ResourceLocation loc, VertexType vertexType) {
|
||||||
return programs.get(spec);
|
ProgramSpec spec = programs.get(loc);
|
||||||
|
|
||||||
|
if (spec == null) {
|
||||||
|
throw new NullPointerException("Cannot compile shader because '" + loc + "' is not recognized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return programCache.getProgram(spec, vertexType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void delete() {
|
||||||
programs.values()
|
programCache.invalidate();
|
||||||
.forEach(LazyCompiler::delete);
|
|
||||||
programs.clear();
|
programs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +93,7 @@ public class WorldContext<P extends WorldProgram> implements ShaderContext<P> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <P extends WorldProgram> WorldContext<P> build(ShaderPipeline<P> pipeline) {
|
public <P extends WorldProgram> WorldContext<P> build(PipelineCompiler<P> pipeline) {
|
||||||
if (specStream == null) {
|
if (specStream == null) {
|
||||||
specStream = () -> backend.allMaterials()
|
specStream = () -> backend.allMaterials()
|
||||||
.stream()
|
.stream()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.jozufozu.flywheel.core.materials;
|
package com.jozufozu.flywheel.core.materials;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.InstanceData;
|
import com.jozufozu.flywheel.api.InstanceData;
|
||||||
|
import com.jozufozu.flywheel.util.Color;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.LightTexture;
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
|
|
||||||
|
@ -33,6 +34,15 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
|
||||||
return LightTexture.pack(this.blockLight, this.skyLight);
|
return LightTexture.pack(this.blockLight, this.skyLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BasicData setColor(Color color) {
|
||||||
|
this.r = (byte) color.getRed();
|
||||||
|
this.g = (byte) color.getGreen();
|
||||||
|
this.b = (byte) color.getBlue();
|
||||||
|
this.a = (byte) color.getAlpha();
|
||||||
|
markDirty();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public BasicData setColor(int color) {
|
public BasicData setColor(int color) {
|
||||||
return setColor(color, false);
|
return setColor(color, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||||
|
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazily compiles shader programs, caching the results.
|
||||||
|
*
|
||||||
|
* @param <P> The class that the PipelineCompiler outputs.
|
||||||
|
*/
|
||||||
|
public class CachingCompiler<P extends GlProgram> {
|
||||||
|
protected final Map<CompilationContext, P> cache = new HashMap<>();
|
||||||
|
private final PipelineCompiler<P> pipeline;
|
||||||
|
|
||||||
|
public CachingCompiler(PipelineCompiler<P> pipeline) {
|
||||||
|
this.pipeline = pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec.
|
||||||
|
*
|
||||||
|
* @param spec The ProgramSpec to target.
|
||||||
|
* @param vertexType The VertexType to target.
|
||||||
|
* @return A compiled GlProgram.
|
||||||
|
*/
|
||||||
|
public P getProgram(ProgramSpec spec, VertexType vertexType) {
|
||||||
|
return cache.computeIfAbsent(new CompilationContext(vertexType, spec, spec.getCurrentStateID()), this.pipeline::compile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidate() {
|
||||||
|
cache.values().forEach(P::delete);
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||||
|
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the entire context of a program's usage.
|
||||||
|
*
|
||||||
|
* @param vertexType The vertexType the program should be adapted for.
|
||||||
|
* @param spec The generic program name.
|
||||||
|
* @param ctx An ID representing the state at the time of usage.
|
||||||
|
*/
|
||||||
|
public record CompilationContext(VertexType vertexType, ProgramSpec spec, long ctx) {
|
||||||
|
|
||||||
|
public SourceFile getFile() {
|
||||||
|
return spec().getSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
CompilationContext that = (CompilationContext) o;
|
||||||
|
// override for instance equality on vertexType
|
||||||
|
return ctx == that.ctx && vertexType == that.vertexType && spec.equals(that.spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(vertexType, spec, ctx);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ public class InstancingTemplateData implements TemplateData {
|
||||||
.append("a_i_")
|
.append("a_i_")
|
||||||
.append(field.name)
|
.append(field.name)
|
||||||
.append(";\n");
|
.append(";\n");
|
||||||
attributeBinding += ShaderInput.from(field).attribCount;
|
attributeBinding += TypeHelper.getAttributeCount(field.type);
|
||||||
}
|
}
|
||||||
Template.prefixFields(template, interpolant, "out", "v2f_");
|
Template.prefixFields(template, interpolant, "out", "v2f_");
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main interface for compiling usable shaders from program specs.
|
||||||
|
* @param <P> the type of the program that this pipeline compiles.
|
||||||
|
*/
|
||||||
|
public interface PipelineCompiler<P extends GlProgram> {
|
||||||
|
|
||||||
|
P compile(CompilationContext vertexType);
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.GL_TRUE;
|
import static org.lwjgl.opengl.GL11.GL_TRUE;
|
||||||
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
|
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -6,15 +6,16 @@ import java.util.List;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||||
import com.jozufozu.flywheel.backend.source.FileResolution;
|
import com.jozufozu.flywheel.backend.source.FileResolution;
|
||||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||||
import com.jozufozu.flywheel.backend.source.error.ErrorBuilder;
|
import com.jozufozu.flywheel.backend.source.error.ErrorBuilder;
|
||||||
|
import com.jozufozu.flywheel.backend.source.error.ErrorReporter;
|
||||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||||
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramState;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
@ -24,27 +25,22 @@ public class ShaderCompiler {
|
||||||
public final Template<?> template;
|
public final Template<?> template;
|
||||||
private final FileResolution header;
|
private final FileResolution header;
|
||||||
|
|
||||||
@Nullable
|
private final List<String> defines;
|
||||||
private ProgramState variant;
|
|
||||||
|
|
||||||
public final VertexType vertexType;
|
public final VertexType vertexType;
|
||||||
|
|
||||||
public SourceFile mainFile;
|
public final SourceFile mainFile;
|
||||||
|
|
||||||
public ShaderCompiler(ResourceLocation name, SourceFile mainSource, Template<?> template, FileResolution header, VertexType vertexType) {
|
private final List<SourceFile> files = new ArrayList<>();
|
||||||
this.name = name;
|
|
||||||
|
public ShaderCompiler(CompilationContext usage, Template<?> template, FileResolution header) {
|
||||||
|
this.name = usage.spec().name;
|
||||||
this.template = template;
|
this.template = template;
|
||||||
this.header = header;
|
this.header = header;
|
||||||
this.mainFile = mainSource;
|
this.mainFile = usage.getFile();
|
||||||
this.vertexType = vertexType;
|
this.defines = usage.spec()
|
||||||
}
|
.getDefines(usage.ctx());
|
||||||
|
this.vertexType = usage.vertexType();
|
||||||
public ShaderCompiler setMainSource(SourceFile file) {
|
|
||||||
if (mainFile == file) return this;
|
|
||||||
|
|
||||||
mainFile = file;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlShader compile(ShaderType type) {
|
public GlShader compile(ShaderType type) {
|
||||||
|
@ -60,13 +56,10 @@ public class ShaderCompiler {
|
||||||
.append(type.define) // special case shader type declaration
|
.append(type.define) // special case shader type declaration
|
||||||
.append('\n');
|
.append('\n');
|
||||||
|
|
||||||
ProgramState variant = getVariant();
|
for (String def : defines) {
|
||||||
if (variant != null) {
|
finalSource.append("#define ")
|
||||||
for (String def : variant.defines()) {
|
.append(def)
|
||||||
finalSource.append("#define ")
|
.append('\n');
|
||||||
.append(def)
|
|
||||||
.append('\n');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == ShaderType.VERTEX) {
|
if (type == ShaderType.VERTEX) {
|
||||||
|
@ -83,9 +76,7 @@ public class ShaderCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
files.clear();
|
files.clear();
|
||||||
if (header.getFile() != null) {
|
header.getFile().generateFinalSource(this, finalSource);
|
||||||
header.getFile().generateFinalSource(this, finalSource);
|
|
||||||
}
|
|
||||||
mainFile.generateFinalSource(this, finalSource);
|
mainFile.generateFinalSource(this, finalSource);
|
||||||
|
|
||||||
template.getMetadata(mainFile).generateFooter(finalSource, type, this);
|
template.getMetadata(mainFile).generateFooter(finalSource, type, this);
|
||||||
|
@ -93,18 +84,21 @@ public class ShaderCompiler {
|
||||||
return new GlShader(this, type, finalSource.toString());
|
return new GlShader(this, type, finalSource.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public <P extends WorldProgram> P compile(ExtensibleGlProgram.Factory<P> worldShaderPipeline) {
|
||||||
public ProgramState getVariant() {
|
return new ProgramAssembler(this.name)
|
||||||
return variant;
|
.attachShader(compile(ShaderType.VERTEX))
|
||||||
|
.attachShader(compile(ShaderType.FRAGMENT))
|
||||||
|
.link()
|
||||||
|
.deleteLinkedShaders()
|
||||||
|
.build(worldShaderPipeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVariant(@Nullable ProgramState variant) {
|
/**
|
||||||
this.variant = variant;
|
* 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 final List<SourceFile> files = new ArrayList<>();
|
*/
|
||||||
|
public int getFileID(SourceFile sourceFile) {
|
||||||
public int allocateFile(SourceFile sourceFile) {
|
|
||||||
int i = files.indexOf(sourceFile);
|
int i = files.indexOf(sourceFile);
|
||||||
if (i != -1) {
|
if (i != -1) {
|
||||||
return i;
|
return i;
|
||||||
|
@ -121,8 +115,32 @@ public class ShaderCompiler {
|
||||||
return file.getLineSpanNoWhitespace(lineNo);
|
return file.getLineSpanNoWhitespace(lineNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void printShaderInfoLog(String source, String log, ResourceLocation name) {
|
||||||
|
List<String> lines = log.lines()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
boolean needsSourceDump = false;
|
||||||
|
|
||||||
|
StringBuilder errors = new StringBuilder();
|
||||||
|
for (String line : lines) {
|
||||||
|
ErrorBuilder builder = parseCompilerError(line);
|
||||||
|
|
||||||
|
if (builder != null) {
|
||||||
|
errors.append(builder.build());
|
||||||
|
} else {
|
||||||
|
errors.append(line).append('\n');
|
||||||
|
needsSourceDump = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Backend.LOGGER.error("Errors compiling '" + name + "': \n" + errors);
|
||||||
|
if (needsSourceDump) {
|
||||||
|
// TODO: generated code gets its own "file"
|
||||||
|
ErrorReporter.printLines(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public ErrorBuilder parseCompilerError(String line) {
|
private ErrorBuilder parseCompilerError(String line) {
|
||||||
try {
|
try {
|
||||||
ErrorBuilder error = ErrorBuilder.fromLogLine(this, line);
|
ErrorBuilder error = ErrorBuilder.fromLogLine(this, line);
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
|
@ -133,13 +151,4 @@ public class ShaderCompiler {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <P extends WorldProgram> P compile(ExtensibleGlProgram.Factory<P> worldShaderPipeline) {
|
|
||||||
return new ProgramAssembler(this.name)
|
|
||||||
.attachShader(compile(ShaderType.VERTEX))
|
|
||||||
.attachShader(compile(ShaderType.FRAGMENT))
|
|
||||||
.link()
|
|
||||||
.deleteLinkedShaders()
|
|
||||||
.build(worldShaderPipeline);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||||
|
import com.jozufozu.flywheel.backend.source.FileResolution;
|
||||||
|
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
||||||
|
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||||
|
|
||||||
|
public class WorldCompiler<P extends WorldProgram> implements PipelineCompiler<P> {
|
||||||
|
|
||||||
|
private final ExtensibleGlProgram.Factory<P> factory;
|
||||||
|
|
||||||
|
private final Template<?> template;
|
||||||
|
private final FileResolution header;
|
||||||
|
|
||||||
|
public WorldCompiler(ExtensibleGlProgram.Factory<P> factory, Template<?> template, FileResolution header) {
|
||||||
|
this.factory = factory;
|
||||||
|
this.template = template;
|
||||||
|
this.header = header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public P compile(CompilationContext usage) {
|
||||||
|
ShaderCompiler compiler = new ShaderCompiler(usage, template, header);
|
||||||
|
|
||||||
|
return new ProgramAssembler(compiler.name)
|
||||||
|
.attachShader(compiler.compile(ShaderType.VERTEX))
|
||||||
|
.attachShader(compiler.compile(ShaderType.FRAGMENT))
|
||||||
|
.link()
|
||||||
|
.deleteLinkedShaders()
|
||||||
|
.build(this.factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
package com.jozufozu.flywheel.core.pipeline;
|
||||||
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.shader;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encapsulates any number of shader programs for use in similar contexts.
|
|
||||||
* Allows the implementor to choose which shader program to use based on arbitrary state.
|
|
||||||
*
|
|
||||||
* @param <P>
|
|
||||||
*/
|
|
||||||
public interface ContextAwareProgram<P extends GlProgram> extends Supplier<P> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the shader program most suited for the current game state.
|
|
||||||
*
|
|
||||||
* @return The one true program.
|
|
||||||
*/
|
|
||||||
P get();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete all shader programs encapsulated by your implementation.
|
|
||||||
*/
|
|
||||||
void delete();
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.shader;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.spec.IGameStateCondition;
|
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
|
||||||
|
|
||||||
public class GameStateProgram<P extends GlProgram> implements ContextAwareProgram<P> {
|
|
||||||
|
|
||||||
private final List<Pair<IGameStateCondition, P>> variants;
|
|
||||||
private final P fallback;
|
|
||||||
|
|
||||||
protected GameStateProgram(List<Pair<IGameStateCondition, P>> variants, P fallback) {
|
|
||||||
this.variants = variants;
|
|
||||||
this.fallback = fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public P get() {
|
|
||||||
for (Pair<IGameStateCondition, P> variant : variants) {
|
|
||||||
if (variant.first()
|
|
||||||
.isMet()) return variant.second();
|
|
||||||
}
|
|
||||||
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete() {
|
|
||||||
for (Pair<IGameStateCondition, P> variant : variants) {
|
|
||||||
variant.second()
|
|
||||||
.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
fallback.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <P extends GlProgram> Builder<P> builder(P fallback) {
|
|
||||||
return new Builder<>(fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder<P extends GlProgram> {
|
|
||||||
private final P fallback;
|
|
||||||
private final List<Pair<IGameStateCondition, P>> variants = new ArrayList<>();
|
|
||||||
|
|
||||||
public Builder(P fallback) {
|
|
||||||
this.fallback = fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder<P> withVariant(IGameStateCondition condition, P program) {
|
|
||||||
variants.add(Pair.of(condition, program));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContextAwareProgram<P> build() {
|
|
||||||
return new GameStateProgram<>(ImmutableList.copyOf(variants), fallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,5 +11,5 @@ public interface IGameStateProvider {
|
||||||
|
|
||||||
ResourceLocation getID();
|
ResourceLocation getID();
|
||||||
|
|
||||||
Object getValue();
|
boolean isTrue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,10 @@ package com.jozufozu.flywheel.core.shader.gamestate;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.config.FlwConfig;
|
import com.jozufozu.flywheel.config.FlwConfig;
|
||||||
import com.jozufozu.flywheel.core.shader.spec.IBooleanStateProvider;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public class NormalDebugStateProvider implements IBooleanStateProvider {
|
public class NormalDebugStateProvider implements IGameStateProvider {
|
||||||
|
|
||||||
public static final NormalDebugStateProvider INSTANCE = new NormalDebugStateProvider();
|
public static final NormalDebugStateProvider INSTANCE = new NormalDebugStateProvider();
|
||||||
public static final ResourceLocation NAME = Flywheel.rl("normal_debug");
|
public static final ResourceLocation NAME = Flywheel.rl("normal_debug");
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.shader.spec;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.shader.gamestate.IGameStateProvider;
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
public class BooleanGameStateCondition implements IGameStateCondition {
|
|
||||||
|
|
||||||
public static final Codec<BooleanGameStateCondition> BOOLEAN_SUGAR = IGameStateProvider.CODEC.xmap(gameContext -> {
|
|
||||||
if (gameContext instanceof IBooleanStateProvider) {
|
|
||||||
return new BooleanGameStateCondition(((IBooleanStateProvider) gameContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}, IGameStateCondition::getStateProvider);
|
|
||||||
protected final IBooleanStateProvider context;
|
|
||||||
|
|
||||||
public BooleanGameStateCondition(IBooleanStateProvider context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation getID() {
|
|
||||||
return context.getID();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IGameStateProvider getStateProvider() {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMet() {
|
|
||||||
return context.isTrue();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.shader.spec;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.shader.gamestate.IGameStateProvider;
|
|
||||||
|
|
||||||
public interface IBooleanStateProvider extends IGameStateProvider {
|
|
||||||
|
|
||||||
boolean isTrue();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default Boolean getValue() {
|
|
||||||
return isTrue();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.shader.spec;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.shader.gamestate.IGameStateProvider;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
public interface IGameStateCondition {
|
|
||||||
|
|
||||||
ResourceLocation getID();
|
|
||||||
|
|
||||||
IGameStateProvider getStateProvider();
|
|
||||||
|
|
||||||
boolean isMet();
|
|
||||||
}
|
|
|
@ -1,8 +1,10 @@
|
||||||
package com.jozufozu.flywheel.core.shader.spec;
|
package com.jozufozu.flywheel.core.shader.spec;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.backend.source.FileResolution;
|
import com.jozufozu.flywheel.backend.source.FileResolution;
|
||||||
import com.jozufozu.flywheel.backend.source.Resolver;
|
import com.jozufozu.flywheel.backend.source.Resolver;
|
||||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||||
|
@ -38,11 +40,11 @@ public class ProgramSpec {
|
||||||
public ResourceLocation name;
|
public ResourceLocation name;
|
||||||
public final FileResolution source;
|
public final FileResolution source;
|
||||||
|
|
||||||
public final List<ProgramState> states;
|
public final ImmutableList<ProgramState> states;
|
||||||
|
|
||||||
public ProgramSpec(ResourceLocation source, List<ProgramState> states) {
|
public ProgramSpec(ResourceLocation source, List<ProgramState> states) {
|
||||||
this.source = Resolver.INSTANCE.findShader(source);
|
this.source = Resolver.INSTANCE.findShader(source);
|
||||||
this.states = states;
|
this.states = ImmutableList.copyOf(states);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(ResourceLocation name) {
|
public void setName(ResourceLocation name) {
|
||||||
|
@ -53,12 +55,40 @@ public class ProgramSpec {
|
||||||
return source.getFileLoc();
|
return source.getFileLoc();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileResolution getSource() {
|
public SourceFile getSource() {
|
||||||
return source;
|
return source.getFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ProgramState> getStates() {
|
public ImmutableList<ProgramState> getStates() {
|
||||||
return states;
|
return states;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate a unique ID representing the current game state.
|
||||||
|
*/
|
||||||
|
public long getCurrentStateID() {
|
||||||
|
long ctx = 0;
|
||||||
|
for (ProgramState state : states) {
|
||||||
|
if (state.context().isTrue()) {
|
||||||
|
ctx |= 1;
|
||||||
|
}
|
||||||
|
ctx <<= 1;
|
||||||
|
}
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the stateID, get a list of defines to include at the top of a compiling program.
|
||||||
|
*/
|
||||||
|
public List<String> getDefines(long stateID) {
|
||||||
|
List<String> defines = new ArrayList<>();
|
||||||
|
|
||||||
|
for (ProgramState state : states) {
|
||||||
|
if ((stateID & 1) == 1) {
|
||||||
|
defines.addAll(state.defines());
|
||||||
|
}
|
||||||
|
stateID >>= 1;
|
||||||
|
}
|
||||||
|
return defines;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,29 +3,14 @@ package com.jozufozu.flywheel.core.shader.spec;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.core.shader.gamestate.IGameStateProvider;
|
||||||
import com.jozufozu.flywheel.util.CodecUtil;
|
import com.jozufozu.flywheel.util.CodecUtil;
|
||||||
import com.mojang.datafixers.util.Either;
|
|
||||||
import com.mojang.serialization.Codec;
|
import com.mojang.serialization.Codec;
|
||||||
import com.mojang.serialization.DataResult;
|
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
public record ProgramState(IGameStateCondition context, List<String> defines) {
|
public record ProgramState(IGameStateProvider context, List<String> defines) {
|
||||||
|
|
||||||
// TODO: Use Codec.dispatch
|
public static final Codec<ProgramState> CODEC = RecordCodecBuilder.create(state -> state.group(IGameStateProvider.CODEC.fieldOf("when")
|
||||||
private static final Codec<IGameStateCondition> WHEN = Codec.either(BooleanGameStateCondition.BOOLEAN_SUGAR, SpecificValueCondition.CODEC)
|
|
||||||
.flatXmap(either -> either.map(DataResult::success, DataResult::success), any -> {
|
|
||||||
if (any instanceof BooleanGameStateCondition) {
|
|
||||||
return DataResult.success(Either.left((BooleanGameStateCondition) any));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (any instanceof SpecificValueCondition) {
|
|
||||||
return DataResult.success(Either.right((SpecificValueCondition) any));
|
|
||||||
}
|
|
||||||
|
|
||||||
return DataResult.error("unknown context condition");
|
|
||||||
});
|
|
||||||
|
|
||||||
public static final Codec<ProgramState> CODEC = RecordCodecBuilder.create(state -> state.group(WHEN.fieldOf("when")
|
|
||||||
.forGetter(ProgramState::context), CodecUtil.oneOrMore(Codec.STRING)
|
.forGetter(ProgramState::context), CodecUtil.oneOrMore(Codec.STRING)
|
||||||
.optionalFieldOf("define", Collections.emptyList())
|
.optionalFieldOf("define", Collections.emptyList())
|
||||||
.forGetter(ProgramState::defines))
|
.forGetter(ProgramState::defines))
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.shader.spec;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.shader.gamestate.IGameStateProvider;
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
public class SpecificValueCondition implements IGameStateCondition {
|
|
||||||
|
|
||||||
public static final Codec<SpecificValueCondition> CODEC = RecordCodecBuilder.create(condition -> condition.group(IGameStateProvider.CODEC.fieldOf("provider")
|
|
||||||
.forGetter(SpecificValueCondition::getStateProvider), Codec.STRING.fieldOf("value")
|
|
||||||
.forGetter(SpecificValueCondition::getValue))
|
|
||||||
.apply(condition, SpecificValueCondition::new));
|
|
||||||
|
|
||||||
private final String required;
|
|
||||||
private final IGameStateProvider context;
|
|
||||||
|
|
||||||
public SpecificValueCondition(IGameStateProvider context, String required) {
|
|
||||||
this.required = required;
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation getID() {
|
|
||||||
return context.getID();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return required;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IGameStateProvider getStateProvider() {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMet() {
|
|
||||||
return required.equals(context.getValue()
|
|
||||||
.toString());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,10 +2,7 @@
|
||||||
"source": "flywheel:model.vert",
|
"source": "flywheel:model.vert",
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"when": {
|
"when": "flywheel:normal_debug",
|
||||||
"provider": "flywheel:normal_debug",
|
|
||||||
"value": "true"
|
|
||||||
},
|
|
||||||
"define": "DEBUG_NORMAL"
|
"define": "DEBUG_NORMAL"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,10 +2,7 @@
|
||||||
"source": "flywheel:oriented.vert",
|
"source": "flywheel:oriented.vert",
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"when": {
|
"when": "flywheel:normal_debug",
|
||||||
"provider": "flywheel:normal_debug",
|
|
||||||
"value": "true"
|
|
||||||
},
|
|
||||||
"define": "DEBUG_NORMAL"
|
"define": "DEBUG_NORMAL"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue