Consolidated compilation

- Note: heavily broken
 - Move all compilation logic into a single package
 - FlwCompiler is responsible for compilation
 - CompilationEnvironment generates combinations and performs analysis
 - Create source components for vertex/fragment ubershader codegen
 - More hacky glsl generation utils
 - Strip explicit uniform buffers from uniform shaders
This commit is contained in:
Jozufozu 2022-09-29 20:41:44 -07:00
parent 79464361d2
commit 28e16a7810
52 changed files with 1113 additions and 779 deletions

View file

@ -7,14 +7,13 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.RenderWork;
import com.jozufozu.flywheel.backend.ShadersModHandler;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer;
import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler;
import com.jozufozu.flywheel.config.BackendTypeArgument;
import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.BackendTypes;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.DebugRender;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.core.StitchedSprite;
@ -81,7 +80,7 @@ public class Flywheel {
forgeEventBus.addListener(FlwCommands::registerClientCommands);
forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onReloadRenderers);
forgeEventBus.addListener(PipelineCompiler::onReloadRenderers);
forgeEventBus.addListener(FlwCompiler::onReloadRenderers);
forgeEventBus.addListener(Models::onReloadRenderers);
forgeEventBus.addListener(DrawBuffer::onReloadRenderers);
@ -108,7 +107,6 @@ public class Flywheel {
// forgeEventBus.addListener(ExampleEffect::onReload);
Components.init();
DebugRender.init();
VanillaInstances.init();

View file

@ -2,21 +2,21 @@ package com.jozufozu.flywheel.api.uniform;
import com.jozufozu.flywheel.core.source.FileResolution;
public abstract class UniformProvider {
public interface UniformProvider {
protected long ptr;
protected Notifier notifier;
int byteSize();
public abstract int getActualByteSize();
FileResolution uniformShader();
public void updatePtr(long ptr, Notifier notifier) {
this.ptr = ptr;
this.notifier = notifier;
ActiveUniformProvider activate(long ptr, Notifier notifier);
interface ActiveUniformProvider {
void delete();
void poll();
}
public abstract FileResolution getUniformShader();
public interface Notifier {
interface Notifier {
void signalChanged();
}
}

View file

@ -1,5 +1,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 net.minecraft.network.chat.Component;
@ -17,4 +20,6 @@ public interface BackendType {
BackendType findFallback();
boolean supported();
@Nullable PipelineShader pipelineShader();
}

View file

@ -1,23 +1,10 @@
package com.jozufozu.flywheel.backend;
import java.util.List;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.pipeline.PipelineShader;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
import com.jozufozu.flywheel.backend.instancing.indirect.ComputeCullerCompiler;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.compile.ShaderCompilationException;
import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
import com.jozufozu.flywheel.core.source.ShaderSources;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.util.StringUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
@ -68,46 +55,7 @@ public class Loader implements ResourceManagerReloadListener {
Backend.LOGGER.info("All shaders passed checks.");
long compileStart = System.nanoTime();
int programCounter = 0;
boolean crash = false;
for (Material material : ComponentRegistry.materials) {
for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) {
for (ContextShader contextShader : ComponentRegistry.contextShaders) {
for (PipelineShader pipelineShader : List.of(Components.INSTANCED_ARRAYS, Components.INDIRECT)) {
var ctx = new PipelineCompiler.Context(vertexType, material, structType, contextShader, pipelineShader);
try {
PipelineCompiler.INSTANCE.getProgram(ctx);
} catch (ShaderCompilationException e) {
Backend.LOGGER.error(e.errors);
crash = true;
}
programCounter++;
}
}
}
}
}
for (StructType<?> structType : ComponentRegistry.structTypes) {
try {
ComputeCullerCompiler.INSTANCE.get(structType);
} catch (ShaderCompilationException e) {
Backend.LOGGER.error(e.errors);
crash = true;
}
programCounter++;
}
long compileEnd = System.nanoTime();
Backend.LOGGER.info("Compiled " + programCounter + " programs in " + StringUtil.formatTime(compileEnd - compileStart));
if (crash) {
throw new ShaderLoadingException("Compilation failed");
}
FlwCompiler.INSTANCE.run();
ClientLevel level = Minecraft.getInstance().level;
if (Backend.canUseInstancing(level)) {

View file

@ -3,6 +3,9 @@ package com.jozufozu.flywheel.backend;
import java.util.function.BooleanSupplier;
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;
@ -17,14 +20,16 @@ public class SimpleBackendType implements BackendType {
private final Supplier<Engine> engineSupplier;
private final Supplier<BackendType> fallback;
private final BooleanSupplier isSupported;
private final PipelineShader pipelineShader;
public SimpleBackendType(String properName, String shortName, Component engineMessage, Supplier<Engine> engineSupplier, Supplier<BackendType> fallback, BooleanSupplier isSupported) {
public SimpleBackendType(String properName, String shortName, Component engineMessage, Supplier<Engine> engineSupplier, Supplier<BackendType> fallback, BooleanSupplier isSupported, @Nullable PipelineShader pipelineShader) {
this.properName = properName;
this.shortName = shortName;
this.engineMessage = engineMessage;
this.engineSupplier = engineSupplier;
this.fallback = fallback;
this.isSupported = isSupported;
this.pipelineShader = pipelineShader;
}
public static Builder builder() {
@ -66,6 +71,11 @@ public class SimpleBackendType implements BackendType {
return isSupported.getAsBoolean();
}
@Override
public @Nullable PipelineShader pipelineShader() {
return pipelineShader;
}
public static class Builder {
private String properName;
private String shortName;
@ -73,6 +83,7 @@ public class SimpleBackendType implements BackendType {
private Supplier<Engine> engineSupplier;
private Supplier<BackendType> fallback;
private BooleanSupplier isSupported;
private PipelineShader pipelineShader;
public Builder properName(String properName) {
this.properName = properName;
@ -104,8 +115,13 @@ public class SimpleBackendType implements BackendType {
return this;
}
public Builder pipelineShader(PipelineShader pipelineShader) {
this.pipelineShader = pipelineShader;
return this;
}
public BackendType register() {
return BackendTypes.register(new SimpleBackendType(properName, shortName, engineMessage, engineSupplier, fallback, isSupported));
return BackendTypes.register(new SimpleBackendType(properName, shortName, engineMessage, engineSupplier, fallback, isSupported, pipelineShader));
}
}
}

View file

@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.gl.shader;
import static org.lwjgl.opengl.GL20.glDeleteProgram;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
import org.jetbrains.annotations.NotNull;
@ -11,17 +10,13 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlObject;
import com.mojang.blaze3d.shaders.ProgramManager;
import net.minecraft.resources.ResourceLocation;
public class GlProgram extends GlObject {
public final ResourceLocation name;
public GlProgram(ResourceLocation name, int handle) {
this.name = name;
public GlProgram(int handle) {
setHandle(handle);
}
// TODO: Programs bind the uniform buffers they need
public void bind() {
ProgramManager.glUseProgram(handle());
}
@ -40,7 +35,7 @@ public class GlProgram extends GlObject {
int index = glGetUniformLocation(this.handle(), uniform);
if (index < 0) {
Backend.LOGGER.debug("No active uniform '{}' exists in program '{}'. Could be unused.", uniform, this.name);
Backend.LOGGER.debug("No active uniform '{}' exists. Could be unused.", uniform);
}
return index;
@ -69,17 +64,11 @@ public class GlProgram extends GlObject {
glDeleteProgram(handle);
}
@Override
public String toString() {
return "program " + name;
}
/**
* A factory interface to create a {@link GlProgram}.
*/
public interface Factory {
@NotNull
GlProgram create(ResourceLocation name, int handle);
@NotNull GlProgram create(int handle);
}
}

View file

@ -10,7 +10,7 @@ import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlObject;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.core.compile.ShaderCompilationException;
import com.jozufozu.flywheel.backend.instancing.compile.ShaderCompilationException;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;

View file

@ -1,178 +0,0 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.LinkedHashSet;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.pipeline.PipelineShader;
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.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.compile.CompileUtil;
import com.jozufozu.flywheel.core.compile.Memoizer;
import com.jozufozu.flywheel.core.compile.ProgramAssembler;
import com.jozufozu.flywheel.core.compile.ShaderCompilationException;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import net.minecraft.resources.ResourceLocation;
/**
* A caching compiler.
*
* <p>
* This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of
* compiled programs, and will only compile a program if it is not already in the cache.
* </p>
* <p>
* A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload.
* </p>
*/
public class PipelineCompiler extends Memoizer<PipelineCompiler.Context, GlProgram> {
public static final PipelineCompiler INSTANCE = new PipelineCompiler();
private final ShaderCompiler shaderCompiler;
private PipelineCompiler() {
this.shaderCompiler = new ShaderCompiler();
}
/**
* Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec.
*
* @param ctx The context of compilation.
* @return A compiled GlProgram.
*/
public GlProgram getProgram(PipelineCompiler.Context ctx) {
return super.get(ctx);
}
@Override
public void invalidate() {
super.invalidate();
shaderCompiler.invalidate();
}
@Override
protected GlProgram _create(PipelineCompiler.Context ctx) {
var glslVersion = ctx.pipelineShader()
.glslVersion();
var vertex = new ShaderCompiler.Context(glslVersion, ShaderType.VERTEX, ctx.getVertexComponents());
var fragment = new ShaderCompiler.Context(glslVersion, ShaderType.FRAGMENT, ctx.getFragmentComponents());
return new ProgramAssembler(ctx.structType.getInstanceShader()
.getFileLoc()).attachShader(shaderCompiler.get(vertex))
.attachShader(shaderCompiler.get(fragment))
.link()
.build(ctx.contextShader.factory());
}
@Override
protected void _destroy(GlProgram value) {
value.delete();
}
public static void onReloadRenderers(ReloadRenderersEvent event) {
INSTANCE.invalidate();
}
/**
* Represents the entire context of a program's usage.
*
* @param vertexType The vertexType the program should be adapted for.
* @param material The material shader to use. TODO: Flatten materials
* @param structType The instance shader to use.
* @param contextShader The context shader to use.
*/
public record Context(VertexType vertexType, Material material, StructType<?> structType,
ContextShader contextShader, PipelineShader pipelineShader) {
ImmutableList<SourceComponent> getVertexComponents() {
var layout = vertexType.getLayoutShader()
.getFile();
var instanceAssembly = pipelineShader.assemble(vertexType, structType);
var instance = structType.getInstanceShader()
.getFile();
var material = this.material.getVertexShader()
.getFile();
var context = contextShader.getVertexShader();
var pipeline = pipelineShader.vertex()
.getFile();
return ImmutableList.of(layout, instanceAssembly, instance, material, context, pipeline);
}
ImmutableList<SourceComponent> getFragmentComponents() {
var material = this.material.getFragmentShader()
.getFile();
var context = contextShader.getFragmentShader();
var pipeline = pipelineShader.fragment()
.getFile();
return ImmutableList.of(material, context, pipeline);
}
}
/**
* Handles compilation and deletion of vertex shaders.
*/
public static class ShaderCompiler extends Memoizer<ShaderCompiler.Context, GlShader> {
private ShaderCompiler() {
}
@Override
protected GlShader _create(Context 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);
}
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
/**
* @param glslVersion The GLSL version to use.
* @param sourceComponents A list of shader components to stitch together, in order.
*/
public record Context(GLSLVersion glslVersion, ShaderType shaderType, List<SourceComponent> sourceComponents) {
public String generateHeader() {
return CompileUtil.generateHeader(glslVersion, shaderType);
}
}
}
}

View file

@ -0,0 +1,59 @@
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,4 +1,4 @@
package com.jozufozu.flywheel.core.compile;
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

View file

@ -0,0 +1,129 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
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.vertex.VertexType;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
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.ComponentRegistry;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import net.minecraft.resources.ResourceLocation;
public class FlwCompiler {
public static final FlwCompiler INSTANCE = new FlwCompiler();
public static void onReloadRenderers(ReloadRenderersEvent t) {
}
private final ShaderCompiler shaderCompiler = new ShaderCompiler();
public final Map<PipelineContext, GlProgram> pipelinePrograms = new HashMap<>();
public final Map<StructType<?>, GlProgram> cullingPrograms = new HashMap<>();
private CompilationEnvironment environment;
FlwCompiler() {
}
public void run() {
environment = new CompilationEnvironment();
for (PipelineContext context : environment.pipelineContexts) {
try {
var glProgram = compilePipelineContext(context);
pipelinePrograms.put(context, glProgram);
} catch (ShaderCompilationException e) {
environment.needsCrash = true;
Backend.LOGGER.error(e.errors);
}
}
for (StructType<?> type : ComponentRegistry.structTypes) {
try {
var glProgram = compileComputeCuller(type);
cullingPrograms.put(type, glProgram);
} catch (ShaderCompilationException e) {
environment.needsCrash = true;
Backend.LOGGER.error(e.errors);
}
}
environment.finish();
}
public GlProgram getPipelineProgram(VertexType vertexType, StructType<?> structType, ContextShader contextShader, PipelineShader pipelineShader) {
return pipelinePrograms.get(new PipelineContext(vertexType, structType, contextShader, pipelineShader));
}
public GlProgram getCullingProgram(StructType<?> structType) {
return cullingPrograms.get(structType);
}
protected GlProgram compilePipelineContext(PipelineContext ctx) throws ShaderCompilationException {
var glslVersion = ctx.pipelineShader()
.glslVersion();
var vertex = new ShaderContext(glslVersion, ShaderType.VERTEX, ctx.getVertexComponents());
var fragment = new ShaderContext(glslVersion, ShaderType.FRAGMENT, ctx.getFragmentComponents());
return ctx.contextShader()
.factory()
.create(new ProgramAssembler().attachShader(shaderCompiler.get(vertex))
.attachShader(shaderCompiler.get(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());
var names = ImmutableList.<ResourceLocation>builder();
var included = new LinkedHashSet<SourceComponent>(); // linked to preserve order
for (var component : components) {
included.addAll(component.included());
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);
} catch (ShaderCompilationException e) {
throw e.withErrorLog(context);
}
}
}

View file

@ -0,0 +1,49 @@
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.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class FragmentMaterialComponent implements SourceComponent {
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 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;
}
@Override
public ResourceLocation name() {
return null;
}
}

View file

@ -0,0 +1,65 @@
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;
/**
* Represents the entire context of a program's usage.
*
* @param vertexType The vertexType the program should be adapted for.
* @param structType The instance shader to use.
* @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);
}
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.core.compile;
package com.jozufozu.flywheel.backend.instancing.compile;
import static org.lwjgl.opengl.GL11.GL_TRUE;
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
@ -9,30 +9,26 @@ import static org.lwjgl.opengl.GL20.glGetProgrami;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import net.minecraft.resources.ResourceLocation;
@Deprecated
public class ProgramAssembler {
public final int program;
private final ResourceLocation name;
private final int program;
public ProgramAssembler(ResourceLocation name) {
this.name = name;
public ProgramAssembler() {
this.program = glCreateProgram();
}
/**
* Links the attached shaders to this program.
*/
public ProgramAssembler link() {
public int link() {
glLinkProgram(this.program);
String log = glGetProgramInfoLog(this.program);
if (!log.isEmpty()) {
Backend.LOGGER.debug("Program link log for " + name + ": " + log);
Backend.LOGGER.debug("Program link log: " + log);
}
int result = glGetProgrami(this.program, GL_LINK_STATUS);
@ -41,15 +37,11 @@ public class ProgramAssembler {
throw new RuntimeException("Shader program linking failed, see log for details");
}
return this;
return program;
}
public ProgramAssembler attachShader(GlShader glShader) {
glAttachShader(this.program, glShader.handle());
return this;
}
public GlProgram build(GlProgram.Factory factory) {
return factory.create(name, program);
}
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.core.compile;
package com.jozufozu.flywheel.backend.instancing.compile;
import org.lwjgl.opengl.GL20;

View file

@ -0,0 +1,64 @@
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

@ -0,0 +1,18 @@
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

@ -0,0 +1,37 @@
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

@ -0,0 +1,71 @@
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.generate.GlslExpr;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class VertexMaterialComponent implements SourceComponent {
private static final String flw_materialVertex = "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();
}
@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

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.instancing.compile;
import javax.annotation.ParametersAreNonnullByDefault;
import net.minecraft.MethodsReturnNonnullByDefault;

View file

@ -1,74 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.indirect;
import java.util.LinkedHashSet;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
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.core.Components;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.compile.CompileUtil;
import com.jozufozu.flywheel.core.compile.Memoizer;
import com.jozufozu.flywheel.core.compile.ProgramAssembler;
import com.jozufozu.flywheel.core.compile.ShaderCompilationException;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import net.minecraft.resources.ResourceLocation;
public class ComputeCullerCompiler extends Memoizer<StructType<?>, GlProgram> {
public static final ComputeCullerCompiler INSTANCE = new ComputeCullerCompiler();
private ComputeCullerCompiler() {
}
@Override
protected GlProgram _create(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());
var names = ImmutableList.<ResourceLocation>builder();
var included = new LinkedHashSet<SourceComponent>(); // linked to preserve order
for (var component : components) {
included.addAll(component.included());
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));
return new ProgramAssembler(fileLoc).attachShader(shader)
.link()
.build(GlProgram::new);
} catch (ShaderCompilationException e) {
throw e.withErrorLog(context);
}
}
@Override
protected void _destroy(GlProgram value) {
value.delete();
}
public static void invalidateAll(ReloadRenderersEvent ignored) {
INSTANCE.invalidate();
}
}

View file

@ -2,7 +2,6 @@ package com.jozufozu.flywheel.backend.instancing.indirect;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.Components;
@ -60,17 +59,13 @@ public class IndirectComponent implements SourceComponent {
builder.blankLine();
var func = builder.function()
builder.function()
.returnType(structName)
.name("flw_unpackInstance")
.argumentIn(packedStructName, UNPACK_ARG);
var args = layoutItems.stream()
.map(layoutItem -> layoutItem.unpackField(UNPACKING_VARIABLE))
.map(GlslExpr::minPrint)
.collect(Collectors.joining(", "));
func.statement("return " + structName + "(" + args + ");");
.argumentIn(packedStructName, UNPACK_ARG)
.body(b -> b.ret(GlslExpr.call(structName, layoutItems.stream()
.map(layoutItem -> layoutItem.unpackField(UNPACKING_VARIABLE))
.toList())));
return builder.build();
}

View file

@ -17,11 +17,9 @@ import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
public class IndirectCullingGroup<T extends InstancedPart> {
@ -65,8 +63,8 @@ public class IndirectCullingGroup<T extends InstancedPart> {
.quads2Tris(2048).buffer.handle();
setupVertexArray();
compute = ComputeCullerCompiler.INSTANCE.get(structType);
draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.SHULKER, structType, Components.WORLD, Components.INDIRECT));
compute = FlwCompiler.INSTANCE.getCullingProgram(structType);
draw = FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, Components.WORLD, Components.INDIRECT);
}
private void setupVertexArray() {
@ -116,9 +114,6 @@ public class IndirectCullingGroup<T extends InstancedPart> {
uploadInstanceData();
uploadIndirectCommands();
UniformBuffer.getInstance()
.sync();
compute.bind();
buffers.bindAll();
@ -137,9 +132,6 @@ public class IndirectCullingGroup<T extends InstancedPart> {
buffers.bindObjectAndTarget();
buffers.bindIndirectBuffer();
UniformBuffer.getInstance()
.sync();
memoryBarrier();
drawSet.submit(stage);

View file

@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.SourceComponent;
@ -68,16 +67,13 @@ public class InstancedArraysComponent implements SourceComponent {
builder.blankLine();
var func = builder.function()
// unpacking function
builder.function()
.returnType(structName)
.name("flw_unpackInstance");
var args = layoutItems.stream()
.map(it -> new GlslExpr.Variable(it.name() + ATTRIBUTE_SUFFIX))
.map(GlslExpr::minPrint)
.collect(Collectors.joining(", "));
func.statement("return " + structName + "(" + args + ");");
.name("flw_unpackInstance")
.body(b -> b.ret(GlslExpr.call(structName, layoutItems.stream()
.map(it -> new GlslExpr.Variable(it.name() + ATTRIBUTE_SUFFIX))
.toList())));
return builder.build();
}

View file

@ -16,11 +16,10 @@ import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
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.RenderContext;
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
import com.jozufozu.flywheel.util.WeakHashSet;
import com.mojang.blaze3d.systems.RenderSystem;
@ -124,12 +123,8 @@ public class InstancingEngine implements Engine {
var structType = desc.instance();
var material = desc.material();
var ctx = new PipelineCompiler.Context(vertexType, material, structType, context, Components.INSTANCED_ARRAYS);
PipelineCompiler.INSTANCE.getProgram(ctx)
FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, context, Components.INSTANCED_ARRAYS)
.bind();
UniformBuffer.getInstance()
.sync();
}
@Override

View file

@ -4,9 +4,12 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
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;
@ -55,6 +58,7 @@ public class BackendTypes {
.fallback(() -> BackendTypes.BATCHING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
.instancedArraysSupported())
.pipelineShader(Components.INSTANCED_ARRAYS)
.register();
/**
@ -68,6 +72,7 @@ public class BackendTypes {
.fallback(() -> BackendTypes.INSTANCING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
.supportsIndirect())
.pipelineShader(Components.INDIRECT)
.register();
public static BackendType register(BackendType type) {
@ -94,4 +99,12 @@ public class BackendTypes {
}
public static Collection<PipelineShader> availablePipelineShaders() {
return BACKEND_TYPES.values()
.stream()
.filter(BackendType::supported)
.map(BackendType::pipelineShader)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}

View file

@ -3,22 +3,29 @@ package com.jozufozu.flywheel.core;
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.Set;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.material.Material;
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.api.context.ContextShader;
import com.jozufozu.flywheel.core.source.FileResolution;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import net.minecraft.resources.ResourceLocation;
public class ComponentRegistry {
private static final Registry<UniformProvider> uniformProviders = new Registry<>();
public static final Set<Material> materials = new HashSet<>();
public static final MaterialRegistry materials = new MaterialRegistry();
public static final Set<StructType<?>> structTypes = new HashSet<>();
public static final Set<VertexType> vertexTypes = new HashSet<>();
public static final Set<ContextShader> contextShaders = new HashSet<>();
@ -26,8 +33,7 @@ public class ComponentRegistry {
// TODO: fill out the rest of the registry
public static <T extends Material> T register(T material) {
materials.add(material);
return material;
return materials.add(material);
}
public static <T extends StructType<?>> T register(T type) {
@ -46,7 +52,7 @@ public class ComponentRegistry {
}
public static <T extends UniformProvider> T register(T provider) {
return uniformProviders.register(provider.getUniformShader()
return uniformProviders.register(provider.uniformShader()
.getFileLoc(), provider);
}
@ -54,17 +60,71 @@ public class ComponentRegistry {
return Collections.unmodifiableCollection(uniformProviders.objects);
}
@Nullable
public static UniformProvider getUniformProvider(ResourceLocation loc) {
return uniformProviders.get(loc);
}
private static class Registry<T> {
private final Set<ResourceLocation> files = new HashSet<>();
private final Map<ResourceLocation, T> files = new HashMap<>();
private final List<T> objects = new ArrayList<>();
public <O extends T> O register(ResourceLocation loc, O object) {
if (files.contains(loc)) {
if (files.containsKey(loc)) {
throw new IllegalArgumentException("Shader file already registered: " + loc);
}
files.add(loc);
files.put(loc, object);
objects.add(object);
return object;
}
@Nullable
public T get(ResourceLocation loc) {
return files.get(loc);
}
}
public static class MaterialRegistry {
private final Set<Material> materials = new HashSet<>();
private final MaterialSources vertexSources = new MaterialSources();
private final MaterialSources fragmentSources = new MaterialSources();
public <T extends Material> T add(T material) {
materials.add(material);
vertexSources.register(material, material.getVertexShader());
fragmentSources.register(material, material.getFragmentShader());
return material;
}
/**
* @return a list of vertex shader sources where the index in the list is the shader's ID.
*/
public List<FileResolution> vertexSources() {
return vertexSources.sourceView;
}
/**
* @return a list of fragment shader sources where the index in the list is the shader's ID.
*/
public List<FileResolution> fragmentSources() {
return fragmentSources.sourceView;
}
private static class MaterialSources {
private final Set<FileResolution> registered = new HashSet<>();
private final List<FileResolution> orderedSources = new ArrayList<>();
private final Reference2IntMap<Material> material2ID = new Reference2IntOpenHashMap<>();
private final List<FileResolution> sourceView = Collections.unmodifiableList(orderedSources);
public void register(Material material, FileResolution vertexShader) {
if (registered.add(vertexShader)) {
material2ID.put(material, orderedSources.size());
orderedSources.add(vertexShader);
}
}
}
}
}

View file

@ -1,104 +0,0 @@
package com.jozufozu.flywheel.core;
import org.lwjgl.opengl.GL46;
import org.lwjgl.system.MemoryStack;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.compile.DebugCompiler;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.util.Lazy;
import com.jozufozu.flywheel.util.joml.FrustumIntersection;
import com.mojang.blaze3d.systems.RenderSystem;
public class DebugRender {
private static final Lazy<GlProgram> SHADER = Lazy.of(() -> DebugCompiler.INSTANCE.get(new DebugCompiler.Context(Files.VERTEX, Files.FRAGMENT)));
private static final Lazy<Frustum> FRUSTUM_VBO = Lazy.of(Frustum::new);
public static void init() {
Files.init();
}
public static void updateFrustum(FrustumIntersection culler) {
FRUSTUM_VBO.get()
.upload(culler);
}
public static void drawFrustum() {
if (!FRUSTUM_VBO.isInitialized()) {
return;
}
RenderSystem.disableCull();
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
try (var ignored = GlStateTracker.getRestoreState()) {
SHADER.get()
.bind();
FRUSTUM_VBO.get()
.draw();
}
}
public static class Files {
public static final FileResolution VERTEX = FileResolution.get(Flywheel.rl("debug/debug.vert"));
public static final FileResolution FRAGMENT = FileResolution.get(Flywheel.rl("debug/debug.frag"));
public static void init() {
}
}
// FIXME: This never worked (and the thing it was meant to debug is already fixed),
// but it should be a quick turnaround
private static class Frustum {
private static final int[] indices = new int[]{
0, 2, 3, 0, 3, 1,
2, 6, 7, 2, 7, 3,
6, 4, 5, 6, 5, 7,
4, 0, 1, 4, 1, 5,
0, 4, 6, 0, 6, 2,
1, 5, 7, 1, 7, 3,
};
private static final int elementCount = indices.length;
private static final int indicesSize = elementCount * 4;
private static final int verticesSize = 3 * 8 * 4;
private final int buffer;
private final int vao;
public Frustum() {
// holy moly DSA is nice
buffer = GL46.glCreateBuffers();
GL46.glNamedBufferStorage(buffer, verticesSize + indicesSize, GL46.GL_DYNAMIC_STORAGE_BIT);
GL46.glNamedBufferSubData(buffer, 0, indices);
vao = GL46.glCreateVertexArrays();
GL46.glEnableVertexArrayAttrib(vao, 0);
GL46.glVertexArrayElementBuffer(vao, buffer);
GL46.glVertexArrayVertexBuffer(vao, 0, buffer, indicesSize, 3 * 4);
GL46.glVertexArrayAttribFormat(vao, 0, 3, GL46.GL_FLOAT, false, 0);
}
public void upload(FrustumIntersection culler) {
try (var stack = MemoryStack.stackPush()) {
var buf = stack.malloc(3 * 8 * 4);
culler.getCorners(buf);
GL46.glNamedBufferSubData(buffer, indicesSize, buf);
}
}
public void draw() {
GL46.glEnableVertexArrayAttrib(vao, 0);
GL46.glVertexArrayElementBuffer(vao, buffer);
GL46.glBindVertexArray(vao);
GL46.glDrawElements(GL46.GL_TRIANGLES, elementCount, GL46.GL_UNSIGNED_INT, 0);
}
}
}

View file

@ -2,8 +2,6 @@ package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import net.minecraft.resources.ResourceLocation;
public class WorldProgram extends GlProgram {
// TODO: sampler registry?
@ -11,8 +9,8 @@ public class WorldProgram extends GlProgram {
protected int overlayTex;
protected int lightTex;
public WorldProgram(ResourceLocation name, int handle) {
super(name, handle);
public WorldProgram(int handle) {
super(handle);
bind();
registerSamplers();

View file

@ -1,95 +0,0 @@
package com.jozufozu.flywheel.core.compile;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
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.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
/**
* Simple shader compiler that pulls no excessive tricks.<p>
* Useful for writing experimental shaders or
*/
public class DebugCompiler extends Memoizer<DebugCompiler.Context, GlProgram> {
public static final DebugCompiler INSTANCE = new DebugCompiler();
private final ShaderCompiler shaderCompiler;
private DebugCompiler() {
this.shaderCompiler = new ShaderCompiler();
}
@Override
public void invalidate() {
super.invalidate();
shaderCompiler.invalidate();
}
@Override
protected GlProgram _create(DebugCompiler.Context ctx) {
return new ProgramAssembler(ctx.vertex.getFileLoc())
.attachShader(shaderCompiler.vertex(ctx.vertex))
.attachShader(shaderCompiler.fragment(ctx.fragment))
.link()
.build(GlProgram::new);
}
@Override
protected void _destroy(GlProgram value) {
value.delete();
}
public static void invalidateAll(ReloadRenderersEvent ignored) {
INSTANCE.invalidate();
}
public record Context(FileResolution vertex, FileResolution fragment) {
}
/**
* Handles compilation and deletion of vertex shaders.
*/
private static class ShaderCompiler extends Memoizer<ShaderCompiler.Context, GlShader> {
public GlShader vertex(FileResolution source) {
return get(new Context(source, ShaderType.VERTEX));
}
public GlShader fragment(FileResolution source) {
return get(new Context(source, ShaderType.FRAGMENT));
}
@Override
protected GlShader _create(Context ctx) {
var index = new CompilationContext();
StringBuilder source = new StringBuilder(CompileUtil.generateHeader(GLSLVersion.V420, ctx.type));
var file = ctx.source.getFile();
for (var include : file.flattenedImports) {
source.append(include.source(index));
}
source.append(file.source(index));
try {
return new GlShader(source.toString(), ctx.type, ImmutableList.of(ctx.source.getFileLoc()));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(index);
}
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
public record Context(FileResolution source, ShaderType type) {
}
}
}

View file

@ -1,22 +0,0 @@
package com.jozufozu.flywheel.core.compile;
import java.util.HashMap;
import java.util.Map;
public abstract class Memoizer<K, V> {
private final Map<K, V> map = new HashMap<>();
public V get(K key) {
return map.computeIfAbsent(key, this::_create);
}
public void invalidate() {
map.values().forEach(this::_destroy);
map.clear();
}
protected abstract V _create(K key);
protected abstract void _destroy(V value);
}

View file

@ -2,11 +2,9 @@ package com.jozufozu.flywheel.core.crumbling;
import com.jozufozu.flywheel.core.WorldProgram;
import net.minecraft.resources.ResourceLocation;
public class CrumblingProgram extends WorldProgram {
public CrumblingProgram(ResourceLocation name, int handle) {
super(name, handle);
public CrumblingProgram(int handle) {
super(handle);
}
@Override

View file

@ -27,11 +27,11 @@ public class CompilationContext {
generatedLines += lines;
if (comment != null) {
out += " // " + comment;
if (comment == null) {
comment = "";
}
return out + '\n';
return out + " // (generated) " + comment + '\n';
}
public boolean contains(SourceFile sourceFile) {

View file

@ -80,7 +80,7 @@ public class SourceFile implements SourceComponent {
@Override
public String source(CompilationContext ctx) {
return ctx.sourceHeader(this) + this.elideImports();
return ctx.sourceHeader(this) + this.genFinalSource();
}
@Override
@ -188,7 +188,7 @@ public class SourceFile implements SourceComponent {
return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers();
}
private CharSequence elideImports() {
private CharSequence genFinalSource() {
StringBuilder out = new StringBuilder();
int lastEnd = 0;

View file

@ -2,13 +2,15 @@ package com.jozufozu.flywheel.core.source.generate;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import com.jozufozu.flywheel.util.Pair;
public class GlslBuilder {
public static final String INDENT = " ";
private final List<SourceElement> elements = new ArrayList<>();
private final List<GlslRootElement> elements = new ArrayList<>();
public void define(String name, String value) {
add(new Define(name, value));
@ -26,7 +28,7 @@ public class GlslBuilder {
return add(new VertexInputBuilder());
}
public <T extends SourceElement> T add(T element) {
public <T extends GlslRootElement> T add(T element) {
elements.add(element);
return element;
}
@ -37,15 +39,19 @@ public class GlslBuilder {
public String build() {
return elements.stream()
.map(SourceElement::build)
.map(GlslRootElement::minPrint)
.collect(Collectors.joining("\n"));
}
public interface SourceElement {
String build();
public static String indent(int indentation) {
return INDENT.repeat(indentation);
}
public enum Separators implements SourceElement {
public interface GlslRootElement {
String minPrint();
}
public enum Separators implements GlslRootElement {
BLANK_LINE(""),
;
@ -54,21 +60,21 @@ public class GlslBuilder {
Separators(String separator) {
this.separator = separator;
}
@Override
public String build() {
public String minPrint() {
return separator;
}
}
public record Define(String name, String value) implements SourceElement {
public record Define(String name, String value) implements GlslRootElement {
@Override
public String build() {
public String minPrint() {
return "#define " + name + " " + value;
}
}
public static class VertexInputBuilder implements SourceElement {
public static class VertexInputBuilder implements GlslRootElement {
private int binding;
private String type;
@ -90,12 +96,12 @@ public class GlslBuilder {
}
@Override
public String build() {
public String minPrint() {
return "layout(location = " + binding + ") in " + type + " " + name + ";";
}
}
public static class StructBuilder implements SourceElement {
public static class StructBuilder implements GlslRootElement {
private final List<Pair<String, String>> fields = new ArrayList<>();
private String name;
@ -110,11 +116,11 @@ public class GlslBuilder {
private String buildFields() {
return fields.stream()
.map(p -> '\t' + p.first() + ' ' + p.second() + ';')
.map(p -> INDENT + p.first() + ' ' + p.second() + ';')
.collect(Collectors.joining("\n"));
}
public String build() {
public String minPrint() {
return """
struct %s {
%s
@ -123,9 +129,9 @@ public class GlslBuilder {
}
}
public static class FunctionBuilder implements SourceElement {
public static class FunctionBuilder implements GlslRootElement {
private final List<Pair<String, String>> arguments = new ArrayList<>();
private final List<String> body = new ArrayList<>();
private final BlockBuilder body = new BlockBuilder();
private String returnType;
private String name;
@ -149,24 +155,17 @@ public class GlslBuilder {
return this;
}
public FunctionBuilder statement(String statement) {
this.body.add(statement);
public FunctionBuilder body(Consumer<BlockBuilder> f) {
f.accept(body);
return this;
}
public String build() {
public String minPrint() {
return """
%s %s(%s) {
%s
}
""".formatted(returnType, name, buildArguments(), buildBody());
}
private String buildBody() {
return body.stream()
.map(s -> '\t' + s)
.collect(Collectors.joining("\n"));
""".formatted(returnType, name, buildArguments(), body.prettyPrint());
}
private String buildArguments() {
@ -175,4 +174,61 @@ public class GlslBuilder {
.collect(Collectors.joining(", "));
}
}
public static class BlockBuilder implements LangItem {
private final List<GlslStmt> body = new ArrayList<>();
public BlockBuilder add(GlslStmt stmt) {
body.add(stmt);
return this;
}
public BlockBuilder eval(GlslExpr expr) {
return add(GlslStmt.eval(expr));
}
public BlockBuilder switchOn(GlslExpr expr, Consumer<SwitchBuilder> f) {
var builder = new SwitchBuilder(expr);
f.accept(builder);
return add(builder.build());
}
public void ret(GlslExpr call) {
add(GlslStmt.ret(call));
}
public void break_() {
add(GlslStmt.BREAK);
}
@Override
public String prettyPrint() {
return body.stream()
.map(GlslStmt::prettyPrint)
.collect(Collectors.joining("\n"));
}
}
public static class SwitchBuilder {
private final GlslExpr on;
private final List<Pair<GlslExpr, BlockBuilder>> cases = new ArrayList<>();
public SwitchBuilder(GlslExpr on) {
this.on = on;
}
public SwitchBuilder case_(int expr, Consumer<BlockBuilder> f) {
var builder = new BlockBuilder();
f.accept(builder);
cases.add(Pair.of(GlslExpr.literal(expr), builder));
return this;
}
public GlslStmt.Switch build() {
return new GlslStmt.Switch(on, cases);
}
}
}

View file

@ -1,8 +1,12 @@
package com.jozufozu.flywheel.core.source.generate;
import java.util.Collection;
import java.util.function.Function;
import java.util.stream.Collectors;
public sealed interface GlslExpr {
import com.google.common.collect.ImmutableList;
public interface GlslExpr extends LangItem {
/**
* Create a glsl variable with the given name.
@ -14,7 +18,17 @@ public sealed interface GlslExpr {
return new Variable(name);
}
String minPrint();
static FunctionCall call(String functionName, Collection<? extends GlslExpr> args) {
return new FunctionCall(functionName, args);
}
static FunctionCall0 call(String functionName) {
return new FunctionCall0(functionName);
}
static GlslExpr literal(int expr) {
return new Literal(expr);
}
/**
* Call a one-parameter function with the given name on this expression.
@ -58,29 +72,55 @@ public sealed interface GlslExpr {
record Variable(String name) implements GlslExpr {
@Override
public String minPrint() {
public String prettyPrint() {
return name;
}
}
record FunctionCall(String name, GlslExpr target) implements GlslExpr {
@Override
public String minPrint() {
return name + "(" + target.minPrint() + ")";
record FunctionCall(String name, Collection<? extends GlslExpr> args) implements GlslExpr {
public FunctionCall(String name, GlslExpr target) {
this(name, ImmutableList.of(target));
}
@Override
public String prettyPrint() {
var args = this.args.stream()
.map(GlslExpr::prettyPrint)
.collect(Collectors.joining(","));
return name + "(" + args + ")";
}
}
record FunctionCall0(String name) implements GlslExpr {
@Override
public String prettyPrint() {
return name + "()";
}
}
record Swizzle(GlslExpr target, String selection) implements GlslExpr {
@Override
public String minPrint() {
return target.minPrint() + "." + selection;
public String prettyPrint() {
return target.prettyPrint() + "." + selection;
}
}
record Access(GlslExpr target, String argName) implements GlslExpr {
@Override
public String minPrint() {
return target.minPrint() + "." + argName;
public String prettyPrint() {
return target.prettyPrint() + "." + argName;
}
}
record Literal(int value) implements GlslExpr {
@Override
public String prettyPrint() {
return Integer.toString(value);
}
}
}

View file

@ -0,0 +1,60 @@
package com.jozufozu.flywheel.core.source.generate;
import java.util.List;
import java.util.stream.Collectors;
import com.jozufozu.flywheel.util.Pair;
public interface GlslStmt extends LangItem {
static GlslStmt eval(GlslExpr expr) {
return new Eval(expr);
}
static GlslStmt ret(GlslExpr value) {
return new Return(value);
}
static GlslStmt BREAK = () -> "break;";
static GlslStmt CONTINUE = () -> "continue;";
static GlslStmt RETURN = () -> "return;";
record Eval(GlslExpr expr) implements GlslStmt {
@Override
public String prettyPrint() {
return expr.prettyPrint() + ";";
}
}
record Return(GlslExpr expr) implements GlslStmt {
@Override
public String prettyPrint() {
return "return " + expr.prettyPrint() + ";";
}
}
record Switch(GlslExpr expr, List<Pair<GlslExpr, GlslBuilder.BlockBuilder>> body) implements GlslStmt {
@Override
public String prettyPrint() {
var cases = body.stream()
.map(Switch::prettyPrintCase)
.collect(Collectors.joining("\n"));
return """
switch (%s) {
%s
}""".formatted(expr.prettyPrint(), cases);
}
private static String prettyPrintCase(Pair<GlslExpr, GlslBuilder.BlockBuilder> p) {
var variant = p.first()
.prettyPrint();
var block = p.second()
.prettyPrint();
return """
case %s:
%s""".formatted(variant, block);
}
}
}

View file

@ -0,0 +1,7 @@
package com.jozufozu.flywheel.core.source.generate;
public interface LangItem {
String prettyPrint();
}

View file

@ -1,5 +1,5 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.core.compile;
package com.jozufozu.flywheel.core.source.generate;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -7,33 +7,59 @@ import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.mojang.blaze3d.systems.RenderSystem;
public class FogProvider extends UniformProvider {
public class FogProvider implements UniformProvider {
public static boolean FOG_UPDATE = true;
@Override
public int getActualByteSize() {
public int byteSize() {
return 16 + 8 + 4;
}
public void update() {
if (ptr == MemoryUtil.NULL) {
return;
}
var color = RenderSystem.getShaderFogColor();
MemoryUtil.memPutFloat(ptr, color[0]);
MemoryUtil.memPutFloat(ptr + 4, color[1]);
MemoryUtil.memPutFloat(ptr + 8, color[2]);
MemoryUtil.memPutFloat(ptr + 12, color[3]);
MemoryUtil.memPutFloat(ptr + 16, RenderSystem.getShaderFogStart());
MemoryUtil.memPutFloat(ptr + 20, RenderSystem.getShaderFogEnd());
MemoryUtil.memPutInt(ptr + 24, RenderSystem.getShaderFogShape().getIndex());
notifier.signalChanged();
@Override
public FileResolution uniformShader() {
return Components.Files.FOG_UNIFORMS;
}
@Override
public FileResolution getUniformShader() {
return Components.Files.FOG_UNIFORMS;
public ActiveUniformProvider activate(long ptr, Notifier notifier) {
return new Active(ptr, notifier);
}
public static class Active implements ActiveUniformProvider {
private final long ptr;
private final Notifier notifier;
public Active(long ptr, Notifier notifier) {
this.ptr = ptr;
this.notifier = notifier;
}
@Override
public void delete() {
}
@Override
public void poll() {
if (!FOG_UPDATE) {
return;
}
var color = RenderSystem.getShaderFogColor();
MemoryUtil.memPutFloat(ptr, color[0]);
MemoryUtil.memPutFloat(ptr + 4, color[1]);
MemoryUtil.memPutFloat(ptr + 8, color[2]);
MemoryUtil.memPutFloat(ptr + 12, color[3]);
MemoryUtil.memPutFloat(ptr + 16, RenderSystem.getShaderFogStart());
MemoryUtil.memPutFloat(ptr + 20, RenderSystem.getShaderFogEnd());
MemoryUtil.memPutInt(ptr + 24, RenderSystem.getShaderFogShape()
.getIndex());
notifier.signalChanged();
FOG_UPDATE = false;
}
}
}

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.core.uniform;
import java.util.function.Consumer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.uniform.UniformProvider;
@ -13,47 +15,71 @@ import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.MinecraftForge;
public class FrustumProvider extends UniformProvider {
public class FrustumProvider implements UniformProvider {
public static boolean PAUSED = false;
public static boolean CAPTURE = false;
public FrustumProvider() {
MinecraftForge.EVENT_BUS.addListener(this::beginFrame);
}
@Override
public int getActualByteSize() {
public int byteSize() {
return 96;
}
@Override
public FileResolution getUniformShader() {
public FileResolution uniformShader() {
return Components.Files.FRUSTUM_UNIFORMS;
}
public void beginFrame(BeginFrameEvent event) {
update(event.getContext());
@Override
public ActiveUniformProvider activate(long ptr, Notifier notifier) {
return new Active(ptr, notifier);
}
public void update(RenderContext context) {
if (ptr == MemoryUtil.NULL || (PAUSED && !CAPTURE)) {
return;
static class Active implements ActiveUniformProvider, Consumer<BeginFrameEvent> {
private final long ptr;
private final Notifier notifier;
public Active(long ptr, Notifier notifier) {
this.ptr = ptr;
this.notifier = notifier;
MinecraftForge.EVENT_BUS.addListener(this);
}
Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(context.level());
Vec3 camera = context.camera()
.getPosition();
@Override
public void delete() {
MinecraftForge.EVENT_BUS.unregister(this);
}
var camX = (float) (camera.x - originCoordinate.getX());
var camY = (float) (camera.y - originCoordinate.getY());
var camZ = (float) (camera.z - originCoordinate.getZ());
@Override
public void poll() {
var shiftedCuller = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ);
}
shiftedCuller.getJozuPackedPlanes(ptr);
@Override
public void accept(BeginFrameEvent event) {
update(event.getContext());
}
notifier.signalChanged();
CAPTURE = false;
public void update(RenderContext context) {
if (ptr == MemoryUtil.NULL || (PAUSED && !CAPTURE)) {
return;
}
Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(context.level());
Vec3 camera = context.camera()
.getPosition();
var camX = (float) (camera.x - originCoordinate.getX());
var camY = (float) (camera.y - originCoordinate.getY());
var camZ = (float) (camera.z - originCoordinate.getZ());
var shiftedCuller = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ);
shiftedCuller.getJozuPackedPlanes(ptr);
notifier.signalChanged();
CAPTURE = false;
}
}
}

View file

@ -45,7 +45,7 @@ public class UniformBuffer {
int totalBytes = 0;
int index = 0;
for (UniformProvider provider : providers) {
int size = alignPo2(provider.getActualByteSize(), 16);
int size = align16(provider.byteSize());
builder.add(new Allocated(provider, totalBytes, size, index));
@ -64,6 +64,7 @@ public class UniformBuffer {
}
public void sync() {
allocatedProviders.forEach(Allocated::pollActive);
if (changedBytes.isEmpty()) {
return;
}
@ -88,8 +89,8 @@ public class UniformBuffer {
}
}
private static int alignPo2(int numToRound, int alignment) {
return (numToRound + alignment - 1) & -alignment;
private static int align16(int numToRound) {
return (numToRound + 16 - 1) & -16;
}
private class Allocated implements UniformProvider.Notifier {
@ -97,6 +98,7 @@ public class UniformBuffer {
private final int offset;
private final int size;
private final int index;
private UniformProvider.ActiveUniformProvider activeProvider;
private Allocated(UniformProvider provider, int offset, int size, int index) {
this.provider = provider;
@ -111,7 +113,10 @@ public class UniformBuffer {
}
private void updatePtr(MemoryBlock bufferBase) {
provider.updatePtr(bufferBase.ptr() + offset, this);
if (activeProvider != null) {
activeProvider.delete();
}
activeProvider = provider.activate(bufferBase.ptr() + offset, this);
}
public UniformProvider provider() {
@ -129,5 +134,11 @@ public class UniformBuffer {
public int index() {
return index;
}
public void pollActive() {
if (activeProvider != null) {
activeProvider.poll();
}
}
}
}

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.core.uniform;
import java.util.function.Consumer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.uniform.UniformProvider;
@ -15,54 +17,75 @@ import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.MinecraftForge;
public class ViewProvider extends UniformProvider {
public class ViewProvider implements UniformProvider {
public ViewProvider() {
MinecraftForge.EVENT_BUS.addListener(this::beginFrame);
}
public static final int SIZE = 4 * 16 + 16 + 4;
public void beginFrame(BeginFrameEvent event) {
update(event.getContext());
@Override
public int byteSize() {
return SIZE;
}
@Override
public int getActualByteSize() {
return 4 * 16 + 16 + 4;
}
public void update(RenderContext context) {
if (ptr == MemoryUtil.NULL) {
return;
}
ClientLevel level = context.level();
int constantAmbientLight = level.effects()
.constantAmbientLight() ? 1 : 0;
Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(level);
Vec3 camera = context.camera()
.getPosition();
var camX = (float) (camera.x - originCoordinate.getX());
var camY = (float) (camera.y - originCoordinate.getY());
var camZ = (float) (camera.z - originCoordinate.getZ());
// don't want to mutate viewProjection
var vp = context.viewProjection().copy();
vp.multiplyWithTranslation(-camX, -camY, -camZ);
MatrixWrite.writeUnsafe(vp, ptr);
MemoryUtil.memPutFloat(ptr + 64, camX);
MemoryUtil.memPutFloat(ptr + 68, camY);
MemoryUtil.memPutFloat(ptr + 72, camZ);
MemoryUtil.memPutInt(ptr + 76, constantAmbientLight);
notifier.signalChanged();
}
@Override
public FileResolution getUniformShader() {
public FileResolution uniformShader() {
return Components.Files.VIEW_UNIFORMS;
}
@Override
public ActiveUniformProvider activate(long ptr, Notifier notifier) {
return new Active(ptr, notifier);
}
public static class Active implements ActiveUniformProvider, Consumer<BeginFrameEvent> {
private final long ptr;
private final Notifier notifier;
public Active(long ptr, Notifier notifier) {
this.ptr = ptr;
this.notifier = notifier;
MinecraftForge.EVENT_BUS.addListener(this);
}
@Override
public void delete() {
MinecraftForge.EVENT_BUS.unregister(this);
}
@Override
public void poll() {
}
@Override
public void accept(BeginFrameEvent event) {
update(event.getContext());
}
public void update(RenderContext context) {
ClientLevel level = context.level();
int constantAmbientLight = level.effects()
.constantAmbientLight() ? 1 : 0;
Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(level);
Vec3 camera = context.camera()
.getPosition();
var camX = (float) (camera.x - originCoordinate.getX());
var camY = (float) (camera.y - originCoordinate.getY());
var camZ = (float) (camera.z - originCoordinate.getZ());
// don't want to mutate viewProjection
var vp = context.viewProjection()
.copy();
vp.multiplyWithTranslation(-camX, -camY, -camZ);
MatrixWrite.writeUnsafe(vp, ptr);
MemoryUtil.memPutFloat(ptr + 64, camX);
MemoryUtil.memPutFloat(ptr + 68, camY);
MemoryUtil.memPutFloat(ptr + 72, camZ);
MemoryUtil.memPutInt(ptr + 76, constantAmbientLight);
notifier.signalChanged();
}
}
}

View file

@ -5,14 +5,14 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.uniform.FogProvider;
import net.minecraft.client.renderer.FogRenderer;
@Mixin(FogRenderer.class)
public class FogUpdateMixin {
private static void flywheel$updateFog() {
Components.FOG_PROVIDER.update();
FogProvider.FOG_UPDATE = true;
}
@Inject(method = "setupNoFog", at = @At("TAIL"))

View file

@ -1,6 +1,7 @@
package com.jozufozu.flywheel.util;
import java.util.Objects;
import java.util.function.Function;
public record Pair<F, S>(F first, S second) {
@ -8,6 +9,10 @@ public record Pair<F, S>(F first, S second) {
return new Pair<>(first, second);
}
public static <I, O> Function<Pair<? extends I, ? extends I>, Pair<O, O>> both(Function<I, O> map) {
return pair -> of(map.apply(pair.first), map.apply(pair.second));
}
public Pair<S, F> swap() {
return Pair.of(second, first);
}

View file

@ -1,9 +1,14 @@
package com.jozufozu.flywheel.util;
import java.util.regex.Pattern;
import net.minecraft.resources.ResourceLocation;
public class ResourceUtil {
// Match the complement of alphanumeric and underscore.
private static final Pattern UNSAFE_CHARS = Pattern.compile("[^a-zA-Z0-9_]");
public static ResourceLocation subPath(ResourceLocation root, String subPath) {
return new ResourceLocation(root.getNamespace(), root.getPath() + subPath);
}
@ -17,4 +22,9 @@ public class ResourceUtil {
String path = loc.getPath();
return new ResourceLocation(loc.getNamespace(), path.substring(prefix.length(), path.length() - suffix.length()));
}
public static String toSafeString(ResourceLocation rl) {
return UNSAFE_CHARS.matcher(rl.toString())
.replaceAll("_");
}
}

View file

@ -13,6 +13,8 @@ in vec4 flw_var1;
in vec4 flw_var2;
in vec4 flw_var3;
flat in uint flw_materialFragmentID;
//
/*const*/ vec4 flw_sampleColor;

View file

@ -1,4 +1,6 @@
#ifdef VERTEX_SHADER
uint flw_materialVertexID;
out vec4 flw_vertexPos;
out vec4 flw_vertexColor;
out vec2 flw_vertexTexCoord;
@ -12,4 +14,5 @@ out vec4 flw_var0;
out vec4 flw_var1;
out vec4 flw_var2;
out vec4 flw_var3;
flat out uint flw_materialFragmentID;
#endif

View file

@ -0,0 +1,50 @@
/*
This is what generated ubershaders should look like
uint flw_materialVertexID = 0;
uint flw_materialFragmentID = 0;
void flw_materialVertex() {
switch (flw_materialVertexID) {
case 0: flw_materialVertex_flywheel_cutout_vert(); break;
default: break;
}
}
void flw_materialFragment() {
switch (flw_materialFragmentID) {
case 0: flw_materialFragment_flywheel_cutout_frag(); break;
default: break;
}
}
bool flw_discardPredicate(vec4 finalColor) {
switch (flw_materialFragmentID) {
case 0: return flw_discardPredicate_flywheel_cutout_frag(finalColor);
default: return false;
}
}
vec4 flw_fogFilter(vec4 color) {
switch (flw_materialFragmentID) {
case 0: return flw_fogFilter_flywheel_cutout_frag(color);
default: return color;
}
}
void flw_materialVertex_flywheel_cutout_vert() {
}
void flw_materialFragment_flywheel_cutout_frag() {
}
bool flw_discardPredicate_flywheel_cutout_frag(vec4 finalColor) {
return finalColor.a < 0.1;
}
vec4 flw_fogFilter_flywheel_cutout_frag(vec4 color) {
return linear_fog(color, flw_distance, flw_fogRange.x, flw_fogRange.y, flw_fogColor);
}
*/

View file

@ -1,5 +1,4 @@
layout(std140, binding = 1) uniform flw_fog {
vec4 flw_fogColor;
vec2 flw_fogRange;
int flw_fogShape;
};
// TODO: Transform uniform shaders
vec4 flw_fogColor;
vec2 flw_fogRange;
int flw_fogShape;

View file

@ -9,6 +9,4 @@ struct FLWPackedPlanes {
vec2 zW; // <nz.w, pz.w>
};
layout(std140, binding = 2) uniform FLWFrustum {
FLWPackedPlanes flw_planes;
};
FLWPackedPlanes flw_planes;

View file

@ -1,5 +1,3 @@
layout(std140, binding = 0) uniform flw_view {
mat4 flw_viewProjection;
vec4 flw_cameraPos;
int flw_constantAmbientLight;
};
mat4 flw_viewProjection;
vec4 flw_cameraPos;
int flw_constantAmbientLight;