A heck of a lot

- Reorganize everything
 - Isolate SourceFile related things
 - Should consider decoupling ShaderLoader from resource loading
 - Document a lot of newer things
 - Index functions
 - Awkward WorldContext builder
 - Template responsible for providing shader inputs
 - Template is now an abstract class
 - Template provides GLSL version
 - ProgramSpecs now only accept one file
This commit is contained in:
Jozufozu 2021-08-10 02:06:22 -07:00
parent 3c24abe837
commit 82ea5b1720
48 changed files with 668 additions and 559 deletions

View file

@ -16,6 +16,7 @@ import org.lwjgl.opengl.GLCapabilities;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.backend.instancing.InstanceData;
import com.jozufozu.flywheel.backend.material.MaterialSpec;
import com.jozufozu.flywheel.backend.source.ShaderSources;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
@ -59,14 +60,6 @@ public class Backend {
OptifineHandler.init();
}
void clearContexts() {
SpecMetaRegistry.clear();
programSpecRegistry.clear();
contexts.forEach(IShaderContext::delete);
contexts.clear();
materialRegistry.clear();
}
/**
* Get a string describing the Flywheel backend. When there are eventually multiple backends
* (Meshlet, MDI, GL31 Draw Instanced are planned), this will name which one is in use.
@ -98,7 +91,7 @@ public class Backend {
/**
* Register a shader context.
*/
public <C extends ShaderContext<?>> C register(C spec) {
public <C extends IShaderContext<?>> C register(C spec) {
contexts.add(spec);
return spec;
}
@ -201,6 +194,17 @@ public class Backend {
RenderWork.enqueue(Minecraft.getInstance().levelRenderer::allChanged);
}
/**
* INTERNAL USE ONLY
*/
public void _clearContexts() {
SpecMetaRegistry.clear();
programSpecRegistry.clear();
contexts.forEach(IShaderContext::delete);
contexts.clear();
materialRegistry.clear();
}
public static void init() {
}
}

View file

@ -1,56 +0,0 @@
package com.jozufozu.flywheel.backend;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
import com.jozufozu.flywheel.backend.pipeline.error.ErrorBuilder;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
import net.minecraft.util.ResourceLocation;
public class FileResolution {
private final List<Span> foundSpans = new ArrayList<>();
private final ResourceLocation fileLoc;
private SourceFile file;
public FileResolution(ResourceLocation fileLoc) {
this.fileLoc = fileLoc;
}
public ResourceLocation getFileLoc() {
return fileLoc;
}
@Nullable
public SourceFile getFile() {
return file;
}
public void addSpan(Span span) {
foundSpans.add(span);
}
public void resolve(ShaderSources sources) {
try {
file = sources.source(fileLoc);
} catch (RuntimeException error) {
ErrorBuilder builder = new ErrorBuilder();
builder.error(String.format("could not find source for file %s", fileLoc));
for (Span span : foundSpans) {
builder.in(span.getSourceFile())
.pointAt(span, 2);
}
Backend.log.error(builder.build());
}
}
void invalidate() {
}
}

View file

@ -1,34 +0,0 @@
package com.jozufozu.flywheel.backend;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
import net.minecraft.util.ResourceLocation;
/**
* Indexes many shader source definitions to allow for error fix suggestions.
*/
public class Index {
private final Multimap<String, ShaderStruct> knownNames = MultimapBuilder.hashKeys().hashSetValues().build();
public Index(Map<ResourceLocation, SourceFile> sources) {
Collection<SourceFile> files = sources.values();
for (SourceFile file : files) {
file.getStructs().forEach(knownNames::put);
}
}
public Collection<ShaderStruct> getStructDefinitionsMatching(CharSequence name) {
return knownNames.get(name.toString());
}
}

View file

@ -1,33 +0,0 @@
package com.jozufozu.flywheel.backend;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.shader.IMultiProgram;
import net.minecraft.util.ResourceLocation;
public abstract class ShaderContext<P extends GlProgram> implements IShaderContext<P> {
protected final Map<ResourceLocation, IMultiProgram<P>> programs = new HashMap<>();
public final Backend backend;
public ShaderContext(Backend backend) {
this.backend = backend;
}
@Override
public Supplier<P> getProgramSupplier(ResourceLocation spec) {
return programs.get(spec);
}
@Override
public void delete() {
programs.values()
.forEach(IMultiProgram::delete);
programs.clear();
}
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.pipeline;
package com.jozufozu.flywheel.backend.gl;
public enum GLSLVersion {
V110(110),

View file

@ -152,6 +152,14 @@ public abstract class InstanceManager<T> implements MaterialManager.OriginShiftL
}
}
public void queueUpdate(T obj) {
if (!Backend.getInstance()
.canUseInstancing()) return;
synchronized (queuedUpdates) {
queuedUpdates.add(obj);
}
}
/**
* Update the instance associated with an object.
*
@ -185,14 +193,6 @@ public abstract class InstanceManager<T> implements MaterialManager.OriginShiftL
}
}
public void queueUpdate(T obj) {
if (!Backend.getInstance()
.canUseInstancing()) return;
synchronized (queuedUpdates) {
queuedUpdates.add(obj);
}
}
public void onLightUpdate(T obj) {
if (!Backend.getInstance()
.canUseInstancing()) return;

View file

@ -27,6 +27,18 @@ public class InstancedRenderDispatcher {
private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>($ -> new InstanceWorld());
/**
* Call this when you want to invoke
* @param te
*/
public static void enqueueUpdate(TileEntity te) {
getTiles(te.getLevel()).queueUpdate(te);
}
public static void enqueueUpdate(Entity entity) {
getEntities(entity.level).queueUpdate(entity);
}
@Nonnull
public static InstanceManager<TileEntity> getTiles(IWorld world) {
return instanceWorlds.get(world)
@ -52,14 +64,6 @@ public class InstancedRenderDispatcher {
instanceWorlds.get(world).tick();
}
public static void enqueueUpdate(TileEntity te) {
getTiles(te.getLevel()).queueUpdate(te);
}
public static void enqueueUpdate(Entity entity) {
getEntities(entity.level).queueUpdate(entity);
}
@SubscribeEvent
public static void onBeginFrame(BeginFrameEvent event) {
instanceWorlds.get(event.getWorld()).beginFrame(event);

View file

@ -4,6 +4,10 @@ import com.jozufozu.flywheel.core.shader.IMultiProgram;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
/**
* The main interface for compiling usable shaders from program specs.
* @param <P> the type of the program that this pipeline compiles.
*/
public interface IShaderPipeline<P extends WorldProgram> {
IMultiProgram<P> compile(ProgramSpec spec);

View file

@ -1,41 +0,0 @@
package com.jozufozu.flywheel.backend.pipeline;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.loading.ProtoProgram;
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
import com.jozufozu.flywheel.backend.pipeline.parse.StructField;
public interface ITemplate {
void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file);
void attachAttributes(ProtoProgram program, SourceFile file);
static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) {
ImmutableList<StructField> fields = struct.getFields();
for (StructField field : fields) {
builder.append(qualifier)
.append(' ')
.append(field.type)
.append(' ')
.append(prefix)
.append(field.name)
.append(";\n");
}
}
static void assignFields(StringBuilder builder, ShaderStruct struct, String prefix1, String prefix2) {
ImmutableList<StructField> fields = struct.getFields();
for (StructField field : fields) {
builder.append(prefix1)
.append(field.name)
.append(" = ")
.append(prefix2)
.append(field.name)
.append(";\n");
}
}
}

View file

@ -1,40 +0,0 @@
package com.jozufozu.flywheel.backend.pipeline;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.pipeline.parse.Import;
import net.minecraft.util.ResourceLocation;
public class Includer {
public static List<SourceFile> recurseIncludes(SourceFile from) {
Set<ResourceLocation> seen = new HashSet<>();
seen.add(from.name);
List<SourceFile> out = new ArrayList<>();
process(seen, out, from);
return out;
}
private static void process(Set<ResourceLocation> seen, List<SourceFile> out, SourceFile source) {
ImmutableList<Import> imports = source.getIncludes();
for (Import use : imports) {
SourceFile file = use.getFile();
if (file != null && seen.add(file.name)) {
process(seen, out, file);
out.add(file);
}
}
}
}

View file

@ -3,13 +3,14 @@ package com.jozufozu.flywheel.backend.pipeline;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction;
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
import com.jozufozu.flywheel.backend.pipeline.parse.Variable;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
import com.jozufozu.flywheel.backend.source.SourceFile;
import com.jozufozu.flywheel.backend.source.error.ErrorReporter;
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
import com.jozufozu.flywheel.backend.source.parse.Variable;
import com.jozufozu.flywheel.backend.source.span.Span;
public class InstanceTemplateData {
public class InstancingProgramMetaData {
public final SourceFile file;
public final ShaderFunction vertexMain;
@ -21,15 +22,14 @@ public class InstanceTemplateData {
public final ShaderStruct vertex;
public final ShaderStruct instance;
public InstanceTemplateData(SourceFile file) {
public InstancingProgramMetaData(SourceFile file) {
this.file = file;
Optional<ShaderFunction> vertexFunc = file.findFunction("vertex");
Optional<ShaderFunction> fragmentFunc = file.findFunction("fragment");
if (!fragmentFunc.isPresent()) {
ErrorReporter.generateFileError(file, "could not find \"fragment\" function");
ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined");
}
if (!vertexFunc.isPresent()) {
ErrorReporter.generateFileError(file, "could not find \"vertex\" function");
@ -64,15 +64,15 @@ public class InstanceTemplateData {
Optional<ShaderStruct> maybeInstance = file.findStruct(instanceName);
if (!maybeVertex.isPresent()) {
ErrorReporter.generateMissingStruct(file, vertexName);
ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined");
}
if (!maybeInterpolant.isPresent()) {
ErrorReporter.generateMissingStruct(file, interpolantName);
ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined");
}
if (!maybeInstance.isPresent()) {
ErrorReporter.generateMissingStruct(file, instanceName);
ErrorReporter.generateMissingStruct(file, instanceName, "struct not defined");
}
if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent() || !maybeInstance.isPresent()) {

View file

@ -1,16 +1,19 @@
package com.jozufozu.flywheel.backend.pipeline;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.loading.ProtoProgram;
import com.jozufozu.flywheel.backend.source.SourceFile;
public class InstancingTemplate implements ITemplate {
public class InstancingTemplate extends Template<InstancingProgramMetaData> {
public static final InstancingTemplate INSTANCE = new InstancingTemplate();
private final Map<SourceFile, InstanceTemplateData> datas = new HashMap<>();
public InstancingTemplate() {
super(InstancingProgramMetaData::new);
}
@Override
public void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file) {
@ -22,51 +25,50 @@ public class InstancingTemplate implements ITemplate {
}
@Override
public void attachAttributes(ProtoProgram program, SourceFile file) {
InstanceTemplateData data = getData(file);
data.vertex.addPrefixedAttributes(program, "a_v_");
data.instance.addPrefixedAttributes(program, "a_i_");
}
public Collection<ShaderInput> getShaderInputs(SourceFile file) {
InstancingProgramMetaData data = getMetadata(file);
public InstanceTemplateData getData(SourceFile file) {
return datas.computeIfAbsent(file, InstanceTemplateData::new);
List<ShaderInput> inputs = new ArrayList<>(ShaderInput.fromStruct(data.vertex, "a_v_"));
inputs.addAll(ShaderInput.fromStruct(data.instance, "a_i_"));
return inputs;
}
public void vertexFooter(StringBuilder template, SourceFile file) {
InstanceTemplateData data = getData(file);
InstancingProgramMetaData data = getMetadata(file);
ITemplate.prefixFields(template, data.vertex, "attribute", "a_v_");
ITemplate.prefixFields(template, data.instance, "attribute", "a_i_");
ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_");
Template.prefixFields(template, data.vertex, "attribute", "a_v_");
Template.prefixFields(template, data.instance, "attribute", "a_i_");
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
template.append("void main() {\n");
template.append(data.vertexName)
.append(" v;\n");
ITemplate.assignFields(template, data.vertex, "v.", "a_v_");
Template.assignFields(template, data.vertex, "v.", "a_v_");
template.append(data.instanceName)
.append(" i;\n");
ITemplate.assignFields(template, data.instance, "i.", "a_i_");
Template.assignFields(template, data.instance, "i.", "a_i_");
template.append(data.interpolantName)
.append(" o = ")
.append(data.vertexMain.call("v", "i"))
.append(";\n");
ITemplate.assignFields(template, data.interpolant, "v2f_", "o.");
Template.assignFields(template, data.interpolant, "v2f_", "o.");
template.append('}');
}
public void fragmentFooter(StringBuilder template, SourceFile file) {
InstanceTemplateData data = getData(file);
InstancingProgramMetaData data = getMetadata(file);
ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_");
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
template.append("void main() {\n");
template.append(data.interpolantName)
.append(" o;\n");
ITemplate.assignFields(template, data.interpolant, "o.", "v2f_");
Template.assignFields(template, data.interpolant, "o.", "v2f_");
template.append(data.fragmentMain.call("o"))
.append(";\n");

View file

@ -3,13 +3,14 @@ package com.jozufozu.flywheel.backend.pipeline;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction;
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
import com.jozufozu.flywheel.backend.pipeline.parse.Variable;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
import com.jozufozu.flywheel.backend.source.SourceFile;
import com.jozufozu.flywheel.backend.source.error.ErrorReporter;
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
import com.jozufozu.flywheel.backend.source.parse.Variable;
import com.jozufozu.flywheel.backend.source.span.Span;
public class OneShotData {
public class OneShotProgramMetaData {
public final SourceFile file;
public final ShaderFunction vertexMain;
@ -19,7 +20,7 @@ public class OneShotData {
public final ShaderStruct vertex;
public final ShaderFunction fragmentMain;
public OneShotData(SourceFile file) {
public OneShotProgramMetaData(SourceFile file) {
this.file = file;
Optional<ShaderFunction> maybeVertexMain = file.findFunction("vertex");
@ -27,29 +28,32 @@ public class OneShotData {
if (!maybeVertexMain.isPresent()) {
ErrorReporter.generateFileError(file, "could not find \"vertex\" function");
throw new RuntimeException();
}
if (!maybeFragmentMain.isPresent()) {
ErrorReporter.generateFileError(file, "could not find \"fragment\" function");
ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined");
}
if (!maybeVertexMain.isPresent() || !maybeFragmentMain.isPresent()) {
throw new RuntimeException();
}
vertexMain = maybeVertexMain.get();
fragmentMain = maybeFragmentMain.get();
ImmutableList<Variable> parameters = fragmentMain.getParameters();
ImmutableList<Variable> fragmentParameters = fragmentMain.getParameters();
ImmutableList<Variable> vertexParameters = vertexMain.getParameters();
if (vertexParameters.size() != 1) {
ErrorReporter.generateSpanError(vertexMain.getArgs(), "a basic model requires vertex function to have one argument");
throw new RuntimeException();
}
if (parameters.size() != 1) {
ErrorReporter.generateSpanError(fragmentMain.getArgs(), "instancing requires fragment function to have 1 argument");
throw new RuntimeException();
if (fragmentParameters.size() != 1) {
ErrorReporter.generateSpanError(fragmentMain.getArgs(), "fragment function must have exactly one argument");
}
if (vertexParameters.size() != 1 || fragmentParameters.size() != 1) {
throw new RuntimeException();
}
interpolantName = vertexMain.getType();
vertexName = vertexParameters.get(0)
@ -59,10 +63,10 @@ public class OneShotData {
Optional<ShaderStruct> maybeVertex = file.findStruct(vertexName);
if (!maybeVertex.isPresent())
ErrorReporter.generateMissingStruct(file, vertexName);
ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined");
if (!maybeInterpolant.isPresent())
ErrorReporter.generateMissingStruct(file, interpolantName);
ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined");
if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent()) {
throw new RuntimeException();

View file

@ -1,18 +1,17 @@
package com.jozufozu.flywheel.backend.pipeline;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Collection;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.loading.ProtoProgram;
import com.jozufozu.flywheel.backend.source.SourceFile;
public class OneShotTemplate implements ITemplate {
public class OneShotTemplate extends Template<OneShotProgramMetaData> {
public static final OneShotTemplate INSTANCE = new OneShotTemplate();
private final Map<SourceFile, OneShotData> datas = new HashMap<>();
public OneShotTemplate() {
super(OneShotProgramMetaData::new);
}
@Override
public void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file) {
@ -24,45 +23,42 @@ public class OneShotTemplate implements ITemplate {
}
@Override
public void attachAttributes(ProtoProgram program, SourceFile file) {
OneShotData data = getData(file);
data.vertex.addPrefixedAttributes(program, "a_v_");
}
public Collection<ShaderInput> getShaderInputs(SourceFile file) {
OneShotProgramMetaData data = getMetadata(file);
public OneShotData getData(SourceFile file) {
return datas.computeIfAbsent(file, OneShotData::new);
return ShaderInput.fromStruct(data.vertex, "a_v_");
}
public void vertexFooter(StringBuilder template, SourceFile file) {
OneShotData data = getData(file);
OneShotProgramMetaData data = getMetadata(file);
ITemplate.prefixFields(template, data.vertex, "attribute", "a_v_");
ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_");
Template.prefixFields(template, data.vertex, "attribute", "a_v_");
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
template.append("void main() {\n");
template.append(data.vertexName)
.append(" v;\n");
ITemplate.assignFields(template, data.vertex, "v.", "a_v_");
Template.assignFields(template, data.vertex, "v.", "a_v_");
template.append(data.interpolantName)
.append(" o = ")
.append(data.vertexMain.call("v"))
.append(";\n");
ITemplate.assignFields(template, data.interpolant, "v2f_", "o.");
Template.assignFields(template, data.interpolant, "v2f_", "o.");
template.append('}');
}
public void fragmentFooter(StringBuilder template, SourceFile file) {
OneShotData data = getData(file);
OneShotProgramMetaData data = getMetadata(file);
ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_");
Template.prefixFields(template, data.interpolant, "varying", "v2f_");
template.append("void main() {\n");
template.append(data.interpolant.name)
.append(" o;\n");
ITemplate.assignFields(template, data.interpolant, "o.", "v2f_");
Template.assignFields(template, data.interpolant, "o.", "v2f_");
template.append(data.fragmentMain.call("o"))
.append(";\n");

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.loading;
package com.jozufozu.flywheel.backend.pipeline;
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
import static org.lwjgl.opengl.GL20.GL_TRUE;
@ -9,53 +9,48 @@ import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
import static org.lwjgl.opengl.GL20.glGetProgrami;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import java.util.EnumMap;
import java.util.Map;
import java.util.function.IntConsumer;
import org.lwjgl.opengl.GL20;
import java.util.List;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import net.minecraft.util.ResourceLocation;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
public class ProtoProgram {
public final int program;
public final WorldShader parent;
public ResourceLocation name;
private int attributeIndex;
private final IntList shaders;
private final List<GlShader> shaders;
public ProtoProgram() {
public ProtoProgram(WorldShader parent) {
this.parent = parent;
this.program = glCreateProgram();
shaders = new IntArrayList(2);
shaders = new ObjectArrayList<>();
}
public void attachShader(GlShader glShader) {
glAttachShader(this.program, glShader.handle());
}
public void addAttribute(String name, int attributeCount) {
glBindAttribLocation(this.program, attributeIndex, name);
attributeIndex += attributeCount;
public ProtoProgram compilePart(ShaderType type) {
GlShader shader = parent.compile(type);
attachShader(shader);
return this;
}
/**
* Links the attached shaders to this program.
*/
public ProtoProgram link(ResourceLocation name) {
this.name = name;
public ProtoProgram link() {
parent.template.getShaderInputs(parent.mainFile)
.forEach(this::addAttribute);
glLinkProgram(this.program);
String log = glGetProgramInfoLog(this.program);
if (!log.isEmpty()) {
Backend.log.debug("Program link log for " + name + ": " + log);
Backend.log.debug("Program link log for " + parent.name + ": " + log);
}
int result = glGetProgrami(this.program, GL_LINK_STATUS);
@ -68,7 +63,18 @@ public class ProtoProgram {
}
public ProtoProgram deleteLinkedShaders() {
shaders.forEach((IntConsumer) GL20::glDeleteShader);
shaders.forEach(GlShader::delete);
return this;
}
private void attachShader(GlShader glShader) {
shaders.add(glShader);
glAttachShader(this.program, glShader.handle());
}
private void addAttribute(ShaderInput shaderInput) {
glBindAttribLocation(this.program, attributeIndex, shaderInput.name);
attributeIndex += shaderInput.attribCount;
}
}

View file

@ -0,0 +1,38 @@
package com.jozufozu.flywheel.backend.pipeline;
import java.util.Collection;
import java.util.stream.Collectors;
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
import com.jozufozu.flywheel.backend.source.parse.StructField;
/**
* A single input to a shader.
*/
public class ShaderInput {
public final CharSequence name;
public final int attribCount;
public ShaderInput(CharSequence name, int attribCount) {
this.name = name;
this.attribCount = attribCount;
}
public ShaderInput withPrefix(CharSequence prefix) {
return new ShaderInput(prefix.toString() + name, attribCount);
}
public static Collection<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);
}
}

View file

@ -0,0 +1,82 @@
package com.jozufozu.flywheel.backend.pipeline;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.source.SourceFile;
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
import com.jozufozu.flywheel.backend.source.parse.StructField;
/**
* A class that generates glsl glue code given a SourceFile.
*
* <p>
* Shader files are written somewhat abstractly. Subclasses of Template handle those abstractions, using SourceFile
* metadata to generate shader code that OpenGL can use to call into our shader programs.
* </p>
* @param <D> Holds metadata, generates errors.
*/
public abstract class Template<D> {
private final Map<SourceFile, D> metadata = new HashMap<>();
private final Function<SourceFile, D> parser;
protected Template(Function<SourceFile, D> parser) {
this.parser = parser;
}
/**
* Generate the necessary glue code here.
*
* <p>
* See {@link InstancingTemplate} and {@link OneShotTemplate} for examples.
* </p>
* @param builder The builder to generate the source into.
* @param type The shader stage glue code is needed for.
* @param file The SourceFile with user written code.
*/
public abstract void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file);
public abstract Collection<ShaderInput> getShaderInputs(SourceFile file);
public D getMetadata(SourceFile file) {
return metadata.computeIfAbsent(file, parser);
}
public GLSLVersion getVersion() {
return GLSLVersion.V120;
}
public static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) {
ImmutableList<StructField> fields = struct.getFields();
for (StructField field : fields) {
builder.append(qualifier)
.append(' ')
.append(field.type)
.append(' ')
.append(prefix)
.append(field.name)
.append(";\n");
}
}
public static void assignFields(StringBuilder builder, ShaderStruct struct, String prefix1, String prefix2) {
ImmutableList<StructField> fields = struct.getFields();
for (StructField field : fields) {
builder.append(prefix1)
.append(field.name)
.append(" = ")
.append(prefix2)
.append(field.name)
.append(";\n");
}
}
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.loading;
package com.jozufozu.flywheel.backend.pipeline;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

View file

@ -1,37 +1,35 @@
package com.jozufozu.flywheel.backend.pipeline;
import java.util.List;
import java.util.Optional;
import com.jozufozu.flywheel.backend.FileResolution;
import com.jozufozu.flywheel.backend.source.FileResolution;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.source.SourceFile;
import net.minecraft.util.ResourceLocation;
public class ShaderBuilder {
public class WorldShader {
public final ResourceLocation name;
public final ITemplate template;
public final FileResolution header;
public final Template<?> template;
public final CharSequence header;
public SourceFile mainFile;
private GLSLVersion version;
private StringBuilder source;
private CharSequence source;
private StringBuilder defines;
public ShaderBuilder(ResourceLocation name, ITemplate template, FileResolution header) {
public WorldShader(ResourceLocation name, Template<?> template, FileResolution header) {
this.name = name;
this.template = template;
this.header = header;
this.header = Optional.ofNullable(header.getFile())
.map(SourceFile::generateFinalSource)
.orElse("");
}
public ShaderBuilder setVersion(GLSLVersion version) {
this.version = version;
return this;
}
public ShaderBuilder setDefines(List<String> defs) {
public WorldShader setDefines(List<String> defs) {
defines = new StringBuilder();
for (String def : defs) {
@ -42,35 +40,37 @@ public class ShaderBuilder {
return this;
}
public ShaderBuilder setMainSource(SourceFile file) {
public WorldShader setMainSource(SourceFile file) {
if (mainFile == file) return this;
mainFile = file;
source = new StringBuilder();
file.generateFinalSource(source);
source = file.generateFinalSource();
return this;
}
public GlShader compile(ResourceLocation name, ShaderType type) {
public GlShader compile(ShaderType type) {
StringBuilder finalSource = new StringBuilder();
finalSource.append("#version ")
.append(version)
.append(template.getVersion())
.append('\n')
.append("#define ")
.append(type.define)
.append('\n')
.append(defines != null ? defines : "");
SourceFile file = header.getFile();
if (file != null) {
file.generateFinalSource(finalSource);
}
mainFile.generateFinalSource(finalSource);
.append(defines != null ? defines : "")
.append(header)
.append('\n')
.append(source)
.append('\n');
template.generateTemplateSource(finalSource, type, mainFile);
return new GlShader(name, type, finalSource);
}
public ProtoProgram createProgram() {
return new ProtoProgram(this);
}
}

View file

@ -1,22 +1,15 @@
package com.jozufozu.flywheel.backend.pipeline;
import static org.lwjgl.opengl.GL11.GL_TRUE;
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
import static org.lwjgl.opengl.GL20.glGetProgrami;
import java.util.List;
import javax.annotation.Nullable;
import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.FileResolution;
import com.jozufozu.flywheel.backend.ShaderSources;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.source.FileResolution;
import com.jozufozu.flywheel.backend.source.ShaderSources;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.loading.ProtoProgram;
import com.jozufozu.flywheel.backend.source.SourceFile;
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
import com.jozufozu.flywheel.core.shader.GameStateProgram;
import com.jozufozu.flywheel.core.shader.IMultiProgram;
@ -32,10 +25,10 @@ public class WorldShaderPipeline<P extends WorldProgram> implements IShaderPipel
private final ExtensibleGlProgram.Factory<P> factory;
private final ITemplate template;
private final Template<?> template;
private final FileResolution header;
public WorldShaderPipeline(ShaderSources sources, ExtensibleGlProgram.Factory<P> factory, ITemplate template, FileResolution header) {
public WorldShaderPipeline(ShaderSources sources, ExtensibleGlProgram.Factory<P> factory, Template<?> template, FileResolution header) {
this.sources = sources;
this.factory = factory;
this.template = template;
@ -44,48 +37,40 @@ public class WorldShaderPipeline<P extends WorldProgram> implements IShaderPipel
public IMultiProgram<P> compile(ProgramSpec spec) {
SourceFile file = sources.source(spec.vert);
SourceFile file = sources.source(spec.source);
return compile(spec.name, file, spec.getStates());
}
public IMultiProgram<P> compile(ResourceLocation name, SourceFile file, List<ProgramState> variants) {
ShaderBuilder shader = new ShaderBuilder(name, template, header)
.setMainSource(file)
.setVersion(GLSLVersion.V110);
WorldShader shader = new WorldShader(name, template, header)
.setMainSource(file);
GameStateProgram.Builder<P> builder = GameStateProgram.builder(compile(shader, name, null));
GameStateProgram.Builder<P> builder = GameStateProgram.builder(compile(shader, null));
for (ProgramState variant : variants) {
builder.withVariant(variant.getContext(), compile(shader, name, variant));
builder.withVariant(variant.getContext(), compile(shader, variant));
}
return builder.build();
}
private P compile(ShaderBuilder shader, ResourceLocation name, @Nullable ProgramState variant) {
private P compile(WorldShader shader, @Nullable ProgramState variant) {
if (variant != null) {
shader.setDefines(variant.getDefines());
}
GlShader vertex = shader.compile(name, ShaderType.VERTEX);
GlShader fragment = shader.compile(name, ShaderType.FRAGMENT);
ProtoProgram program = new ProtoProgram();
program.attachShader(vertex);
program.attachShader(fragment);
template.attachAttributes(program, shader.mainFile);
program.link(name);
program.deleteLinkedShaders();
ProtoProgram program = shader.createProgram()
.compilePart(ShaderType.VERTEX)
.compilePart(ShaderType.FRAGMENT)
.link()
.deleteLinkedShaders();
if (variant != null) {
return factory.create(name, program.program, variant.getExtensions());
return factory.create(shader.name, program.program, variant.getExtensions());
} else {
return factory.create(name, program.program, null);
return factory.create(shader.name, program.program, null);
}
}
}

View file

@ -1,50 +0,0 @@
package com.jozufozu.flywheel.backend.pipeline.error;
import java.util.Optional;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
public class ErrorReporter {
public static void generateSpanError(Span span, String message) {
SourceFile file = span.getSourceFile();
ErrorBuilder builder = new ErrorBuilder();
CharSequence error = builder.error(message)
.in(file)
.pointAt(span, 2)
.build();
Backend.log.error(error);
}
public static void generateFileError(SourceFile file, String message) {
ErrorBuilder builder = new ErrorBuilder();
CharSequence error = builder.error(message)
.in(file)
.build();
Backend.log.error(error);
}
public static void generateMissingStruct(SourceFile file, Span vertexName) {
Optional<Span> span = file.parent.index.getStructDefinitionsMatching(vertexName)
.stream()
.findFirst()
.map(ShaderStruct::getName);
ErrorBuilder builder = new ErrorBuilder();
ErrorBuilder error = builder.error("struct not defined")
.in(file)
.pointAt(vertexName, 2)
.hintIncludeFor(span.orElse(null));
Backend.log.error(error.build());
}
}

View file

@ -0,0 +1,86 @@
package com.jozufozu.flywheel.backend.source;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.source.error.ErrorBuilder;
import com.jozufozu.flywheel.backend.source.span.Span;
import net.minecraft.util.ResourceLocation;
/**
* A reference to a source file that might not be loaded when the owning object is created.
*
* <p>
* FileResolutions are used primarily while parsing import statements. {@link FileResolution#file} is initially
* null, but will be populated later on, after <em>all</em> SourceFiles are loaded (assuming
* {@link FileResolution#fileLoc} references an actual file).
* </p>
*/
public class FileResolution {
/**
* Spans that have references that resolved to this.
*/
private final List<Span> foundSpans = new ArrayList<>();
private final ShaderSources parent;
private final ResourceLocation fileLoc;
private SourceFile file;
public FileResolution(ShaderSources parent, ResourceLocation fileLoc) {
this.parent = parent;
this.fileLoc = fileLoc;
}
public ResourceLocation getFileLoc() {
return fileLoc;
}
@Nullable
public SourceFile getFile() {
return file;
}
/**
* Store the given span so this resolution can know all the places that reference the file.
*
* <p>
* Used for error reporting.
* </p>
* @param span A span where this file is referenced.
*/
public void addSpan(Span span) {
foundSpans.add(span);
}
/**
* Check to see if this file actually resolves to something.
*
* <p>
* Called after all files are loaded. If we can't find the file here, it doesn't exist.
* </p>
*/
void resolve() {
try {
file = this.parent.source(fileLoc);
} catch (RuntimeException error) {
ErrorBuilder builder = new ErrorBuilder();
builder.error(String.format("could not find source for file %s", fileLoc));
// print the location of all places where this file was referenced
for (Span span : foundSpans) {
builder.in(span.getSourceFile())
.pointAt(span, 2);
}
Backend.log.error(builder.build());
}
}
void invalidate() {
foundSpans.clear();
file = null;
}
}

View file

@ -0,0 +1,42 @@
package com.jozufozu.flywheel.backend.source;
import java.util.Collection;
import java.util.Map;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
import net.minecraft.util.ResourceLocation;
/**
* Indexes many shader source definitions to allow for error fix suggestions.
*/
public class Index {
private final Multimap<String, ShaderStruct> knownStructs = MultimapBuilder.hashKeys()
.hashSetValues()
.build();
private final Multimap<String, ShaderFunction> knownFunctions = MultimapBuilder.hashKeys()
.hashSetValues()
.build();
public Index(Map<ResourceLocation, SourceFile> sources) {
Collection<SourceFile> files = sources.values();
for (SourceFile file : files) {
file.getStructs().forEach(knownStructs::put);
file.getFunctions().forEach(knownFunctions::put);
}
}
public Collection<ShaderStruct> getStructDefinitionsMatching(CharSequence name) {
return knownStructs.get(name.toString());
}
public Collection<ShaderFunction> getFunctionDefinitionsMatching(CharSequence name) {
return knownFunctions.get(name.toString());
}
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.loading;
package com.jozufozu.flywheel.backend.source;
public class ShaderLoadingException extends RuntimeException {
public ShaderLoadingException() {

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend;
package com.jozufozu.flywheel.backend.source;
import java.io.IOException;
import java.util.ArrayList;
@ -7,16 +7,14 @@ import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.IShaderContext;
import com.jozufozu.flywheel.backend.ResourceUtil;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.loading.ShaderLoadingException;
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
import com.jozufozu.flywheel.event.GatherContextEvent;
@ -36,6 +34,13 @@ import net.minecraftforge.resource.IResourceType;
import net.minecraftforge.resource.ISelectiveResourceReloadListener;
import net.minecraftforge.resource.VanillaResourceType;
/**
* The main entity for loading shaders.
*
* <p>
* This class is responsible for invoking the loading, parsing, and compilation stages.
* </p>
*/
public class ShaderSources implements ISelectiveResourceReloadListener {
public static final String SHADER_DIR = "flywheel/shaders/";
public static final String PROGRAM_DIR = "flywheel/programs/";
@ -70,7 +75,7 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
}
public FileResolution resolveFile(ResourceLocation fileLoc) {
return resolutions.computeIfAbsent(fileLoc, FileResolution::new);
return resolutions.computeIfAbsent(fileLoc, l -> new FileResolution(this, l));
}
@Deprecated
@ -86,17 +91,17 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
if (backend.gl20()) {
shouldCrash = false;
backend.clearContexts();
backend._clearContexts();
resolutions.values().forEach(FileResolution::invalidate);
shaderSources.clear();
ModLoader.get()
.postEvent(new GatherContextEvent(backend));
resolutions.values().forEach(FileResolution::invalidate);
loadProgramSpecs(manager);
loadShaderSources(manager);
for (FileResolution resolution : resolutions.values()) {
resolution.resolve(this);
resolution.resolve();
}
for (IShaderContext<?> context : backend.allContexts()) {

View file

@ -1,29 +1,22 @@
package com.jozufozu.flywheel.backend.pipeline;
package com.jozufozu.flywheel.backend.source;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.backend.FileResolution;
import com.jozufozu.flywheel.backend.ShaderSources;
import com.jozufozu.flywheel.backend.pipeline.parse.Import;
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction;
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
import com.jozufozu.flywheel.backend.pipeline.span.CharPos;
import com.jozufozu.flywheel.backend.pipeline.span.ErrorSpan;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
import com.jozufozu.flywheel.backend.pipeline.span.StringSpan;
import com.jozufozu.flywheel.backend.source.parse.Import;
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
import com.jozufozu.flywheel.backend.source.span.CharPos;
import com.jozufozu.flywheel.backend.source.span.ErrorSpan;
import com.jozufozu.flywheel.backend.source.span.Span;
import com.jozufozu.flywheel.backend.source.span.StringSpan;
import com.jozufozu.flywheel.util.StringUtil;
import it.unimi.dsi.fastutil.ints.IntArrayList;
@ -142,6 +135,12 @@ public class SourceFile {
return "#use " + '"' + name + '"';
}
public CharSequence generateFinalSource() {
StringBuilder builder = new StringBuilder();
generateFinalSource(builder);
return builder;
}
public void generateFinalSource(StringBuilder source) {
for (Import include : getIncludes()) {
SourceFile file = include.getFile();

View file

@ -1,11 +1,7 @@
package com.jozufozu.flywheel.backend.pipeline.error;
package com.jozufozu.flywheel.backend.source.error;
import java.util.Optional;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
import com.jozufozu.flywheel.backend.source.SourceFile;
import com.jozufozu.flywheel.backend.source.span.Span;
import com.jozufozu.flywheel.util.FlwUtil;
public class ErrorBuilder {
@ -40,6 +36,11 @@ public class ErrorBuilder {
return line(String.valueOf(no), content);
}
public ErrorBuilder numberedLine(int no, int width, CharSequence content) {
String fmt = "%1$" + width + 's';
return line(String.format(fmt, no), content);
}
public ErrorBuilder line(CharSequence leftColumn, CharSequence rightColumn) {
internal.append(leftColumn)
@ -54,10 +55,10 @@ public class ErrorBuilder {
return this;
}
public ErrorBuilder hintIncludeFor(Span span) {
public ErrorBuilder hintIncludeFor(Span span, CharSequence msg) {
if (span == null) return this;
hint("add " + span.getSourceFile().importStatement())
hint("add " + span.getSourceFile().importStatement() + " " + msg)
.in(span.getSourceFile())
.pointAt(span, 1);
@ -77,14 +78,13 @@ public class ErrorBuilder {
int digits = FlwUtil.numDigits(lastLine);
int firstCol = span.getStart().getCol();
int lastCol = span.getEnd().getCol();
for (int i = firstLine; i <= lastLine; i++) {
CharSequence line = file.getLine(i);
numberedLine(i + 1, line);
numberedLine(i + 1, digits, line);
if (i == spanLine) {
line(FlwUtil.repeatChar(' ', digits), generateUnderline(firstCol, lastCol));

View file

@ -0,0 +1,73 @@
package com.jozufozu.flywheel.backend.source.error;
import java.util.Optional;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.source.SourceFile;
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
import com.jozufozu.flywheel.backend.source.span.Span;
public class ErrorReporter {
public static void generateSpanError(Span span, String message) {
SourceFile file = span.getSourceFile();
ErrorBuilder builder = new ErrorBuilder();
CharSequence error = builder.error(message)
.in(file)
.pointAt(span, 2)
.build();
Backend.log.error(error);
}
public static void generateFileError(SourceFile file, String message) {
ErrorBuilder builder = new ErrorBuilder();
CharSequence error = builder.error(message)
.in(file)
.build();
Backend.log.error(error);
}
public static void generateMissingStruct(SourceFile file, Span vertexName, CharSequence msg) {
generateMissingStruct(file, vertexName, msg, "");
}
public static void generateMissingStruct(SourceFile file, Span vertexName, CharSequence msg, CharSequence hint) {
Optional<Span> span = file.parent.index.getStructDefinitionsMatching(vertexName)
.stream()
.findFirst()
.map(ShaderStruct::getName);
ErrorBuilder builder = new ErrorBuilder();
ErrorBuilder error = builder.error(msg)
.in(file)
.pointAt(vertexName, 2)
.hintIncludeFor(span.orElse(null), hint);
Backend.log.error(error.build());
}
public static void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg) {
generateMissingFunction(file, functionName, msg, "");
}
public static void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg, CharSequence hint) {
Optional<Span> span = file.parent.index.getFunctionDefinitionsMatching(functionName)
.stream()
.findFirst()
.map(ShaderFunction::getName);
ErrorBuilder builder = new ErrorBuilder();
ErrorBuilder error = builder.error(msg)
.in(file)
.hintIncludeFor(span.orElse(null), hint);
Backend.log.error(error.build());
}
}

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.source.error;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;

View file

@ -1,5 +1,5 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.loading;
package com.jozufozu.flywheel.backend.source;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.pipeline.parse;
package com.jozufozu.flywheel.backend.source.parse;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
import com.jozufozu.flywheel.backend.source.span.Span;
public abstract class AbstractShaderElement {

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.pipeline.parse;
package com.jozufozu.flywheel.backend.source.parse;
import java.util.ArrayList;
import java.util.List;
@ -6,11 +6,11 @@ import java.util.Optional;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.backend.FileResolution;
import com.jozufozu.flywheel.backend.ShaderSources;
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
import com.jozufozu.flywheel.backend.source.FileResolution;
import com.jozufozu.flywheel.backend.source.ShaderSources;
import com.jozufozu.flywheel.backend.source.error.ErrorReporter;
import com.jozufozu.flywheel.backend.source.SourceFile;
import com.jozufozu.flywheel.backend.source.span.Span;
import net.minecraft.util.ResourceLocation;

View file

@ -1,11 +1,11 @@
package com.jozufozu.flywheel.backend.pipeline.parse;
package com.jozufozu.flywheel.backend.source.parse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
import com.jozufozu.flywheel.backend.source.span.Span;
public class ShaderFunction extends AbstractShaderElement {

View file

@ -1,13 +1,11 @@
package com.jozufozu.flywheel.backend.pipeline.parse;
package com.jozufozu.flywheel.backend.source.parse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.backend.loading.ProtoProgram;
import com.jozufozu.flywheel.backend.loading.TypeHelper;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
import com.jozufozu.flywheel.backend.source.span.Span;
public class ShaderStruct extends AbstractShaderElement {
@ -65,14 +63,6 @@ public class ShaderStruct extends AbstractShaderElement {
return fields.build();
}
public void addPrefixedAttributes(ProtoProgram builder, String prefix) {
for (StructField field : fields) {
int attributeCount = TypeHelper.getAttributeCount(field.type);
builder.addAttribute(prefix + field.name, attributeCount);
}
}
@Override
public String toString() {
return "struct " + name;

View file

@ -1,9 +1,8 @@
package com.jozufozu.flywheel.backend.pipeline.parse;
package com.jozufozu.flywheel.backend.source.parse;
import java.util.regex.Pattern;
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
import com.jozufozu.flywheel.backend.source.span.Span;
public class StructField extends AbstractShaderElement {
public static final Pattern fieldPattern = Pattern.compile("(\\S+)\\s*(\\S+);");

View file

@ -1,7 +1,6 @@
package com.jozufozu.flywheel.backend.pipeline.parse;
package com.jozufozu.flywheel.backend.source.parse;
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
import com.jozufozu.flywheel.backend.pipeline.span.Span;
import com.jozufozu.flywheel.backend.source.span.Span;
public class Variable extends AbstractShaderElement {

View file

@ -1,6 +1,6 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.pipeline.parse;
package com.jozufozu.flywheel.backend.source.parse;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.pipeline.span;
package com.jozufozu.flywheel.backend.source.span;
/**
* A position in a file.

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.pipeline.span;
package com.jozufozu.flywheel.backend.source.span;
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
import com.jozufozu.flywheel.backend.source.SourceFile;
/**
* Represents a (syntactically) malformed segment of code.

View file

@ -1,8 +1,8 @@
package com.jozufozu.flywheel.backend.pipeline.span;
package com.jozufozu.flywheel.backend.source.span;
import java.util.regex.Matcher;
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
import com.jozufozu.flywheel.backend.source.SourceFile;
/**
* A span of code in a {@link SourceFile}.

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.pipeline.span;
package com.jozufozu.flywheel.backend.source.span;
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
import com.jozufozu.flywheel.backend.source.SourceFile;
public class StringSpan extends Span {

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.source.span;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;

View file

@ -2,7 +2,7 @@ package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.FileResolution;
import com.jozufozu.flywheel.backend.source.FileResolution;
import com.jozufozu.flywheel.backend.ResourceUtil;
import com.jozufozu.flywheel.backend.SpecMetaRegistry;
import com.jozufozu.flywheel.backend.pipeline.IShaderPipeline;
@ -40,8 +40,8 @@ public class Contexts {
IShaderPipeline<CrumblingProgram> crumblingPipeline = new WorldShaderPipeline<>(backend.sources, CrumblingProgram::new, InstancingTemplate.INSTANCE, crumblingBuiltins);
IShaderPipeline<WorldProgram> worldPipeline = new WorldShaderPipeline<>(backend.sources, WorldProgram::new, InstancingTemplate.INSTANCE, worldBuiltins);
CRUMBLING = backend.register(new WorldContext<>(backend, crumblingPipeline).withName(Names.CRUMBLING));
WORLD = backend.register(new WorldContext<>(backend, worldPipeline).withName(Names.WORLD));
CRUMBLING = backend.register(WorldContext.builder(backend, Names.CRUMBLING).build(crumblingPipeline));
WORLD = backend.register(WorldContext.builder(backend, Names.WORLD).build(worldPipeline));
}
public static class Names {

View file

@ -1,40 +1,33 @@
package com.jozufozu.flywheel.core;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Stream;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.ShaderContext;
import com.jozufozu.flywheel.backend.IShaderContext;
import com.jozufozu.flywheel.backend.material.MaterialSpec;
import com.jozufozu.flywheel.backend.pipeline.IShaderPipeline;
import com.jozufozu.flywheel.core.shader.IMultiProgram;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
import net.minecraft.util.ResourceLocation;
public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
protected ResourceLocation name;
protected Supplier<Stream<ResourceLocation>> specStream;
public class WorldContext<P extends WorldProgram> implements IShaderContext<P> {
public final Backend backend;
protected final Map<ResourceLocation, IMultiProgram<P>> programs = new HashMap<>();
protected final ResourceLocation name;
protected final Supplier<Stream<ResourceLocation>> specStream;
public final IShaderPipeline<P> pipeline;
public WorldContext(Backend backend, IShaderPipeline<P> factory) {
super(backend);
this.pipeline = factory;
specStream = () -> backend.allMaterials()
.stream()
.map(MaterialSpec::getProgramName);
}
public WorldContext<P> withName(ResourceLocation name) {
public WorldContext(Backend backend, ResourceLocation name, Supplier<Stream<ResourceLocation>> specStream, IShaderPipeline<P> pipeline) {
this.backend = backend;
this.name = name;
return this;
}
public WorldContext<P> withSpecStream(Supplier<Stream<ResourceLocation>> specStream) {
this.specStream = specStream;
return this;
this.pipeline = pipeline;
}
@Override
@ -59,4 +52,45 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
backend.sources.notifyError();
}
}
@Override
public Supplier<P> getProgramSupplier(ResourceLocation spec) {
return programs.get(spec);
}
@Override
public void delete() {
programs.values()
.forEach(IMultiProgram::delete);
programs.clear();
}
public static Builder builder(Backend backend, ResourceLocation name) {
return new Builder(backend, name);
}
public static class Builder {
private final Backend backend;
private final ResourceLocation name;
private Supplier<Stream<ResourceLocation>> specStream;
public Builder(Backend backend, ResourceLocation name) {
this.backend = backend;
this.name = name;
}
public Builder setSpecStream(Supplier<Stream<ResourceLocation>> specStream) {
this.specStream = specStream;
return this;
}
public <P extends WorldProgram> WorldContext<P> build(IShaderPipeline<P> pipeline) {
if (specStream == null) {
specStream = () -> backend.allMaterials()
.stream()
.map(MaterialSpec::getProgramName);
}
return new WorldContext<>(backend, name, specStream, pipeline);
}
}
}

View file

@ -1,56 +1,54 @@
package com.jozufozu.flywheel.core.shader.spec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.jozufozu.flywheel.backend.source.SourceFile;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.util.ResourceLocation;
/**
* An object describing a shader program that can be loaded by flywheel.
*
* <p>
* These are defined through json. All ProgramSpecs in <code>assets/modid/flywheel/programs</code> are parsed and
* processed. One ProgramSpec typically specifies one "material" that can be used in game to render things.
* </p>
* <p>
* All shader source files in <code>assets/modid/flywheel/shaders</code> are completely loaded and parsed into
* {@link SourceFile SourceFiles}, but not compiled until one of them is
* referenced by a ProgramSpec.
* </p>
*/
public class ProgramSpec {
// TODO: Block model style inheritance?
public static final Codec<ProgramSpec> CODEC = RecordCodecBuilder.create(instance -> instance.group(
ResourceLocation.CODEC.fieldOf("vert")
.forGetter(ProgramSpec::getVert),
ResourceLocation.CODEC.fieldOf("frag")
.forGetter(ProgramSpec::getFrag),
ResourceLocation.CODEC.fieldOf("source")
.forGetter(ProgramSpec::getSource),
ProgramState.CODEC.listOf()
.optionalFieldOf("states", Collections.emptyList())
.forGetter(ProgramSpec::getStates))
.apply(instance, ProgramSpec::new));
public ResourceLocation name;
public final ResourceLocation vert;
public final ResourceLocation frag;
public final ResourceLocation source;
public final List<ProgramState> states;
public ProgramSpec(ResourceLocation vert, ResourceLocation frag, List<ProgramState> states) {
this.vert = vert;
this.frag = frag;
public ProgramSpec(ResourceLocation source, List<ProgramState> states) {
this.source = source;
this.states = states;
}
public ProgramSpec(ResourceLocation name, ResourceLocation vert, ResourceLocation frag) {
this.name = name;
this.vert = vert;
this.frag = frag;
this.states = new ArrayList<>();
}
public void setName(ResourceLocation name) {
this.name = name;
}
public ResourceLocation getVert() {
return vert;
}
public ResourceLocation getFrag() {
return frag;
public ResourceLocation getSource() {
return source;
}
public List<ProgramState> getStates() {

View file

@ -95,6 +95,9 @@ public class RenderHooksMixin {
// Instancing
/**
* This gets called when a block is marked for rerender by vanilla.
*/
@Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;)V")
private void checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) {
InstancedRenderDispatcher.getTiles(level)

View file

@ -1,6 +1,5 @@
{
"vert": "flywheel:model.vert",
"frag": "flywheel:block.frag",
"source": "flywheel:model.vert",
"states": [
{
"when": {

View file

@ -1,6 +1,5 @@
{
"vert": "flywheel:oriented.vert",
"frag": "flywheel:block.frag",
"source": "flywheel:oriented.vert",
"states": [
{
"when": {