Taxi shaders

- Not *quite* ubershaders yet
 - Note: heavily broken
 - "Pull" based compilation
   - Sources are only ready when they are needed by the compiler
   - Compiler is responsible for import resolution
   - Compiler prepends file headers
 - Reduces need for FileResolution
   - May replace with ResourceLocations?
   - Not sure about the future of source checks
   - TODO: Lots of dead code left in FileResolution
 - PipelineShader -> Pipeline interface + Simple impl
   - Use Context object for assembler factory
This commit is contained in:
Jozufozu 2022-10-08 14:02:54 -07:00
parent 28e16a7810
commit 99e4105e94
45 changed files with 703 additions and 759 deletions

View file

@ -2,15 +2,7 @@ package com.jozufozu.flywheel.api.context;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceFile;
public record ContextShader(GlProgram.Factory factory, FileResolution vertexShader, FileResolution fragmentShader) {
public SourceFile getVertexShader() {
return vertexShader.getFile();
}
public SourceFile getFragmentShader() {
return fragmentShader.getFile();
}
}

View file

@ -1,27 +1,27 @@
package com.jozufozu.flywheel.api.pipeline;
import java.util.function.BiFunction;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.ShaderSources;
public record PipelineShader(GLSLVersion glslVersion, FileResolution vertex, FileResolution fragment,
InstanceAssemblerFactory factory) {
public interface Pipeline {
GLSLVersion glslVersion();
FileResolution vertex();
FileResolution fragment();
/**
* Generate the source component necessary to convert a packed {@link StructType} into its shader representation.
*
* @param structType The struct type to convert.
* @return A source component defining functions that unpack a representation of the given struct type.
*/
public SourceComponent assemble(VertexType vertexType, StructType<?> structType) {
return factory.apply(vertexType, structType);
}
public interface InstanceAssemblerFactory extends BiFunction<VertexType, StructType<?>, SourceComponent> {
SourceComponent assemble(InstanceAssemblerContext context);
record InstanceAssemblerContext(ShaderSources sources, VertexType vertexType, StructType<?> structType) {
}
}

View file

@ -2,8 +2,8 @@ package com.jozufozu.flywheel.backend;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.pipeline.PipelineShader;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
import net.minecraft.network.chat.Component;
@ -21,5 +21,5 @@ public interface BackendType {
boolean supported();
@Nullable PipelineShader pipelineShader();
@Nullable SimplePipeline pipelineShader();
}

View file

@ -2,7 +2,6 @@ package com.jozufozu.flywheel.backend;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.ShaderSources;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
@ -24,11 +23,12 @@ public class Loader implements ResourceManagerReloadListener {
Loader() {
// Can be null when running datagenerators due to the unfortunate time we call this
Minecraft minecraft = Minecraft.getInstance();
if (minecraft != null) {
ResourceManager manager = minecraft.getResourceManager();
if (manager instanceof ReloadableResourceManager) {
((ReloadableResourceManager) manager).registerReloadListener(this);
}
if (minecraft == null) {
return;
}
if (minecraft.getResourceManager() instanceof ReloadableResourceManager reloadable) {
reloadable.registerReloadListener(this);
}
}
@ -39,23 +39,7 @@ public class Loader implements ResourceManagerReloadListener {
var errorReporter = new ErrorReporter();
ShaderSources sources = new ShaderSources(errorReporter, manager);
Backend.LOGGER.info("Loaded all shader sources in " + sources.getLoadTime());
FileResolution.run(errorReporter, sources);
if (errorReporter.hasErrored()) {
throw errorReporter.dump();
}
sources.postResolve();
Backend.LOGGER.info("Successfully resolved all source files.");
FileResolution.checkAll(errorReporter);
Backend.LOGGER.info("All shaders passed checks.");
FlwCompiler.INSTANCE.run();
FlwCompiler.INSTANCE = new FlwCompiler(sources);
ClientLevel level = Minecraft.getInstance().level;
if (Backend.canUseInstancing(level)) {

View file

@ -5,9 +5,9 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.pipeline.PipelineShader;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.core.BackendTypes;
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
import net.minecraft.network.chat.Component;
@ -20,9 +20,9 @@ public class SimpleBackendType implements BackendType {
private final Supplier<Engine> engineSupplier;
private final Supplier<BackendType> fallback;
private final BooleanSupplier isSupported;
private final PipelineShader pipelineShader;
private final SimplePipeline pipelineShader;
public SimpleBackendType(String properName, String shortName, Component engineMessage, Supplier<Engine> engineSupplier, Supplier<BackendType> fallback, BooleanSupplier isSupported, @Nullable PipelineShader pipelineShader) {
public SimpleBackendType(String properName, String shortName, Component engineMessage, Supplier<Engine> engineSupplier, Supplier<BackendType> fallback, BooleanSupplier isSupported, @Nullable SimplePipeline pipelineShader) {
this.properName = properName;
this.shortName = shortName;
this.engineMessage = engineMessage;
@ -72,7 +72,7 @@ public class SimpleBackendType implements BackendType {
}
@Override
public @Nullable PipelineShader pipelineShader() {
public @Nullable SimplePipeline pipelineShader() {
return pipelineShader;
}
@ -83,7 +83,7 @@ public class SimpleBackendType implements BackendType {
private Supplier<Engine> engineSupplier;
private Supplier<BackendType> fallback;
private BooleanSupplier isSupported;
private PipelineShader pipelineShader;
private SimplePipeline pipelineShader;
public Builder properName(String properName) {
this.properName = properName;
@ -115,7 +115,7 @@ public class SimpleBackendType implements BackendType {
return this;
}
public Builder pipelineShader(PipelineShader pipelineShader) {
public Builder pipelineShader(SimplePipeline pipelineShader) {
this.pipelineShader = pipelineShader;
return this;
}

View file

@ -1,59 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.pipeline.PipelineShader;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.uniform.UniformProvider;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.core.BackendTypes;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
import com.jozufozu.flywheel.util.StringUtil;
class CompilationEnvironment {
final VertexMaterialComponent vertexMaterialComponent;
final FragmentMaterialComponent fragmentMaterialComponent;
boolean needsCrash = false;
final long compileStart = System.nanoTime();
final Multimap<Set<UniformProvider>, PipelineContext> uniformProviderGroups = ArrayListMultimap.create();
final List<PipelineContext> pipelineContexts = new ArrayList<>();
CompilationEnvironment() {
for (PipelineShader pipelineShader : BackendTypes.availablePipelineShaders()) {
for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) {
for (ContextShader contextShader : ComponentRegistry.contextShaders) {
acknowledgeContext(new PipelineContext(vertexType, structType, contextShader, pipelineShader));
}
}
}
}
this.vertexMaterialComponent = new VertexMaterialComponent(ComponentRegistry.materials.vertexSources());
this.fragmentMaterialComponent = new FragmentMaterialComponent(ComponentRegistry.materials.fragmentSources());
}
private void acknowledgeContext(PipelineContext ctx) {
uniformProviderGroups.put(ctx.uniformProviders(), ctx);
pipelineContexts.add(ctx);
}
public void finish() {
long compileEnd = System.nanoTime();
Backend.LOGGER.info("Compiled " + pipelineContexts.size() + " programs in " + StringUtil.formatTime(compileEnd - compileStart));
if (needsCrash) {
throw new ShaderLoadingException("Compilation failed");
}
}
}

View file

@ -1,14 +1,21 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.pipeline.PipelineShader;
import com.jozufozu.flywheel.api.pipeline.Pipeline;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.uniform.UniformProvider;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
@ -16,42 +23,64 @@ import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.instancing.indirect.IndirectComponent;
import com.jozufozu.flywheel.core.BackendTypes;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.Pipelines;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
import com.jozufozu.flywheel.core.source.ShaderSources;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.util.StringUtil;
import net.minecraft.resources.ResourceLocation;
public class FlwCompiler {
public static final FlwCompiler INSTANCE = new FlwCompiler();
public static FlwCompiler INSTANCE;
public static void onReloadRenderers(ReloadRenderersEvent t) {
}
private final ShaderCompiler shaderCompiler = new ShaderCompiler();
public final Map<PipelineContext, GlProgram> pipelinePrograms = new HashMap<>();
final Map<PipelineContext, GlProgram> pipelinePrograms = new HashMap<>();
final Map<StructType<?>, GlProgram> cullingPrograms = new HashMap<>();
public final Map<StructType<?>, GlProgram> cullingPrograms = new HashMap<>();
boolean needsCrash = false;
private CompilationEnvironment environment;
final long compileStart = System.nanoTime();
final Multimap<Set<UniformProvider>, PipelineContext> uniformProviderGroups = ArrayListMultimap.create();
final List<PipelineContext> pipelineContexts = new ArrayList<>();
FlwCompiler() {
private final ShaderSources sources;
private final VertexMaterialComponent vertexMaterialComponent;
private final FragmentMaterialComponent fragmentMaterialComponent;
}
public FlwCompiler(ShaderSources sources) {
this.sources = sources;
this.vertexMaterialComponent = new VertexMaterialComponent(sources, ComponentRegistry.materials.vertexSources());
this.fragmentMaterialComponent = new FragmentMaterialComponent(sources, ComponentRegistry.materials.fragmentSources());
public void run() {
environment = new CompilationEnvironment();
for (SimplePipeline pipelineShader : BackendTypes.availablePipelineShaders()) {
for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) {
// TODO: context ubershaders, or not?
pipelineContexts.add(new PipelineContext(vertexType, structType, Components.WORLD, pipelineShader));
}
}
}
for (PipelineContext context : environment.pipelineContexts) {
// TODO: analyze uniform providers and group them into sets; break up this ctor
for (PipelineContext context : pipelineContexts) {
try {
var glProgram = compilePipelineContext(context);
pipelinePrograms.put(context, glProgram);
} catch (ShaderCompilationException e) {
environment.needsCrash = true;
needsCrash = true;
Backend.LOGGER.error(e.errors);
}
}
@ -61,15 +90,25 @@ public class FlwCompiler {
var glProgram = compileComputeCuller(type);
cullingPrograms.put(type, glProgram);
} catch (ShaderCompilationException e) {
environment.needsCrash = true;
needsCrash = true;
Backend.LOGGER.error(e.errors);
}
}
environment.finish();
finish();
}
public GlProgram getPipelineProgram(VertexType vertexType, StructType<?> structType, ContextShader contextShader, PipelineShader pipelineShader) {
public void finish() {
long compileEnd = System.nanoTime();
Backend.LOGGER.info("Compiled " + pipelineContexts.size() + " programs in " + StringUtil.formatTime(compileEnd - compileStart));
if (needsCrash) {
throw new ShaderLoadingException("Compilation failed");
}
}
public GlProgram getPipelineProgram(VertexType vertexType, StructType<?> structType, ContextShader contextShader, SimplePipeline pipelineShader) {
return pipelinePrograms.get(new PipelineContext(vertexType, structType, contextShader, pipelineShader));
}
@ -82,48 +121,110 @@ public class FlwCompiler {
var glslVersion = ctx.pipelineShader()
.glslVersion();
var vertex = new ShaderContext(glslVersion, ShaderType.VERTEX, ctx.getVertexComponents());
var fragment = new ShaderContext(glslVersion, ShaderType.FRAGMENT, ctx.getFragmentComponents());
var vertex = compileShader(glslVersion, ShaderType.VERTEX, getVertexComponents(ctx));
var fragment = compileShader(glslVersion, ShaderType.FRAGMENT, getFragmentComponents(ctx));
return ctx.contextShader()
.factory()
.create(new ProgramAssembler().attachShader(shaderCompiler.get(vertex))
.attachShader(shaderCompiler.get(fragment))
.create(new ProgramAssembler().attachShader(vertex)
.attachShader(fragment)
.link());
}
protected GlProgram compileComputeCuller(StructType<?> structType) {
var location = structType.getInstanceShader();
var finalSource = new StringBuilder();
CompilationContext context = new CompilationContext();
var components = List.of(new IndirectComponent(structType.getLayout().layoutItems), location.getFile(), Components.Pipeline.INDIRECT_CULL.getFile());
return new GlProgram(new ProgramAssembler().attachShader(compileShader(GLSLVersion.V460, ShaderType.COMPUTE, getComputeComponents(structType)))
.link());
}
ImmutableList<SourceComponent> getVertexComponents(PipelineContext ctx) {
var instanceAssembly = ctx.pipelineShader()
.assemble(new Pipeline.InstanceAssemblerContext(sources, ctx.vertexType(), ctx.structType()));
var layout = sources.find(ctx.vertexType()
.getLayoutShader()
.resourceLocation());
var instance = sources.find(ctx.structType()
.getInstanceShader()
.resourceLocation());
var context = sources.find(ctx.contextShader()
.vertexShader()
.resourceLocation());
var pipeline = sources.find(ctx.pipelineShader()
.vertex()
.resourceLocation());
return ImmutableList.of(vertexMaterialComponent, instanceAssembly, layout, instance, context, pipeline);
}
ImmutableList<SourceComponent> getFragmentComponents(PipelineContext ctx) {
var context = sources.find(ctx.contextShader()
.fragmentShader()
.resourceLocation());
var pipeline = sources.find(ctx.pipelineShader()
.fragment()
.resourceLocation());
return ImmutableList.of(fragmentMaterialComponent, context, pipeline);
}
@NotNull ImmutableList<SourceComponent> getComputeComponents(StructType<?> structType) {
var instanceAssembly = new IndirectComponent(sources, structType);
var instance = sources.find(structType.getInstanceShader()
.resourceLocation());
var pipeline = sources.find(Pipelines.Files.INDIRECT_CULL.resourceLocation());
return ImmutableList.of(instanceAssembly, instance, pipeline);
}
protected GlShader compileShader(GLSLVersion glslVersion, ShaderType shaderType, ImmutableList<SourceComponent> sourceComponents) {
StringBuilder finalSource = new StringBuilder(CompileUtil.generateHeader(glslVersion, shaderType));
finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n");
finalSource.append("#extension GL_ARB_conservative_depth : enable\n");
var ctx = new CompilationContext();
var names = ImmutableList.<ResourceLocation>builder();
var included = new LinkedHashSet<SourceComponent>(); // linked to preserve order
for (var component : components) {
included.addAll(component.included());
for (var include : depthFirstInclude(sourceComponents)) {
appendFinalSource(finalSource, ctx, include);
}
for (var component : sourceComponents) {
appendFinalSource(finalSource, ctx, component);
names.add(component.name());
}
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V460, ShaderType.COMPUTE));
for (var include : included) {
finalSource.append(include.source(context));
}
for (var component : components) {
finalSource.append(component.source(context));
}
try {
var fileLoc = location.getFileLoc();
var shader = new GlShader(finalSource.toString(), ShaderType.COMPUTE, ImmutableList.of(fileLoc));
var program = new ProgramAssembler().attachShader(shader)
.link();
return new GlProgram(program);
return new GlShader(finalSource.toString(), shaderType, names.build());
} catch (ShaderCompilationException e) {
throw e.withErrorLog(context);
throw e.withErrorLog(ctx);
}
}
private static void appendFinalSource(StringBuilder finalSource, CompilationContext ctx, SourceComponent component) {
var source = component.source();
if (component instanceof SourceFile file) {
finalSource.append(ctx.sourceHeader(file));
} else {
finalSource.append(ctx.generatedHeader(source, component.name()
.toString()));
}
finalSource.append(source);
}
protected static Set<SourceComponent> depthFirstInclude(ImmutableList<SourceComponent> root) {
var included = new LinkedHashSet<SourceComponent>(); // linked to preserve order
for (var component : root) {
recursiveDepthFirstInclude(included, component);
}
return included;
}
protected static void recursiveDepthFirstInclude(Set<SourceComponent> included, SourceComponent component) {
for (var include : component.included()) {
recursiveDepthFirstInclude(included, include);
}
included.addAll(component.included());
}
}

View file

@ -1,49 +1,29 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.Collection;
import java.util.List;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.ShaderSources;
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class FragmentMaterialComponent implements SourceComponent {
public class FragmentMaterialComponent extends MaterialAdapterComponent {
private static final String flw_materialFragment = "flw_materialFragment";
private static final String flw_discardPredicate = "flw_discardPredicate";
private static final String flw_fogFilter = "flw_fogFilter";
private static final List<String> adaptedFunctions = List.of(flw_materialFragment, flw_discardPredicate, flw_fogFilter);
private static final GlslExpr flw_materialFragmentID = GlslExpr.variable(flw_materialFragment + "ID");
private final List<TransformedSourceComponent> transformedMaterials;
public FragmentMaterialComponent(List<FileResolution> sourceMaterials) {
this.transformedMaterials = sourceMaterials.stream()
.map(FileResolution::getFile)
.map(s -> {
var newName = flw_materialFragment + '_' + ResourceUtil.toSafeString(s.name());
return new TransformedSourceComponent(s, flw_materialFragment, newName);
})
.toList();
}
@Override
public Collection<? extends SourceComponent> included() {
return transformedMaterials;
}
@Override
public String source(CompilationContext ctx) {
return null;
public FragmentMaterialComponent(ShaderSources sources, List<FileResolution> sourceMaterials) {
super(sources, sourceMaterials, flw_materialFragmentID, adaptedFunctions);
}
@Override
public ResourceLocation name() {
return null;
return Flywheel.rl("fragment_material_adapter");
}
}

View file

@ -0,0 +1,87 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.ShaderSources;
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public abstract class MaterialAdapterComponent implements SourceComponent {
// TODO: material id handling in pipeline shader
private final GlslExpr switchArg;
private final List<String> adaptedFunctions;
private final List<RenamedFunctionsSourceComponent> transformedMaterials;
// TODO: Create builder and remove Fragment* and Vertex* classes
public MaterialAdapterComponent(ShaderSources sources, List<FileResolution> sourceMaterials, GlslExpr switchArg, List<String> adaptedFunctions) {
this.switchArg = switchArg;
this.adaptedFunctions = adaptedFunctions;
var transformed = ImmutableList.<RenamedFunctionsSourceComponent>builder();
for (FileResolution fileResolution : sourceMaterials) {
var loc = fileResolution.resourceLocation();
var sourceFile = sources.find(loc);
transformed.add(new RenamedFunctionsSourceComponent(sourceFile, createAdapterMap(adaptedFunctions, loc)));
}
this.transformedMaterials = transformed.build();
}
@Override
public Collection<? extends SourceComponent> included() {
return transformedMaterials;
}
@Override
public String source() {
var builder = new GlslBuilder();
for (String adaptedFunction : adaptedFunctions) {
// TODO: support different function signatures
builder.function()
.returnType("void")
.name(adaptedFunction)
.body(body -> generateAdapter(body, adaptedFunction));
}
return builder.build();
}
private void generateAdapter(GlslBuilder.BlockBuilder body, String adaptedFunction) {
var sw = new GlslBuilder.SwitchBuilder(switchArg);
for (int i = 0; i < transformedMaterials.size(); i++) {
var variant = transformedMaterials.get(i)
.replacement(adaptedFunction);
sw.case_(i, b -> b.eval(GlslExpr.call(variant))
.break_());
}
body.add(sw.build());
}
@NotNull
private static HashMap<String, String> createAdapterMap(List<String> adaptedFunctions, ResourceLocation loc) {
HashMap<String, String> out = new HashMap<>();
var suffix = '_' + ResourceUtil.toSafeString(loc);
for (String fnName : adaptedFunctions) {
out.put(fnName, fnName + suffix);
}
return out;
}
}

View file

@ -1,20 +1,9 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.pipeline.PipelineShader;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.uniform.UniformProvider;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
/**
* Represents the entire context of a program's usage.
@ -24,42 +13,5 @@ import com.jozufozu.flywheel.core.SourceComponent;
* @param contextShader The context shader to use.
*/
public record PipelineContext(VertexType vertexType, StructType<?> structType, ContextShader contextShader,
PipelineShader pipelineShader) {
@NotNull
public Set<UniformProvider> uniformProviders() {
var fragmentComponents = getFragmentComponents();
var vertexComponents = getVertexComponents();
return Stream.concat(fragmentComponents.stream(), vertexComponents.stream())
.map(SourceComponent::included)
.flatMap(Collection::stream)
.map(SourceComponent::name)
.<UniformProvider>mapMulti((component, consumer) -> {
var uniformProvider = ComponentRegistry.getUniformProvider(component);
if (uniformProvider != null) {
consumer.accept(uniformProvider);
}
})
.collect(Collectors.toSet());
}
ImmutableList<SourceComponent> getVertexComponents() {
var layout = vertexType.getLayoutShader()
.getFile();
var instanceAssembly = pipelineShader.assemble(vertexType, structType);
var instance = structType.getInstanceShader()
.getFile();
var context = contextShader.getVertexShader();
var pipeline = pipelineShader.vertex()
.getFile();
return ImmutableList.of(layout, instanceAssembly, instance, context, pipeline);
}
ImmutableList<SourceComponent> getFragmentComponents() {
var context = contextShader.getFragmentShader();
var pipeline = pipelineShader.fragment()
.getFile();
return ImmutableList.of(context, pipeline);
}
SimplePipeline pipelineShader) {
}

View file

@ -0,0 +1,49 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.Collection;
import java.util.Map;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public final class RenamedFunctionsSourceComponent implements SourceComponent {
private final SourceComponent source;
private final Map<String, String> replacements;
public RenamedFunctionsSourceComponent(SourceComponent source, String find, String replace) {
this.source = source;
this.replacements = Map.of(find, replace);
}
public RenamedFunctionsSourceComponent(SourceComponent source, Map<String, String> replacements) {
this.source = source;
this.replacements = replacements;
}
public String replacement(String name) {
return replacements.getOrDefault(name, name);
}
@Override
public String source() {
var source = this.source.source();
for (var entry : replacements.entrySet()) {
source = source.replace(entry.getKey(), entry.getValue());
}
return source;
}
@Override
public ResourceLocation name() {
return ResourceUtil.subPath(source.name(), "_renamed");
}
@Override
public Collection<? extends SourceComponent> included() {
return source.included();
}
}

View file

@ -1,64 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.gl.GlObject;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.CompilationContext;
import net.minecraft.resources.ResourceLocation;
/**
* Handles compilation and deletion of vertex shaders.
*/
public class ShaderCompiler {
private final Map<ShaderContext, GlShader> map = new HashMap<>();
ShaderCompiler() {
}
public GlShader get(ShaderContext key) {
return map.computeIfAbsent(key, this::_create);
}
protected GlShader _create(ShaderContext key) {
StringBuilder finalSource = new StringBuilder(key.generateHeader());
finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n");
finalSource.append("#extension GL_ARB_conservative_depth : enable\n");
var ctx = new CompilationContext();
var names = ImmutableList.<ResourceLocation>builder();
var included = new LinkedHashSet<SourceComponent>(); // linked to preserve order
for (var component : key.sourceComponents()) {
included.addAll(component.included());
names.add(component.name());
}
for (var include : included) {
finalSource.append(include.source(ctx));
}
for (var component : key.sourceComponents()) {
finalSource.append(component.source(ctx));
}
try {
return new GlShader(finalSource.toString(), key.shaderType(), names.build());
} catch (ShaderCompilationException e) {
throw e.withErrorLog(ctx);
}
}
public void invalidate() {
map.values()
.forEach(GlObject::delete);
map.clear();
}
}

View file

@ -1,18 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.List;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.SourceComponent;
/**
* @param glslVersion The GLSL version to use.
* @param sourceComponents A list of shader components to stitch together, in order.
*/
public record ShaderContext(GLSLVersion glslVersion, ShaderType shaderType, List<SourceComponent> sourceComponents) {
public String generateHeader() {
return CompileUtil.generateHeader(glslVersion, shaderType);
}
}

View file

@ -1,37 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.Collection;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public final class TransformedSourceComponent implements SourceComponent {
final SourceComponent source;
final String find;
final String replacement;
public TransformedSourceComponent(SourceComponent source, String find, String replacement) {
this.source = source;
this.find = find;
this.replacement = replacement;
}
@Override
public String source(CompilationContext ctx) {
return source.source(ctx)
.replace(find, replacement);
}
@Override
public ResourceLocation name() {
return ResourceUtil.subPath(source.name(), "_renamed");
}
@Override
public Collection<? extends SourceComponent> included() {
return source.included();
}
}

View file

@ -1,71 +1,27 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.Collection;
import java.util.List;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
import com.jozufozu.flywheel.core.source.ShaderSources;
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class VertexMaterialComponent implements SourceComponent {
public class VertexMaterialComponent extends MaterialAdapterComponent {
private static final String flw_materialVertex = "flw_materialVertex";
private static final List<String> adaptedFunctions = List.of(flw_materialVertex);
private static final GlslExpr flw_materialVertexID = GlslExpr.variable(flw_materialVertex + "ID");
private final List<TransformedSourceComponent> transformedMaterials;
public VertexMaterialComponent(List<FileResolution> sourceMaterials) {
this.transformedMaterials = sourceMaterials.stream()
.map(FileResolution::getFile)
.map(s -> {
var newName = flw_materialVertex + '_' + ResourceUtil.toSafeString(s.name());
return new TransformedSourceComponent(s, flw_materialVertex, newName);
})
.toList();
}
@Override
public Collection<? extends SourceComponent> included() {
return transformedMaterials;
}
@Override
public String source(CompilationContext ctx) {
String out = genSource();
return ctx.generatedHeader(out, "material adapter") + out;
}
public String genSource() {
var builder = new GlslBuilder();
builder.function()
.returnType("void")
.name("flw_materialVertex")
.body(this::accept);
return builder.build();
public VertexMaterialComponent(ShaderSources sources, List<FileResolution> sourceMaterials) {
super(sources, sourceMaterials, flw_materialVertexID, adaptedFunctions);
}
@Override
public ResourceLocation name() {
return Flywheel.rl("vertex_material_adapter");
}
private void accept(GlslBuilder.BlockBuilder body) {
var sw = new GlslBuilder.SwitchBuilder(flw_materialVertexID);
for (int i = 0; i < transformedMaterials.size(); i++) {
var variant = transformedMaterials.get(i).replacement;
sw.case_(i, b -> b.eval(GlslExpr.call(variant))
.break_());
}
body.add(sw.build());
}
}

View file

@ -3,11 +3,15 @@ package com.jozufozu.flywheel.backend.instancing.indirect;
import java.util.Collection;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.api.pipeline.Pipeline;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.Pipelines;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.layout.LayoutItem;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.ShaderSources;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
@ -19,14 +23,20 @@ public class IndirectComponent implements SourceComponent {
private static final GlslExpr.Variable UNPACKING_VARIABLE = GlslExpr.variable(UNPACK_ARG);
private final List<LayoutItem> layoutItems;
private final ImmutableList<SourceFile> included;
public IndirectComponent(List<LayoutItem> layoutItems) {
this.layoutItems = layoutItems;
public IndirectComponent(Pipeline.InstanceAssemblerContext ctx) {
this(ctx.sources(), ctx.structType());
}
public IndirectComponent(ShaderSources sources, StructType<?> structType) {
this.layoutItems = structType.getLayout().layoutItems;
included = ImmutableList.of(sources.find(Pipelines.Files.UTIL_TYPES.resourceLocation()));
}
@Override
public Collection<? extends SourceComponent> included() {
return List.of(Components.UTIL_TYPES.getFile());
return included;
}
@Override
@ -35,9 +45,8 @@ public class IndirectComponent implements SourceComponent {
}
@Override
public String source(CompilationContext ctx) {
var generated = generateIndirect("IndirectStruct");
return ctx.generatedHeader(generated, name().toString()) + generated;
public String source() {
return generateIndirect("IndirectStruct");
}
public String generateIndirect(String structName) {

View file

@ -19,6 +19,7 @@ import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.Pipelines;
import com.jozufozu.flywheel.core.QuadConverter;
public class IndirectCullingGroup<T extends InstancedPart> {
@ -64,7 +65,7 @@ public class IndirectCullingGroup<T extends InstancedPart> {
setupVertexArray();
compute = FlwCompiler.INSTANCE.getCullingProgram(structType);
draw = FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, Components.WORLD, Components.INDIRECT);
draw = FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, Components.WORLD, Pipelines.INDIRECT);
}
private void setupVertexArray() {

View file

@ -5,9 +5,9 @@ import java.util.Collections;
import java.util.List;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.pipeline.Pipeline;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.layout.LayoutItem;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
@ -19,9 +19,12 @@ public class InstancedArraysComponent implements SourceComponent {
private final List<LayoutItem> layoutItems;
private final int baseIndex;
public InstancedArraysComponent(List<LayoutItem> layoutItems, int baseIndex) {
this.layoutItems = layoutItems;
this.baseIndex = baseIndex;
public InstancedArraysComponent(Pipeline.InstanceAssemblerContext ctx) {
this.layoutItems = ctx.structType()
.getLayout().layoutItems;
this.baseIndex = ctx.vertexType()
.getLayout()
.getAttributeCount();
}
@Override
@ -30,9 +33,8 @@ public class InstancedArraysComponent implements SourceComponent {
}
@Override
public String source(CompilationContext ctx) {
var generated = generateInstancedArrays("Instance");
return ctx.generatedHeader(generated, name().toString()) + generated;
public String source() {
return generateInstancedArrays("Instance");
}
@Override

View file

@ -18,7 +18,7 @@ import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.Pipelines;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.util.WeakHashSet;
import com.mojang.blaze3d.systems.RenderSystem;
@ -123,7 +123,7 @@ public class InstancingEngine implements Engine {
var structType = desc.instance();
var material = desc.material();
FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, context, Components.INSTANCED_ARRAYS)
FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, context, Pipelines.INSTANCED_ARRAYS)
.bind();
}

View file

@ -9,7 +9,6 @@ import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.pipeline.PipelineShader;
import com.jozufozu.flywheel.backend.BackendType;
import com.jozufozu.flywheel.backend.ShadersModHandler;
import com.jozufozu.flywheel.backend.SimpleBackendType;
@ -17,6 +16,7 @@ import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine;
import com.jozufozu.flywheel.backend.instancing.indirect.IndirectEngine;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.TextComponent;
@ -58,7 +58,7 @@ public class BackendTypes {
.fallback(() -> BackendTypes.BATCHING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
.instancedArraysSupported())
.pipelineShader(Components.INSTANCED_ARRAYS)
.pipelineShader(Pipelines.INSTANCED_ARRAYS)
.register();
/**
@ -72,7 +72,7 @@ public class BackendTypes {
.fallback(() -> BackendTypes.INSTANCING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
.supportsIndirect())
.pipelineShader(Components.INDIRECT)
.pipelineShader(Pipelines.INDIRECT)
.register();
public static BackendType register(BackendType type) {
@ -99,7 +99,7 @@ public class BackendTypes {
}
public static Collection<PipelineShader> availablePipelineShaders() {
public static Collection<SimplePipeline> availablePipelineShaders() {
return BACKEND_TYPES.values()
.stream()
.filter(BackendType::supported)

View file

@ -53,7 +53,7 @@ public class ComponentRegistry {
public static <T extends UniformProvider> T register(T provider) {
return uniformProviders.register(provider.uniformShader()
.getFileLoc(), provider);
.resourceLocation(), provider);
}
public static Collection<UniformProvider> getAllUniformProviders() {

View file

@ -4,10 +4,6 @@ import java.util.function.BiConsumer;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.pipeline.PipelineShader;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.instancing.indirect.IndirectComponent;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysComponent;
import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceChecks;
@ -31,28 +27,12 @@ public class Components {
public static final ContextShader WORLD = ComponentRegistry.register(new ContextShader(WorldProgram::new, Files.WORLD_VERTEX, Files.WORLD_FRAGMENT));
public static final ContextShader CRUMBLING = ComponentRegistry.register(new ContextShader(CrumblingProgram::new, Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT));
public static final PipelineShader INSTANCED_ARRAYS = new PipelineShader(GLSLVersion.V420, Pipeline.INSTANCED_ARRAYS_DRAW, Pipeline.DRAW_FRAGMENT, (vertexType, structType) -> new InstancedArraysComponent(structType.getLayout().layoutItems, vertexType.getLayout()
.getAttributeCount()));
public static final PipelineShader INDIRECT = new PipelineShader(GLSLVersion.V460, Pipeline.INDIRECT_DRAW, Pipeline.DRAW_FRAGMENT, (vertexType, structType) -> new IndirectComponent(structType.getLayout().layoutItems));
public static final FileResolution UTIL_TYPES = FileResolution.get(Flywheel.rl("util/types.glsl"));
public static void init() {
Files.init();
Formats.init();
StructTypes.init();
Materials.init();
}
public static class Pipeline {
public static final FileResolution DRAW_FRAGMENT = pipeline("pipeline/draw.frag");
public static final FileResolution INSTANCED_ARRAYS_DRAW = pipeline("pipeline/instanced_arrays_draw.vert");
public static final FileResolution INDIRECT_DRAW = pipeline("pipeline/indirect_draw.vert");
public static final FileResolution INDIRECT_CULL = pipeline("pipeline/indirect_cull.glsl");
private static FileResolution pipeline(String name) {
return FileResolution.get(Flywheel.rl(name))
.validateWith(Checks.PIPELINE);
}
Pipelines.init();
}
public static class Files {

View file

@ -0,0 +1,40 @@
package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.instancing.indirect.IndirectComponent;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysComponent;
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
import com.jozufozu.flywheel.core.source.FileResolution;
public class Pipelines {
public static final SimplePipeline INSTANCED_ARRAYS = SimplePipeline.builder()
.glslVersion(GLSLVersion.V420)
.vertex(Files.INSTANCED_ARRAYS_DRAW)
.fragment(Files.DRAW_FRAGMENT)
.assemblerFactory(InstancedArraysComponent::new)
.build();
public static final SimplePipeline INDIRECT = SimplePipeline.builder()
.glslVersion(GLSLVersion.V460)
.vertex(Files.INDIRECT_DRAW)
.fragment(Files.DRAW_FRAGMENT)
.assemblerFactory(IndirectComponent::new)
.build();
public static void init() {
// noop
}
public static class Files {
public static final FileResolution DRAW_FRAGMENT = pipeline("pipeline/draw.frag");
public static final FileResolution INSTANCED_ARRAYS_DRAW = pipeline("pipeline/instanced_arrays_draw.vert");
public static final FileResolution INDIRECT_DRAW = pipeline("pipeline/indirect_draw.vert");
public static final FileResolution INDIRECT_CULL = pipeline("pipeline/indirect_cull.glsl");
public static final FileResolution UTIL_TYPES = FileResolution.get(Flywheel.rl("util/types.glsl"));
private static FileResolution pipeline(String name) {
return FileResolution.get(Flywheel.rl(name))
.validateWith(Components.Checks.PIPELINE);
}
}
}

View file

@ -2,14 +2,12 @@ package com.jozufozu.flywheel.core;
import java.util.Collection;
import com.jozufozu.flywheel.core.source.CompilationContext;
import net.minecraft.resources.ResourceLocation;
public interface SourceComponent {
Collection<? extends SourceComponent> included();
String source(CompilationContext ctx);
String source();
ResourceLocation name();
}

View file

@ -0,0 +1,86 @@
package com.jozufozu.flywheel.core.pipeline;
import com.jozufozu.flywheel.api.pipeline.Pipeline;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.FileResolution;
public final class SimplePipeline implements Pipeline {
private final GLSLVersion glslVersion;
private final FileResolution vertex;
private final FileResolution fragment;
private final InstanceAssemblerFactory factory;
public SimplePipeline(GLSLVersion glslVersion, FileResolution vertex, FileResolution fragment, InstanceAssemblerFactory factory) {
this.glslVersion = glslVersion;
this.vertex = vertex;
this.fragment = fragment;
this.factory = factory;
}
/**
* Generate the source component necessary to convert a packed {@link StructType} into its shader representation.
*
* @return A source component defining functions that unpack a representation of the given struct type.
*/
@Override
public SourceComponent assemble(InstanceAssemblerContext context) {
return factory.apply(context);
}
@Override
public GLSLVersion glslVersion() {
return glslVersion;
}
@Override
public FileResolution vertex() {
return vertex;
}
@Override
public FileResolution fragment() {
return fragment;
}
@FunctionalInterface
public interface InstanceAssemblerFactory {
SourceComponent apply(InstanceAssemblerContext context);
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private GLSLVersion glslVersion;
private FileResolution vertex;
private FileResolution fragment;
private InstanceAssemblerFactory factory;
public Builder glslVersion(GLSLVersion glslVersion) {
this.glslVersion = glslVersion;
return this;
}
public Builder vertex(FileResolution vertex) {
this.vertex = vertex;
return this;
}
public Builder fragment(FileResolution fragment) {
this.fragment = fragment;
return this;
}
public Builder assemblerFactory(InstanceAssemblerFactory factory) {
this.factory = factory;
return this;
}
public SimplePipeline build() {
return new SimplePipeline(glslVersion, vertex, fragment, factory);
}
}
}

View file

@ -15,7 +15,7 @@ public class CompilationContext {
private int generatedLines = 0;
String sourceHeader(SourceFile sourceFile) {
public String sourceHeader(SourceFile sourceFile) {
return "#line " + 0 + ' ' + getOrCreateFileID(sourceFile) + " // " + sourceFile.name + '\n';
}

View file

@ -36,8 +36,6 @@ public class FileResolution {
private final ResourceLocation fileLoc;
private final boolean weak;
private SourceFile file;
private FileResolution(ResourceLocation fileLoc, boolean weak) {
this.fileLoc = fileLoc;
this.weak = weak;
@ -76,40 +74,12 @@ public class FileResolution {
return WEAK.computeIfAbsent(file, loc -> new FileResolution(loc, true));
}
/**
* Try and resolve all referenced source files, printing errors if any aren't found.
*/
public static void run(ErrorReporter errorReporter, SourceFinder sources) {
for (FileResolution resolution : ALL.values()) {
resolution.resolve(errorReporter, sources);
}
for (FileResolution resolution : WEAK.values()) {
resolution.resolve(errorReporter, sources);
}
WEAK.clear();
tooLate = true;
}
public static void checkAll(ErrorReporter errorReporter) {
for (FileResolution resolution : ALL.values()) {
resolution.runChecks(errorReporter);
}
}
private void resolve(ErrorReporter errorReporter, SourceFinder sources) {
file = sources.findSource(fileLoc);
if (file == null) {
reportMissing(errorReporter);
}
// Let the GC do its thing
neededAt.clear();
}
private void reportMissing(ErrorReporter errorReporter) {
ErrorBuilder builder = errorReporter.error(String.format("could not find source for file %s", fileLoc));
for (Span location : neededAt) {
@ -119,22 +89,15 @@ public class FileResolution {
}
private void runChecks(ErrorReporter errorReporter) {
for (var check : checks) {
check.accept(errorReporter, file);
}
// for (var check : checks) {
// check.accept(errorReporter, file);
// }
}
public ResourceLocation getFileLoc() {
public ResourceLocation resourceLocation() {
return fileLoc;
}
/**
* Non-null if this file is resolved because there would have been a crash otherwise.
* @return The file that this resolution resolves to.
*/
public SourceFile getFile() {
return file;
}
public boolean isWeak() {
return weak;

View file

@ -5,4 +5,8 @@ public class ShaderLoadingException extends RuntimeException {
public ShaderLoadingException(String message) {
super(message);
}
public ShaderLoadingException(String message, Throwable cause) {
super(message, cause);
}
}

View file

@ -1,13 +1,14 @@
package com.jozufozu.flywheel.core.source;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import com.google.common.collect.Lists;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
@ -15,72 +16,66 @@ import com.jozufozu.flywheel.util.ResourceUtil;
import com.jozufozu.flywheel.util.StringUtil;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager;
/**
* The main object for loading and parsing source files.
*/
public class ShaderSources implements SourceFinder {
public class ShaderSources {
public static final String SHADER_DIR = "flywheel/";
public static final ArrayList<String> EXTENSIONS = Lists.newArrayList(".vert", ".vsh", ".frag", ".fsh", ".glsl");
private final Map<ResourceLocation, SourceFile> shaderSources = new HashMap<>();
private final Map<ResourceLocation, SourceFile> cache = new HashMap<>();
public final Index index;
/**
* Tracks
*/
private final Deque<ResourceLocation> findStack = new ArrayDeque<>();
public final long loadTimeNs;
public final long indexTimeNs;
public final long totalTimeNs;
private final ResourceManager manager;
private final ErrorReporter errorReporter;
public ShaderSources(ErrorReporter errorReporter, ResourceManager manager) {
long loadStart = System.nanoTime();
for (ResourceLocation location : getValidShaderFiles(manager)) {
try (Resource resource = manager.getResource(location)) {
String source = StringUtil.readToString(resource.getInputStream());
ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR);
shaderSources.put(name, new SourceFile(errorReporter, this, name, source));
} catch (IOException e) {
//
}
}
long loadEnd = System.nanoTime();
long indexStart = System.nanoTime();
index = new Index(shaderSources);
long indexEnd = System.nanoTime();
loadTimeNs = loadEnd - loadStart;
indexTimeNs = indexEnd - indexStart;
totalTimeNs = indexEnd - loadStart;
this.errorReporter = errorReporter;
this.manager = manager;
}
public void postResolve() {
for (SourceFile file : shaderSources.values()) {
file.postResolve();
@Nonnull
public SourceFile find(ResourceLocation location) {
if (findStack.contains(location)) {
generateRecursiveImportException(location);
}
findStack.add(location);
// Can't use computeIfAbsent because mutual recursion causes ConcurrentModificationExceptions
var out = cache.get(location);
if (out == null) {
out = load(location);
cache.put(location, out);
}
findStack.pop();
return out;
}
@Nonnull
private SourceFile load(ResourceLocation loc) {
try {
var resource = manager.getResource(ResourceUtil.prefixed(SHADER_DIR, loc));
var sourceString = StringUtil.readToString(resource.getInputStream());
return new SourceFile(this, loc, sourceString);
} catch (IOException ioException) {
throw new ShaderLoadingException("Could not load shader " + loc, ioException);
}
}
@Override
@Nullable
public SourceFile findSource(ResourceLocation name) {
return shaderSources.get(name);
}
@NotNull
private static Collection<ResourceLocation> getValidShaderFiles(ResourceManager manager) {
return manager.listResources(SHADER_DIR, s -> {
for (String ext : EXTENSIONS) {
if (s.endsWith(ext)) return true;
}
return false;
});
}
public String getLoadTime() {
return StringUtil.formatTime(totalTimeNs);
private void generateRecursiveImportException(ResourceLocation location) {
findStack.add(location);
String path = findStack.stream()
.dropWhile(l -> !l.equals(location))
.map(ResourceLocation::toString)
.collect(Collectors.joining(" -> "));
findStack.clear();
throw new ShaderLoadingException("recursive import: " + path);
}
}

View file

@ -1,20 +1,15 @@
package com.jozufozu.flywheel.core.source;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.core.source.parse.Import;
import com.jozufozu.flywheel.core.source.parse.ShaderField;
import com.jozufozu.flywheel.core.source.parse.ShaderFunction;
@ -37,9 +32,8 @@ public class SourceFile implements SourceComponent {
public final ResourceLocation name;
public final ShaderSources parent;
public final String source;
public final SourceLines lines;
public final SourceLines source;
/**
* Function lookup by name.
@ -57,30 +51,45 @@ public class SourceFile implements SourceComponent {
public final ImmutableList<Import> imports;
public final ImmutableMap<String, ShaderField> fields;
// POST-RESOLUTION
public List<SourceFile> flattenedImports;
public final List<SourceFile> included;
public SourceFile(ErrorReporter errorReporter, ShaderSources parent, ResourceLocation name, String source) {
this.parent = parent;
public SourceFile(ShaderSources sourceFinder, ResourceLocation name, String source) {
this.parent = sourceFinder;
this.name = name;
this.source = source;
this.lines = new SourceLines(source);
this.source = new SourceLines(source);
this.imports = parseImports(errorReporter);
this.functions = parseFunctions();
this.structs = parseStructs();
this.fields = parseFields();
this.imports = parseImports(source);
this.functions = parseFunctions(source);
this.structs = parseStructs(source);
this.fields = parseFields(source);
this.included = imports.stream()
.map(i -> i.file)
.map(Span::toString)
.distinct()
.<SourceFile>mapMulti((file, sink) -> {
try {
var loc = new ResourceLocation(file);
var sourceFile = sourceFinder.find(loc);
if (sourceFile != null) {
sink.accept(sourceFile);
}
} catch (Exception ignored) {
}
})
.toList();
}
@Override
public Collection<? extends SourceComponent> included() {
return flattenedImports;
return included;
}
@Override
public String source(CompilationContext ctx) {
return ctx.sourceHeader(this) + this.genFinalSource();
public String source() {
return this.genFinalSource();
}
@Override
@ -88,52 +97,23 @@ public class SourceFile implements SourceComponent {
return name;
}
public void postResolve() {
this.flattenImports();
}
private void flattenImports() {
// somebody #used us and got resolved before we did
if (this.flattenedImports != null) {
return;
}
if (this.imports.isEmpty()) {
this.flattenedImports = Collections.emptyList();
return;
}
ArrayList<SourceFile> flat = new ArrayList<>(this.imports.size());
for (Import include : this.imports) {
SourceFile file = include.resolution.getFile();
file.flattenImports();
flat.addAll(file.flattenedImports);
flat.add(file);
}
this.flattenedImports = flat.stream()
.distinct()
.toList();
}
public Span getLineSpan(int line) {
int begin = lines.getLineStart(line);
int end = begin + lines.getLine(line).length();
return new StringSpan(this, lines.getCharPos(begin), lines.getCharPos(end));
public Span getLineSpan(int lineNo) {
int begin = source.getLineStart(lineNo);
int end = begin + source.getLine(lineNo)
.length();
return new StringSpan(this, source.getCharPos(begin), source.getCharPos(end));
}
public Span getLineSpanNoWhitespace(int line) {
int begin = lines.getLineStart(line);
int end = begin + lines.getLine(line).length();
int begin = source.getLineStart(line);
int end = begin + source.getLine(line)
.length();
while (begin < end && Character.isWhitespace(source.charAt(begin))) {
begin++;
}
return new StringSpan(this, lines.getCharPos(begin), lines.getCharPos(end));
return new StringSpan(this, source.getCharPos(begin), source.getCharPos(end));
}
/**
@ -147,7 +127,7 @@ public class SourceFile implements SourceComponent {
if (struct != null) return Optional.of(struct);
for (var include : flattenedImports) {
for (var include : included) {
var external = include.structs.get(name);
if (external != null) {
@ -169,7 +149,7 @@ public class SourceFile implements SourceComponent {
if (local != null) return Optional.of(local);
for (var include : flattenedImports) {
for (var include : included) {
var external = include.functions.get(name);
if (external != null) {
@ -185,10 +165,10 @@ public class SourceFile implements SourceComponent {
}
public String printSource() {
return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers();
return "Source for shader '" + name + "':\n" + source.printLinesWithNumbers();
}
private CharSequence genFinalSource() {
private String genFinalSource() {
StringBuilder out = new StringBuilder();
int lastEnd = 0;
@ -203,13 +183,50 @@ public class SourceFile implements SourceComponent {
out.append(this.source, lastEnd, this.source.length());
return out;
return out.toString();
}
@Override
public String toString() {
return name.toString();
}
@Override
public boolean equals(Object o) {
// SourceFiles are only equal by reference.
return this == o;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
/**
* Scan the source for {@code #use "..."} directives.
* Records the contents of the directive into an {@link Import} object, and marks the directive for elision.
*/
private ImmutableList<Import> parseImports(String source) {
Matcher uses = Import.PATTERN.matcher(source);
var imports = ImmutableList.<Import>builder();
while (uses.find()) {
Span use = Span.fromMatcher(this, uses);
Span file = Span.fromMatcher(this, uses, 1);
imports.add(new Import(use, file));
}
return imports.build();
}
/**
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
*/
private ImmutableMap<String, ShaderFunction> parseFunctions() {
private ImmutableMap<String, ShaderFunction> parseFunctions(String source) {
Matcher matcher = ShaderFunction.PATTERN.matcher(source);
Map<String, ShaderFunction> functions = new HashMap<>();
@ -220,7 +237,7 @@ public class SourceFile implements SourceComponent {
Span args = Span.fromMatcher(this, matcher, 3);
int blockStart = matcher.end();
int blockEnd = findEndOfBlock(blockStart);
int blockEnd = findEndOfBlock(source, blockStart);
Span self;
Span body;
@ -243,7 +260,7 @@ public class SourceFile implements SourceComponent {
/**
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
*/
private ImmutableMap<String, ShaderStruct> parseStructs() {
private ImmutableMap<String, ShaderStruct> parseStructs(String source) {
Matcher matcher = ShaderStruct.PATTERN.matcher(source);
ImmutableMap.Builder<String, ShaderStruct> structs = ImmutableMap.builder();
@ -263,7 +280,7 @@ public class SourceFile implements SourceComponent {
/**
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
*/
private ImmutableMap<String, ShaderField> parseFields() {
private ImmutableMap<String, ShaderField> parseFields(String source) {
Matcher matcher = ShaderField.PATTERN.matcher(source);
ImmutableMap.Builder<String, ShaderField> fields = ImmutableMap.builder();
@ -280,36 +297,10 @@ public class SourceFile implements SourceComponent {
return fields.build();
}
/**
* Scan the source for {@code #use "..."} directives.
* Records the contents of the directive into an {@link Import} object, and marks the directive for elision.
*/
private ImmutableList<Import> parseImports(ErrorReporter errorReporter) {
Matcher uses = Import.PATTERN.matcher(source);
Set<String> importedFiles = new HashSet<>();
var imports = ImmutableList.<Import>builder();
while (uses.find()) {
Span use = Span.fromMatcher(this, uses);
Span file = Span.fromMatcher(this, uses, 1);
String fileName = file.get();
if (importedFiles.add(fileName)) {
var checked = Import.create(errorReporter, use, file);
if (checked != null) {
imports.add(checked);
}
}
}
return imports.build();
}
/**
* Given the position of an opening brace, scans through the source for a paired closing brace.
*/
private int findEndOfBlock(int start) {
private static int findEndOfBlock(String source, int start) {
int blockDepth = 0;
for (int i = start + 1; i < source.length(); i++) {
char ch = source.charAt(i);
@ -324,20 +315,4 @@ public class SourceFile implements SourceComponent {
return -1;
}
@Override
public String toString() {
return name.toString();
}
@Override
public boolean equals(Object o) {
// SourceFiles are only equal by reference.
return this == o;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
}

View file

@ -1,15 +0,0 @@
package com.jozufozu.flywheel.core.source;
import org.jetbrains.annotations.Nullable;
import net.minecraft.resources.ResourceLocation;
/**
* A minimal source file lookup function.
*/
@FunctionalInterface
public interface SourceFinder {
@Nullable
SourceFile findSource(ResourceLocation name);
}

View file

@ -3,6 +3,8 @@ package com.jozufozu.flywheel.core.source;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.source.span.CharPos;
import com.jozufozu.flywheel.util.StringUtil;
@ -10,7 +12,7 @@ import com.jozufozu.flywheel.util.StringUtil;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
public class SourceLines {
public class SourceLines implements CharSequence {
private static final Pattern newLine = Pattern.compile("(\\r\\n|\\r|\\n)");
@ -23,10 +25,12 @@ public class SourceLines {
* 0-indexed line lookup
*/
private final ImmutableList<String> lines;
public final String raw;
public SourceLines(String source) {
this.lineStarts = createLineLookup(source);
this.lines = getLines(source, lineStarts);
public SourceLines(String raw) {
this.raw = raw;
this.lineStarts = createLineLookup(raw);
this.lines = getLines(raw, lineStarts);
}
public int getLineCount() {
@ -38,7 +42,6 @@ public class SourceLines {
}
public int getLineStart(int lineNo) {
return lineStarts.getInt(lineNo);
}
@ -99,4 +102,23 @@ public class SourceLines {
return builder.build();
}
@Override
public String toString() {
return raw;
}
@NotNull
@Override
public CharSequence subSequence(int start, int end) {
return raw.subSequence(start, end);
}
public char charAt(int i) {
return raw.charAt(i);
}
public int length() {
return raw.length();
}
}

View file

@ -100,7 +100,7 @@ public class ErrorBuilder {
public ErrorBuilder pointAt(Span span, int ctxLines) {
if (span.lines() == 1) {
SourceLines lines = span.getSourceFile().lines;
SourceLines lines = span.getSourceFile().source;
int spanLine = span.firstLine();

View file

@ -2,15 +2,11 @@ package com.jozufozu.flywheel.core.source.error;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.parse.ShaderFunction;
import com.jozufozu.flywheel.core.source.parse.ShaderStruct;
import com.jozufozu.flywheel.core.source.span.Span;
import com.jozufozu.flywheel.util.FlwUtil;
@ -23,15 +19,15 @@ public class ErrorReporter {
}
public void generateMissingStruct(SourceFile file, Span vertexName, CharSequence msg, CharSequence hint) {
Optional<Span> span = file.parent.index.getStructDefinitionsMatching(vertexName)
.stream()
.findFirst()
.map(ShaderStruct::getName);
this.error(msg)
.pointAtFile(file)
.pointAt(vertexName, 1)
.hintIncludeFor(span.orElse(null), hint);
// Optional<Span> span = file.parent.index.getStructDefinitionsMatching(vertexName)
// .stream()
// .findFirst()
// .map(ShaderStruct::getName);
//
// this.error(msg)
// .pointAtFile(file)
// .pointAt(vertexName, 1)
// .hintIncludeFor(span.orElse(null), hint);
}
public void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg) {
@ -39,14 +35,14 @@ public class ErrorReporter {
}
public void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg, CharSequence hint) {
Optional<Span> span = file.parent.index.getFunctionDefinitionsMatching(functionName)
.stream()
.findFirst()
.map(ShaderFunction::getName);
this.error(msg)
.pointAtFile(file)
.hintIncludeFor(span.orElse(null), hint);
// Optional<Span> span = file.parent.index.getFunctionDefinitionsMatching(functionName)
// .stream()
// .findFirst()
// .map(ShaderFunction::getName);
//
// this.error(msg)
// .pointAtFile(file)
// .hintIncludeFor(span.orElse(null), hint);
}
public ErrorBuilder generateFunctionArgumentCountError(String name, int requiredArguments, Span span) {

View file

@ -1,13 +0,0 @@
package com.jozufozu.flywheel.core.source.parse;
import com.jozufozu.flywheel.core.source.span.Span;
public abstract class AbstractShaderElement {
public final Span self;
public AbstractShaderElement(Span self) {
this.self = self;
}
}

View file

@ -1,52 +1,19 @@
package com.jozufozu.flywheel.core.source.parse;
import java.util.Optional;
import java.util.regex.Pattern;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.core.source.span.Span;
import net.minecraft.ResourceLocationException;
import net.minecraft.resources.ResourceLocation;
public class Import extends AbstractShaderElement {
public class Import {
public static final Pattern PATTERN = Pattern.compile("^\\s*#\\s*use\\s+\"(.*)\"", Pattern.MULTILINE);
public final FileResolution resolution;
public final Span self;
public final Span file;
protected Import(Span self, FileResolution resolution, Span file) {
super(self);
this.resolution = resolution.addSpan(file);
public Import(Span self, Span file) {
this.self = self;
this.file = file;
}
@Nullable
public static Import create(ErrorReporter errorReporter, Span self, Span file) {
ResourceLocation fileLocation;
try {
fileLocation = new ResourceLocation(file.get());
} catch (ResourceLocationException e) {
errorReporter.generateSpanError(file, "malformed source location");
return null;
}
return new Import(self, FileResolution.weak(fileLocation), file);
}
public Optional<SourceFile> getOptional() {
return Optional.ofNullable(resolution.getFile());
}
@Nullable
public SourceFile getFile() {
return resolution.getFile();
}
public ResourceLocation getFileLoc() {
return resolution.getFileLoc();
}
}

View file

@ -6,16 +6,17 @@ import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.core.source.span.Span;
public class ShaderField extends AbstractShaderElement {
public class ShaderField {
public static final Pattern PATTERN = Pattern.compile("layout\\s*\\(location\\s*=\\s*(\\d+)\\)\\s+(in|out)\\s+([\\w\\d]+)\\s+" + "([\\w\\d]+)");
public final Span location;
public final @Nullable Decoration decoration;
public final Span type;
public final Span name;
public final Span self;
public ShaderField(Span self, Span location, Span inOut, Span type, Span name) {
super(self);
this.self = self;
this.location = location;
this.decoration = Decoration.fromSpan(inOut);

View file

@ -7,13 +7,14 @@ import java.util.stream.Collectors;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.source.span.Span;
public class ShaderFunction extends AbstractShaderElement {
public class ShaderFunction {
// https://regexr.com/60n3d
public static final Pattern PATTERN = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{");
public static final Pattern argument = Pattern.compile("(?:(inout|in|out) )?(\\w+)\\s+(\\w+)");
public static final Pattern assignment = Pattern.compile("(\\w+)\\s*=");
public final Span self;
private final Span type;
private final Span name;
@ -23,7 +24,7 @@ public class ShaderFunction extends AbstractShaderElement {
private final ImmutableList<ShaderVariable> parameters;
public ShaderFunction(Span self, Span type, Span name, Span args, Span body) {
super(self);
this.self = self;
this.type = type;
this.name = name;
this.args = args;

View file

@ -7,19 +7,20 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.core.source.span.Span;
public class ShaderStruct extends AbstractShaderElement {
public class ShaderStruct {
// https://regexr.com/61rpe
public static final Pattern PATTERN = Pattern.compile("struct\\s+([\\w\\d]*)\\s*\\{([\\w\\d\\s,;]*)}\\s*;\\s");
public final Span name;
public final Span body;
public final Span self;
private final ImmutableList<StructField> fields;
private final ImmutableMap<String, Span> fields2Types;
public ShaderStruct(Span self, Span name, Span body) {
super(self);
this.self = self;
this.name = name;
this.body = body;
this.fields = parseFields();

View file

@ -2,15 +2,16 @@ package com.jozufozu.flywheel.core.source.parse;
import com.jozufozu.flywheel.core.source.span.Span;
public class ShaderVariable extends AbstractShaderElement {
public class ShaderVariable {
public final Span qualifierSpan;
public final Span type;
public final Span name;
public final Qualifier qualifier;
public final Span self;
public ShaderVariable(Span self, Span qualifier, Span type, Span name) {
super(self);
this.self = self;
this.qualifierSpan = qualifier;
this.type = type;
this.name = name;

View file

@ -4,14 +4,15 @@ import java.util.regex.Pattern;
import com.jozufozu.flywheel.core.source.span.Span;
public class StructField extends AbstractShaderElement {
public class StructField {
public static final Pattern fieldPattern = Pattern.compile("(\\S+)\\s*(\\S+);");
public final Span self;
public Span type;
public Span name;
public StructField(Span self, Span type, Span name) {
super(self);
this.self = self;
this.type = type;
this.name = name;
}

View file

@ -23,7 +23,7 @@ public abstract class Span implements CharSequence, Comparable<Span> {
protected final CharPos end;
public Span(SourceFile in, int start, int end) {
this(in, in.lines.getCharPos(start), in.lines.getCharPos(end));
this(in, in.source.getCharPos(start), in.source.getCharPos(end));
}
public Span(SourceFile in, CharPos start, CharPos end) {

View file

@ -19,8 +19,7 @@ public class StringSpan extends Span {
@Override
public String get() {
return in.source
.substring(start.pos(), end.pos());
return in.source.raw.substring(start.pos(), end.pos());
}
@Override

View file

@ -27,4 +27,9 @@ public class ResourceUtil {
return UNSAFE_CHARS.matcher(rl.toString())
.replaceAll("_");
}
public static ResourceLocation prefixed(String basePath, ResourceLocation resourceLocation) {
String path = resourceLocation.getPath();
return new ResourceLocation(resourceLocation.getNamespace(), basePath + path);
}
}

View file

@ -13,6 +13,8 @@ import java.text.NumberFormat;
import java.util.Arrays;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.memory.FlwMemoryTracker;
@ -65,7 +67,8 @@ public class StringUtil {
return value.substring(0, len);
}
public static String readToString(InputStream is) {
@Nonnull
public static String readToString(InputStream is) throws IOException {
ByteBuffer bytebuffer = null;
try {
@ -73,17 +76,14 @@ public class StringUtil {
int i = bytebuffer.position();
((Buffer) bytebuffer).rewind();
return MemoryUtil.memASCII(bytebuffer, i);
} catch (IOException ignored) {
//
} finally {
if (bytebuffer != null) {
FlwMemoryTracker.freeBuffer(bytebuffer);
}
}
return null;
}
@Nonnull
public static ByteBuffer readToBuffer(InputStream is) throws IOException {
if (is instanceof FileInputStream fileinputstream) {
return readFileInputStream(fileinputstream);
@ -92,6 +92,7 @@ public class StringUtil {
}
}
@Nonnull
private static ByteBuffer readInputStream(InputStream is) throws IOException {
ByteBuffer bytebuffer = FlwMemoryTracker.mallocBuffer(8192);
ReadableByteChannel readablebytechannel = Channels.newChannel(is);
@ -104,6 +105,7 @@ public class StringUtil {
return bytebuffer;
}
@Nonnull
private static ByteBuffer readFileInputStream(FileInputStream fileinputstream) throws IOException {
FileChannel filechannel = fileinputstream.getChannel();
ByteBuffer bytebuffer = FlwMemoryTracker.mallocBuffer((int) filechannel.size() + 1);