mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-28 16:06:28 +01:00
More loading tweaks
- Immutable function map - Parse #use directives on load - Recursive include gathering - More sane spec loading in WorldContext - StateSensitiveMultiProgram builder - Rename confusing game state things
This commit is contained in:
parent
959434e01a
commit
15d5396bc9
12 changed files with 220 additions and 68 deletions
|
@ -133,10 +133,12 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
|||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void notifyError() {
|
||||
shouldCrash = true;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Nonnull
|
||||
public String getShaderSource(ResourceLocation loc) {
|
||||
String source = shaderSource.get(loc);
|
||||
|
@ -172,12 +174,19 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
|||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Shader source(ResourceLocation name, ShaderType type) {
|
||||
return new Shader(this, type, name, getShaderSource(name));
|
||||
}
|
||||
|
||||
public static Stream<String> lines(String s) {
|
||||
return new BufferedReader(new StringReader(s)).lines();
|
||||
public SourceFile source(ResourceLocation name) {
|
||||
SourceFile source = shaderSources.get(name);
|
||||
|
||||
if (source == null) {
|
||||
throw new ShaderLoadingException(String.format("shader '%s' does not exist", name));
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
public String readToString(InputStream is) {
|
||||
|
|
|
@ -2,6 +2,10 @@ package com.jozufozu.flywheel.backend.gl.buffer;
|
|||
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
/**
|
||||
* Gives a hint to the driver about how you intend to use a buffer. For a detailed explanation, see
|
||||
* <a href="https://www.khronos.org/opengl/wiki/Buffer_Object#Buffer_Object_Usage">this article</a>.
|
||||
*/
|
||||
public enum GlBufferUsage {
|
||||
STREAM_DRAW(GL15.GL_STREAM_DRAW),
|
||||
STREAM_READ(GL15.GL_STREAM_READ),
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class Includer {
|
||||
|
||||
public static List<SourceFile> recurseIncludes(SourceFile from) {
|
||||
ShaderSources sources = from.getParent();
|
||||
|
||||
Set<ResourceLocation> seen = new HashSet<>();
|
||||
|
||||
seen.add(from.name);
|
||||
|
||||
List<SourceFile> out = new ArrayList<>();
|
||||
|
||||
process(sources, seen, out, from);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private static void process(ShaderSources sources, Set<ResourceLocation> seen, List<SourceFile> out, SourceFile source) {
|
||||
ImmutableList<ResourceLocation> includes = source.getIncludes();
|
||||
|
||||
for (ResourceLocation include : includes) {
|
||||
|
||||
if (seen.add(include)) {
|
||||
SourceFile file = sources.source(include);
|
||||
|
||||
process(sources, seen, out, file);
|
||||
|
||||
out.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class PipelineProgramBuilder {
|
||||
|
||||
private final List<SourceFile> sources = new ArrayList<>();
|
||||
|
||||
public PipelineProgramBuilder() {
|
||||
|
||||
}
|
||||
|
||||
public PipelineProgramBuilder include(SourceFile file) {
|
||||
sources.add(file);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PipelineProgramBuilder includeAll(Collection<? extends SourceFile> files) {
|
||||
sources.addAll(files);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -2,12 +2,17 @@ package com.jozufozu.flywheel.backend.pipeline;
|
|||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.ErrorSpan;
|
||||
|
@ -27,25 +32,63 @@ public class SourceFile {
|
|||
|
||||
public final ResourceLocation name;
|
||||
private final String source;
|
||||
private final ShaderSources loader;
|
||||
private final ShaderSources parent;
|
||||
|
||||
private final Map<String, ShaderFunction> functions = new HashMap<>();
|
||||
// function name -> function object
|
||||
private final ImmutableMap<String, ShaderFunction> functions;
|
||||
private final ImmutableList<ResourceLocation> includes;
|
||||
|
||||
public SourceFile(ShaderSources loader, ResourceLocation name, String source) {
|
||||
this.loader = loader;
|
||||
// Sections of the source that must be trimmed for compilation.
|
||||
private final List<Span> elisions = new ArrayList<>();
|
||||
|
||||
public SourceFile(ShaderSources parent, ResourceLocation name, String source) {
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
this.source = source;
|
||||
|
||||
parseFunctions();
|
||||
functions = parseFunctions();
|
||||
includes = parseIncludes();
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
protected void parseFunctions() {
|
||||
public ShaderSources getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public ImmutableMap<String, ShaderFunction> getFunctions() {
|
||||
return functions;
|
||||
}
|
||||
|
||||
public ImmutableList<ResourceLocation> getIncludes() {
|
||||
return includes;
|
||||
}
|
||||
|
||||
private ImmutableList<ResourceLocation> parseIncludes() {
|
||||
Matcher uses = includePattern.matcher(source);
|
||||
|
||||
List<ResourceLocation> includes = new ArrayList<>();
|
||||
|
||||
while (uses.find()) {
|
||||
Span use = Span.fromMatcher(this, uses);
|
||||
|
||||
elisions.add(use); // we have to trim that later
|
||||
|
||||
ResourceLocation loc = new ResourceLocation(uses.group(1)); // TODO: error gracefully
|
||||
|
||||
includes.add(loc);
|
||||
}
|
||||
|
||||
return ImmutableList.copyOf(includes);
|
||||
}
|
||||
|
||||
private ImmutableMap<String, ShaderFunction> parseFunctions() {
|
||||
Matcher matcher = functionDeclaration.matcher(source);
|
||||
|
||||
Map<String, ShaderFunction> functions = new HashMap<>();
|
||||
|
||||
while (matcher.find()) {
|
||||
Span type = Span.fromMatcher(this, matcher, 1);
|
||||
Span name = Span.fromMatcher(this, matcher, 2);
|
||||
|
@ -68,6 +111,8 @@ public class SourceFile {
|
|||
|
||||
functions.put(name.get(), function);
|
||||
}
|
||||
|
||||
return ImmutableMap.copyOf(functions);
|
||||
}
|
||||
|
||||
private int findEndOfBlock(int end) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.core.shader.IMultiProgram;
|
||||
|
@ -8,7 +10,14 @@ import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|||
public class WorldShaderPipeline<P extends WorldProgram> {
|
||||
|
||||
@Nullable // TODO: temporary null return
|
||||
public IMultiProgram<P> compile(SourceFile file) {
|
||||
public P compile(SourceFile file) {
|
||||
|
||||
PipelineProgramBuilder builder = new PipelineProgramBuilder();
|
||||
|
||||
builder.includeAll(Includer.recurseIncludes(file));
|
||||
|
||||
builder.include(file);
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ import com.jozufozu.flywheel.backend.loading.ShaderTransformer;
|
|||
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
||||
import com.jozufozu.flywheel.core.shader.StateSensitiveMultiProgram;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramState;
|
||||
import com.jozufozu.flywheel.util.WorldAttached;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
@ -109,17 +111,7 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
|||
|
||||
specStream.get()
|
||||
.map(backend::getSpec)
|
||||
.forEach(spec -> {
|
||||
|
||||
try {
|
||||
programs.put(spec.name, new StateSensitiveMultiProgram<>(factory, this, spec));
|
||||
|
||||
Backend.log.debug("Loaded program {}", spec.name);
|
||||
} catch (Exception e) {
|
||||
Backend.log.error("Program '{}': {}", spec.name, e);
|
||||
backend.sources.notifyError();
|
||||
}
|
||||
});
|
||||
.forEach(this::loadSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -154,6 +146,26 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
|||
throw new ShaderLoadingException(String.format("%s is missing %s, cannot use in World Context", shader.type.name, declaration));
|
||||
}
|
||||
|
||||
private void loadSpec(ProgramSpec spec) {
|
||||
|
||||
try {
|
||||
StateSensitiveMultiProgram.Builder<P> builder = new StateSensitiveMultiProgram.Builder<>(factory.create(loadAndLink(spec, null)));
|
||||
|
||||
for (ProgramState state : spec.states) {
|
||||
Program variant = loadAndLink(spec, state);
|
||||
|
||||
builder.withVariant(state.getContext(), factory.create(variant, state.getExtensions()));
|
||||
}
|
||||
|
||||
programs.put(spec.name, builder.build());
|
||||
|
||||
Backend.log.debug("Loaded program {}", spec.name);
|
||||
} catch (Exception e) {
|
||||
Backend.log.error("Program '{}': {}", spec.name, e);
|
||||
backend.sources.notifyError();
|
||||
}
|
||||
}
|
||||
|
||||
public interface TemplateFactory {
|
||||
ProgramTemplate create(ShaderSources loader);
|
||||
}
|
||||
|
|
|
@ -3,39 +3,26 @@ package com.jozufozu.flywheel.core.shader;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.jozufozu.flywheel.backend.ShaderContext;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||
import com.jozufozu.flywheel.backend.loading.Program;
|
||||
import com.jozufozu.flywheel.core.shader.spec.IContextCondition;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramState;
|
||||
import com.jozufozu.flywheel.core.shader.spec.IGameStateCondition;
|
||||
import com.jozufozu.flywheel.util.Pair;
|
||||
|
||||
public class StateSensitiveMultiProgram<P extends GlProgram> implements IMultiProgram<P> {
|
||||
|
||||
List<Pair<IContextCondition, P>> variants;
|
||||
P fallback;
|
||||
private final List<Pair<IGameStateCondition, P>> variants;
|
||||
private final P fallback;
|
||||
|
||||
public StateSensitiveMultiProgram(ExtensibleGlProgram.Factory<P> factory, ShaderContext<P> context, ProgramSpec p) {
|
||||
variants = new ArrayList<>(p.states.size());
|
||||
|
||||
for (ProgramState state : p.states) {
|
||||
|
||||
Program variant = context.loadAndLink(p, state);
|
||||
|
||||
Pair<IContextCondition, P> pair = Pair.of(state.getContext(), factory.create(variant, state.getExtensions()));
|
||||
|
||||
variants.add(pair);
|
||||
}
|
||||
|
||||
fallback = factory.create(context.loadAndLink(p, null));
|
||||
protected StateSensitiveMultiProgram(List<Pair<IGameStateCondition, P>> variants, P fallback) {
|
||||
this.variants = variants;
|
||||
this.fallback = fallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public P get() {
|
||||
for (Pair<IContextCondition, P> variant : variants) {
|
||||
for (Pair<IGameStateCondition, P> variant : variants) {
|
||||
if (variant.getFirst()
|
||||
.get()) return variant.getSecond();
|
||||
.isMet()) return variant.getSecond();
|
||||
}
|
||||
|
||||
return fallback;
|
||||
|
@ -43,11 +30,29 @@ public class StateSensitiveMultiProgram<P extends GlProgram> implements IMultiPr
|
|||
|
||||
@Override
|
||||
public void delete() {
|
||||
for (Pair<IContextCondition, P> variant : variants) {
|
||||
for (Pair<IGameStateCondition, P> variant : variants) {
|
||||
variant.getSecond()
|
||||
.delete();
|
||||
}
|
||||
|
||||
fallback.delete();
|
||||
}
|
||||
|
||||
public static class Builder<P extends GlProgram> {
|
||||
private final P fallback;
|
||||
private final List<Pair<IGameStateCondition, P>> variants = new ArrayList<>();
|
||||
|
||||
public Builder(P fallback) {
|
||||
this.fallback = fallback;
|
||||
}
|
||||
|
||||
public Builder<P> withVariant(IGameStateCondition condition, P program) {
|
||||
variants.add(Pair.of(condition, program));
|
||||
return this;
|
||||
}
|
||||
|
||||
public IMultiProgram<P> build() {
|
||||
return new StateSensitiveMultiProgram<>(ImmutableList.copyOf(variants), fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,18 +5,18 @@ import com.mojang.serialization.Codec;
|
|||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class BooleanContextCondition implements IContextCondition {
|
||||
public class BooleanGameStateCondition implements IGameStateCondition {
|
||||
|
||||
public static final Codec<BooleanContextCondition> BOOLEAN_SUGAR = IGameStateProvider.CODEC.xmap(gameContext -> {
|
||||
public static final Codec<BooleanGameStateCondition> BOOLEAN_SUGAR = IGameStateProvider.CODEC.xmap(gameContext -> {
|
||||
if (gameContext instanceof IBooleanStateProvider) {
|
||||
return new BooleanContextCondition(((IBooleanStateProvider) gameContext));
|
||||
return new BooleanGameStateCondition(((IBooleanStateProvider) gameContext));
|
||||
}
|
||||
|
||||
return null;
|
||||
}, IContextCondition::contextProvider);
|
||||
}, IGameStateCondition::getStateProvider);
|
||||
protected final IBooleanStateProvider context;
|
||||
|
||||
public BooleanContextCondition(IBooleanStateProvider context) {
|
||||
public BooleanGameStateCondition(IBooleanStateProvider context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
@ -26,12 +26,12 @@ public class BooleanContextCondition implements IContextCondition {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IGameStateProvider contextProvider() {
|
||||
public IGameStateProvider getStateProvider() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean get() {
|
||||
public boolean isMet() {
|
||||
return context.isTrue();
|
||||
}
|
||||
}
|
|
@ -4,11 +4,11 @@ import com.jozufozu.flywheel.core.shader.gamestate.IGameStateProvider;
|
|||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public interface IContextCondition {
|
||||
public interface IGameStateCondition {
|
||||
|
||||
ResourceLocation getID();
|
||||
|
||||
IGameStateProvider contextProvider();
|
||||
IGameStateProvider getStateProvider();
|
||||
|
||||
boolean get();
|
||||
boolean isMet();
|
||||
}
|
|
@ -13,10 +13,10 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|||
public class ProgramState {
|
||||
|
||||
// TODO: Use Codec.dispatch
|
||||
private static final Codec<IContextCondition> WHEN = Codec.either(BooleanContextCondition.BOOLEAN_SUGAR, SpecificValueCondition.CODEC)
|
||||
private static final Codec<IGameStateCondition> WHEN = Codec.either(BooleanGameStateCondition.BOOLEAN_SUGAR, SpecificValueCondition.CODEC)
|
||||
.flatXmap(either -> either.map(DataResult::success, DataResult::success), any -> {
|
||||
if (any instanceof BooleanContextCondition) {
|
||||
return DataResult.success(Either.left((BooleanContextCondition) any));
|
||||
if (any instanceof BooleanGameStateCondition) {
|
||||
return DataResult.success(Either.left((BooleanGameStateCondition) any));
|
||||
}
|
||||
|
||||
if (any instanceof SpecificValueCondition) {
|
||||
|
@ -27,24 +27,24 @@ public class ProgramState {
|
|||
});
|
||||
|
||||
public static final Codec<ProgramState> CODEC = RecordCodecBuilder.create(state -> state.group(WHEN.fieldOf("when")
|
||||
.forGetter(ProgramState::getContext), CodecUtil.oneOrMore(Codec.STRING)
|
||||
.optionalFieldOf("define", Collections.emptyList())
|
||||
.forGetter(ProgramState::getDefines), CodecUtil.oneOrMore(IProgramExtension.CODEC)
|
||||
.optionalFieldOf("extend", Collections.emptyList())
|
||||
.forGetter(ProgramState::getExtensions))
|
||||
.forGetter(ProgramState::getContext), CodecUtil.oneOrMore(Codec.STRING)
|
||||
.optionalFieldOf("define", Collections.emptyList())
|
||||
.forGetter(ProgramState::getDefines), CodecUtil.oneOrMore(IProgramExtension.CODEC)
|
||||
.optionalFieldOf("extend", Collections.emptyList())
|
||||
.forGetter(ProgramState::getExtensions))
|
||||
.apply(state, ProgramState::new));
|
||||
|
||||
private final IContextCondition context;
|
||||
private final IGameStateCondition context;
|
||||
private final List<String> defines;
|
||||
private final List<IProgramExtension> extensions;
|
||||
|
||||
public ProgramState(IContextCondition context, List<String> defines, List<IProgramExtension> extensions) {
|
||||
public ProgramState(IGameStateCondition context, List<String> defines, List<IProgramExtension> extensions) {
|
||||
this.context = context;
|
||||
this.defines = defines;
|
||||
this.extensions = extensions;
|
||||
}
|
||||
|
||||
public IContextCondition getContext() {
|
||||
public IGameStateCondition getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class SpecificValueCondition implements IContextCondition {
|
||||
public class SpecificValueCondition implements IGameStateCondition {
|
||||
|
||||
public static final Codec<SpecificValueCondition> CODEC = RecordCodecBuilder.create(condition -> condition.group(IGameStateProvider.CODEC.fieldOf("provider")
|
||||
.forGetter(SpecificValueCondition::contextProvider), Codec.STRING.fieldOf("value")
|
||||
.forGetter(SpecificValueCondition::getStateProvider), Codec.STRING.fieldOf("value")
|
||||
.forGetter(SpecificValueCondition::getValue))
|
||||
.apply(condition, SpecificValueCondition::new));
|
||||
|
||||
|
@ -31,12 +31,12 @@ public class SpecificValueCondition implements IContextCondition {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IGameStateProvider contextProvider() {
|
||||
public IGameStateProvider getStateProvider() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean get() {
|
||||
public boolean isMet() {
|
||||
return required.equals(context.getValue()
|
||||
.toString());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue