mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 07:26:48 +01:00
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:
parent
3c24abe837
commit
82ea5b1720
48 changed files with 668 additions and 559 deletions
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
package com.jozufozu.flywheel.backend.gl;
|
||||
|
||||
public enum GLSLVersion {
|
||||
V110(110),
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()) {
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.backend.loading;
|
||||
package com.jozufozu.flywheel.backend.source;
|
||||
|
||||
public class ShaderLoadingException extends RuntimeException {
|
||||
public ShaderLoadingException() {
|
|
@ -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()) {
|
|
@ -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();
|
|
@ -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));
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.source.error;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -1,5 +1,5 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.loading;
|
||||
package com.jozufozu.flywheel.backend.source;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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;
|
|
@ -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+);");
|
|
@ -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 {
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.pipeline.parse;
|
||||
package com.jozufozu.flywheel.backend.source.parse;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline.span;
|
||||
package com.jozufozu.flywheel.backend.source.span;
|
||||
|
||||
/**
|
||||
* A position in a 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.
|
|
@ -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}.
|
|
@ -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 {
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.source.span;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"vert": "flywheel:model.vert",
|
||||
"frag": "flywheel:block.frag",
|
||||
"source": "flywheel:model.vert",
|
||||
"states": [
|
||||
{
|
||||
"when": {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"vert": "flywheel:oriented.vert",
|
||||
"frag": "flywheel:block.frag",
|
||||
"source": "flywheel:oriented.vert",
|
||||
"states": [
|
||||
{
|
||||
"when": {
|
||||
|
|
Loading…
Reference in a new issue