mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-27 20:24:39 +01:00
Merge branch '1.18/ubershaders' into 1.18/next
# Conflicts: # src/main/java/com/jozufozu/flywheel/api/material/Material.java # src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java # src/main/java/com/jozufozu/flywheel/backend/instancing/indirect/IndirectEngine.java # src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java # src/main/java/com/jozufozu/flywheel/core/material/SimpleMaterial.java # src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java # src/main/java/com/jozufozu/flywheel/core/structs/transformed/TransformedType.java # src/main/java/com/jozufozu/flywheel/core/uniform/ViewProvider.java # src/main/java/com/jozufozu/flywheel/util/FlwUtil.java
This commit is contained in:
commit
e31575de3b
127 changed files with 2896 additions and 2231 deletions
|
@ -7,14 +7,12 @@ import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.RenderWork;
|
import com.jozufozu.flywheel.backend.RenderWork;
|
||||||
import com.jozufozu.flywheel.backend.ShadersModHandler;
|
import com.jozufozu.flywheel.backend.ShadersModHandler;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
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.batching.DrawBuffer;
|
||||||
import com.jozufozu.flywheel.config.BackendTypeArgument;
|
import com.jozufozu.flywheel.config.BackendTypeArgument;
|
||||||
import com.jozufozu.flywheel.config.FlwCommands;
|
import com.jozufozu.flywheel.config.FlwCommands;
|
||||||
import com.jozufozu.flywheel.config.FlwConfig;
|
import com.jozufozu.flywheel.config.FlwConfig;
|
||||||
import com.jozufozu.flywheel.core.BackendTypes;
|
import com.jozufozu.flywheel.core.BackendTypes;
|
||||||
import com.jozufozu.flywheel.core.Components;
|
import com.jozufozu.flywheel.core.Components;
|
||||||
import com.jozufozu.flywheel.core.DebugRender;
|
|
||||||
import com.jozufozu.flywheel.core.PartialModel;
|
import com.jozufozu.flywheel.core.PartialModel;
|
||||||
import com.jozufozu.flywheel.core.QuadConverter;
|
import com.jozufozu.flywheel.core.QuadConverter;
|
||||||
import com.jozufozu.flywheel.core.StitchedSprite;
|
import com.jozufozu.flywheel.core.StitchedSprite;
|
||||||
|
@ -81,7 +79,6 @@ public class Flywheel {
|
||||||
forgeEventBus.addListener(FlwCommands::registerClientCommands);
|
forgeEventBus.addListener(FlwCommands::registerClientCommands);
|
||||||
|
|
||||||
forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onReloadRenderers);
|
forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onReloadRenderers);
|
||||||
forgeEventBus.addListener(PipelineCompiler::onReloadRenderers);
|
|
||||||
forgeEventBus.addListener(Models::onReloadRenderers);
|
forgeEventBus.addListener(Models::onReloadRenderers);
|
||||||
forgeEventBus.addListener(DrawBuffer::onReloadRenderers);
|
forgeEventBus.addListener(DrawBuffer::onReloadRenderers);
|
||||||
|
|
||||||
|
@ -108,7 +105,6 @@ public class Flywheel {
|
||||||
// forgeEventBus.addListener(ExampleEffect::onReload);
|
// forgeEventBus.addListener(ExampleEffect::onReload);
|
||||||
|
|
||||||
Components.init();
|
Components.init();
|
||||||
DebugRender.init();
|
|
||||||
|
|
||||||
VanillaInstances.init();
|
VanillaInstances.init();
|
||||||
|
|
||||||
|
|
13
src/main/java/com/jozufozu/flywheel/api/context/Context.java
Normal file
13
src/main/java/com/jozufozu/flywheel/api/context/Context.java
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package com.jozufozu.flywheel.api.context;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public interface Context {
|
||||||
|
void onProgramLink(GlProgram program);
|
||||||
|
|
||||||
|
ResourceLocation vertexShader();
|
||||||
|
|
||||||
|
ResourceLocation fragmentShader();
|
||||||
|
}
|
|
@ -1,16 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +1,15 @@
|
||||||
package com.jozufozu.flywheel.api.material;
|
package com.jozufozu.flywheel.api.material;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public interface Material {
|
public interface Material {
|
||||||
FileResolution getVertexShader();
|
ResourceLocation vertexShader();
|
||||||
|
|
||||||
FileResolution getFragmentShader();
|
ResourceLocation fragmentShader();
|
||||||
|
|
||||||
void setup();
|
void setup();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.jozufozu.flywheel.api.pipeline;
|
||||||
|
|
||||||
|
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.ShaderSources;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public interface Pipeline {
|
||||||
|
|
||||||
|
GLSLVersion glslVersion();
|
||||||
|
|
||||||
|
ResourceLocation vertexShader();
|
||||||
|
|
||||||
|
ResourceLocation fragmentShader();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
SourceComponent assemble(InstanceAssemblerContext context);
|
||||||
|
|
||||||
|
record InstanceAssemblerContext(ShaderSources sources, VertexType vertexType, StructType<?> structType) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
||||||
public record PipelineShader(GLSLVersion glslVersion, FileResolution vertex, FileResolution fragment,
|
|
||||||
InstanceAssemblerFactory factory) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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> {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,9 +3,9 @@ package com.jozufozu.flywheel.api.struct;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
||||||
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A StructType contains metadata for a specific instance struct that Flywheel can interface with.
|
* A StructType contains metadata for a specific instance struct that Flywheel can interface with.
|
||||||
|
@ -25,7 +25,7 @@ public interface StructType<S extends InstancedPart> {
|
||||||
|
|
||||||
StructWriter<S> getWriter();
|
StructWriter<S> getWriter();
|
||||||
|
|
||||||
FileResolution getInstanceShader();
|
ResourceLocation instanceShader();
|
||||||
|
|
||||||
VertexTransformer<S> getVertexTransformer();
|
VertexTransformer<S> getVertexTransformer();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.jozufozu.flywheel.api.uniform;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public interface ShaderUniforms {
|
||||||
|
|
||||||
|
Provider activate(long ptr);
|
||||||
|
|
||||||
|
ResourceLocation uniformShader();
|
||||||
|
|
||||||
|
int byteSize();
|
||||||
|
|
||||||
|
interface Provider {
|
||||||
|
/**
|
||||||
|
* Delete this provider.<p>
|
||||||
|
* <p>
|
||||||
|
* Do not free the ptr passed to {@link #activate(long)}.<br>
|
||||||
|
* Clean up other resources, and unsubscribe from events.
|
||||||
|
*/
|
||||||
|
void delete();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Poll the provider for changes.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the provider updated its backing store.
|
||||||
|
*/
|
||||||
|
boolean poll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.uniform;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
|
||||||
|
|
||||||
public abstract class UniformProvider {
|
|
||||||
|
|
||||||
protected long ptr;
|
|
||||||
protected Notifier notifier;
|
|
||||||
|
|
||||||
public abstract int getActualByteSize();
|
|
||||||
|
|
||||||
public void updatePtr(long ptr, Notifier notifier) {
|
|
||||||
this.ptr = ptr;
|
|
||||||
this.notifier = notifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract FileResolution getUniformShader();
|
|
||||||
|
|
||||||
public interface Notifier {
|
|
||||||
void signalChanged();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,8 @@
|
||||||
package com.jozufozu.flywheel.api.vertex;
|
package com.jozufozu.flywheel.api.vertex;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A vertex type containing metadata about a specific vertex layout.
|
* A vertex type containing metadata about a specific vertex layout.
|
||||||
|
@ -13,5 +14,13 @@ public interface VertexType extends VertexListProvider {
|
||||||
*/
|
*/
|
||||||
BufferLayout getLayout();
|
BufferLayout getLayout();
|
||||||
|
|
||||||
FileResolution getLayoutShader();
|
ResourceLocation layoutShader();
|
||||||
|
|
||||||
|
default int getStride() {
|
||||||
|
return getLayout().getStride();
|
||||||
|
}
|
||||||
|
|
||||||
|
default int byteOffset(int vertexIndex) {
|
||||||
|
return getStride() * vertexIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
package com.jozufozu.flywheel.backend;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
@ -60,6 +61,7 @@ public class Backend {
|
||||||
return TYPE != BackendTypes.OFF;
|
return TYPE != BackendTypes.OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract("null -> false")
|
||||||
public static boolean canUseInstancing(@Nullable Level level) {
|
public static boolean canUseInstancing(@Nullable Level level) {
|
||||||
return isOn() && isFlywheelLevel(level);
|
return isOn() && isFlywheelLevel(level);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
package com.jozufozu.flywheel.backend;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||||
|
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
|
||||||
|
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
|
@ -17,4 +20,6 @@ public interface BackendType {
|
||||||
BackendType findFallback();
|
BackendType findFallback();
|
||||||
|
|
||||||
boolean supported();
|
boolean supported();
|
||||||
|
|
||||||
|
@Nullable SimplePipeline pipelineShader();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,9 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
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.InstancedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
|
import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler;
|
||||||
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.core.source.FileResolution;
|
|
||||||
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
|
|
||||||
import com.jozufozu.flywheel.core.source.ShaderSources;
|
import com.jozufozu.flywheel.core.source.ShaderSources;
|
||||||
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
|
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
@ -37,11 +23,12 @@ public class Loader implements ResourceManagerReloadListener {
|
||||||
Loader() {
|
Loader() {
|
||||||
// Can be null when running datagenerators due to the unfortunate time we call this
|
// Can be null when running datagenerators due to the unfortunate time we call this
|
||||||
Minecraft minecraft = Minecraft.getInstance();
|
Minecraft minecraft = Minecraft.getInstance();
|
||||||
if (minecraft != null) {
|
if (minecraft == null) {
|
||||||
ResourceManager manager = minecraft.getResourceManager();
|
return;
|
||||||
if (manager instanceof ReloadableResourceManager) {
|
|
||||||
((ReloadableResourceManager) manager).registerReloadListener(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (minecraft.getResourceManager() instanceof ReloadableResourceManager reloadable) {
|
||||||
|
reloadable.registerReloadListener(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,62 +39,11 @@ public class Loader implements ResourceManagerReloadListener {
|
||||||
var errorReporter = new ErrorReporter();
|
var errorReporter = new ErrorReporter();
|
||||||
ShaderSources sources = new ShaderSources(errorReporter, manager);
|
ShaderSources sources = new ShaderSources(errorReporter, manager);
|
||||||
|
|
||||||
Backend.LOGGER.info("Loaded all shader sources in " + sources.getLoadTime());
|
if (FlwCompiler.INSTANCE != null) {
|
||||||
|
FlwCompiler.INSTANCE.delete();
|
||||||
FileResolution.run(errorReporter, sources);
|
|
||||||
|
|
||||||
if (errorReporter.hasErrored()) {
|
|
||||||
throw errorReporter.dump();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sources.postResolve();
|
FlwCompiler.INSTANCE = new FlwCompiler(sources);
|
||||||
|
|
||||||
Backend.LOGGER.info("Successfully resolved all source files.");
|
|
||||||
|
|
||||||
FileResolution.checkAll(errorReporter);
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientLevel level = Minecraft.getInstance().level;
|
ClientLevel level = Minecraft.getInstance().level;
|
||||||
if (Backend.canUseInstancing(level)) {
|
if (Backend.canUseInstancing(level)) {
|
||||||
|
|
|
@ -3,8 +3,11 @@ package com.jozufozu.flywheel.backend;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||||
import com.jozufozu.flywheel.core.BackendTypes;
|
import com.jozufozu.flywheel.core.BackendTypes;
|
||||||
|
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
|
||||||
|
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
|
@ -17,14 +20,16 @@ public class SimpleBackendType implements BackendType {
|
||||||
private final Supplier<Engine> engineSupplier;
|
private final Supplier<Engine> engineSupplier;
|
||||||
private final Supplier<BackendType> fallback;
|
private final Supplier<BackendType> fallback;
|
||||||
private final BooleanSupplier isSupported;
|
private final BooleanSupplier isSupported;
|
||||||
|
private final SimplePipeline 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 SimplePipeline pipelineShader) {
|
||||||
this.properName = properName;
|
this.properName = properName;
|
||||||
this.shortName = shortName;
|
this.shortName = shortName;
|
||||||
this.engineMessage = engineMessage;
|
this.engineMessage = engineMessage;
|
||||||
this.engineSupplier = engineSupplier;
|
this.engineSupplier = engineSupplier;
|
||||||
this.fallback = fallback;
|
this.fallback = fallback;
|
||||||
this.isSupported = isSupported;
|
this.isSupported = isSupported;
|
||||||
|
this.pipelineShader = pipelineShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
|
@ -66,6 +71,11 @@ public class SimpleBackendType implements BackendType {
|
||||||
return isSupported.getAsBoolean();
|
return isSupported.getAsBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable SimplePipeline pipelineShader() {
|
||||||
|
return pipelineShader;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private String properName;
|
private String properName;
|
||||||
private String shortName;
|
private String shortName;
|
||||||
|
@ -73,6 +83,7 @@ public class SimpleBackendType implements BackendType {
|
||||||
private Supplier<Engine> engineSupplier;
|
private Supplier<Engine> engineSupplier;
|
||||||
private Supplier<BackendType> fallback;
|
private Supplier<BackendType> fallback;
|
||||||
private BooleanSupplier isSupported;
|
private BooleanSupplier isSupported;
|
||||||
|
private SimplePipeline pipelineShader;
|
||||||
|
|
||||||
public Builder properName(String properName) {
|
public Builder properName(String properName) {
|
||||||
this.properName = properName;
|
this.properName = properName;
|
||||||
|
@ -104,8 +115,13 @@ public class SimpleBackendType implements BackendType {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder pipelineShader(SimplePipeline pipelineShader) {
|
||||||
|
this.pipelineShader = pipelineShader;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public BackendType register() {
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,22 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.shader;
|
package com.jozufozu.flywheel.backend.gl.shader;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL20.glDeleteProgram;
|
import static org.lwjgl.opengl.GL20.*;
|
||||||
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;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
import com.jozufozu.flywheel.backend.gl.GlObject;
|
||||||
|
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
|
||||||
import com.mojang.blaze3d.shaders.ProgramManager;
|
import com.mojang.blaze3d.shaders.ProgramManager;
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
public class GlProgram extends GlObject {
|
public class GlProgram extends GlObject {
|
||||||
|
|
||||||
public final ResourceLocation name;
|
public GlProgram(int handle) {
|
||||||
|
|
||||||
public GlProgram(ResourceLocation name, int handle) {
|
|
||||||
this.name = name;
|
|
||||||
setHandle(handle);
|
setHandle(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind() {
|
public void bind() {
|
||||||
|
// TODO: bind textures?
|
||||||
|
UniformBuffer.getInstance()
|
||||||
|
.sync();
|
||||||
ProgramManager.glUseProgram(handle());
|
ProgramManager.glUseProgram(handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +34,7 @@ public class GlProgram extends GlObject {
|
||||||
int index = glGetUniformLocation(this.handle(), uniform);
|
int index = glGetUniformLocation(this.handle(), uniform);
|
||||||
|
|
||||||
if (index < 0) {
|
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;
|
return index;
|
||||||
|
@ -51,17 +45,13 @@ public class GlProgram extends GlObject {
|
||||||
*
|
*
|
||||||
* @param name The name of the sampler uniform.
|
* @param name The name of the sampler uniform.
|
||||||
* @param binding The index of the texture unit.
|
* @param binding The index of the texture unit.
|
||||||
* @return The sampler uniform's index.
|
|
||||||
* @throws NullPointerException If no uniform exists with the given name.
|
|
||||||
*/
|
*/
|
||||||
public int setSamplerBinding(String name, int binding) {
|
public void setSamplerBinding(String name, int binding) {
|
||||||
int samplerUniform = getUniformLocation(name);
|
int samplerUniform = getUniformLocation(name);
|
||||||
|
|
||||||
if (samplerUniform >= 0) {
|
if (samplerUniform >= 0) {
|
||||||
glUniform1i(samplerUniform, binding);
|
glUniform1i(samplerUniform, binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
return samplerUniform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -69,17 +59,4 @@ public class GlProgram extends GlObject {
|
||||||
glDeleteProgram(handle);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,17 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.shader;
|
package com.jozufozu.flywheel.backend.gl.shader;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
import org.lwjgl.opengl.GL20;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
import com.jozufozu.flywheel.backend.gl.GlObject;
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
|
||||||
import com.jozufozu.flywheel.core.compile.ShaderCompilationException;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
public class GlShader extends GlObject {
|
public class GlShader extends GlObject {
|
||||||
|
|
||||||
public final ShaderType type;
|
public final ShaderType type;
|
||||||
private final List<ResourceLocation> parts;
|
private final String name;
|
||||||
|
|
||||||
public GlShader(String source, ShaderType type, List<ResourceLocation> parts) throws ShaderCompilationException {
|
public GlShader(int handle, ShaderType type, String name) {
|
||||||
this.parts = parts;
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
int handle = GL20.glCreateShader(type.glEnum);
|
this.name = name;
|
||||||
|
|
||||||
GlCompat.safeShaderSource(handle, source);
|
|
||||||
GL20.glCompileShader(handle);
|
|
||||||
|
|
||||||
dumpSource(source, type);
|
|
||||||
|
|
||||||
if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) {
|
|
||||||
throw new ShaderCompilationException("Could not compile " + getName(), handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
setHandle(handle);
|
setHandle(handle);
|
||||||
}
|
}
|
||||||
|
@ -42,26 +21,9 @@ public class GlShader extends GlObject {
|
||||||
GL20.glDeleteShader(handle);
|
GL20.glDeleteShader(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
@Override
|
||||||
return parts.stream()
|
public String toString() {
|
||||||
.map(ResourceLocation::toString)
|
return "GlShader{" + type.name + handle() + " " + name + "}";
|
||||||
.map(s -> s.replaceAll("/", "_")
|
|
||||||
.replaceAll(":", "\\$"))
|
|
||||||
.collect(Collectors.joining(";"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dumpSource(String source, ShaderType type) {
|
|
||||||
if (!Backend.DUMP_SHADER_SOURCE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
File dir = new File(Minecraft.getInstance().gameDirectory, "flywheel_sources");
|
|
||||||
dir.mkdirs();
|
|
||||||
File file = new File(dir, type.getFileName(getName()));
|
|
||||||
try (FileWriter writer = new FileWriter(file)) {
|
|
||||||
writer.write(source);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.lwjgl.opengl.GL20;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
||||||
|
import com.jozufozu.flywheel.core.SourceComponent;
|
||||||
|
import com.jozufozu.flywheel.core.source.SourceFile;
|
||||||
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder style class for compiling shaders.
|
||||||
|
* <p>
|
||||||
|
* Keeps track of the source files and components used to compile a shader,
|
||||||
|
* and interprets/pretty prints any errors that occur.
|
||||||
|
*/
|
||||||
|
public class Compilation {
|
||||||
|
private final List<SourceFile> files = new ArrayList<>();
|
||||||
|
private final List<ResourceLocation> componentNames = new ArrayList<>();
|
||||||
|
private final StringBuilder generatedSource;
|
||||||
|
private final StringBuilder fullSource;
|
||||||
|
private final GLSLVersion glslVersion;
|
||||||
|
private final ShaderType shaderType;
|
||||||
|
private int generatedLines = 0;
|
||||||
|
|
||||||
|
public Compilation(GLSLVersion glslVersion, ShaderType shaderType) {
|
||||||
|
this.generatedSource = new StringBuilder();
|
||||||
|
this.fullSource = new StringBuilder(CompileUtil.generateHeader(glslVersion, shaderType));
|
||||||
|
this.glslVersion = glslVersion;
|
||||||
|
this.shaderType = shaderType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public CompilationResult compile() {
|
||||||
|
int handle = GL20.glCreateShader(shaderType.glEnum);
|
||||||
|
var source = fullSource.toString();
|
||||||
|
|
||||||
|
GlCompat.safeShaderSource(handle, source);
|
||||||
|
GL20.glCompileShader(handle);
|
||||||
|
|
||||||
|
var shaderName = buildShaderName();
|
||||||
|
dumpSource(source, shaderType.getFileName(shaderName));
|
||||||
|
|
||||||
|
if (compiledSuccessfully(handle)) {
|
||||||
|
return CompilationResult.success(new GlShader(handle, shaderType, shaderName));
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorLog = GL20.glGetShaderInfoLog(handle);
|
||||||
|
GL20.glDeleteShader(handle);
|
||||||
|
return CompilationResult.failure(new FailedCompilation(shaderName, files, generatedSource.toString(), errorLog));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enableExtension(String ext) {
|
||||||
|
fullSource.append("#extension ")
|
||||||
|
.append(ext)
|
||||||
|
.append(" : enable\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addComponentName(ResourceLocation name) {
|
||||||
|
componentNames.add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendComponent(SourceComponent component) {
|
||||||
|
var source = component.source();
|
||||||
|
|
||||||
|
if (component instanceof SourceFile file) {
|
||||||
|
fullSource.append(sourceHeader(file));
|
||||||
|
} else {
|
||||||
|
fullSource.append(generatedHeader(source, component.name()
|
||||||
|
.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fullSource.append(source)
|
||||||
|
.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sourceHeader(SourceFile sourceFile) {
|
||||||
|
return "#line " + 0 + ' ' + getOrCreateFileID(sourceFile) + " // " + sourceFile.name + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generatedHeader(String generatedCode, String comment) {
|
||||||
|
generatedSource.append(generatedCode);
|
||||||
|
int lines = StringUtil.countLines(generatedCode);
|
||||||
|
|
||||||
|
// all generated code is put in file 0,
|
||||||
|
var out = "#line " + generatedLines + ' ' + 0;
|
||||||
|
|
||||||
|
generatedLines += lines;
|
||||||
|
|
||||||
|
return out + " // (generated) " + comment + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an arbitrary file ID for use this compilation context, or generates one if missing.
|
||||||
|
*
|
||||||
|
* @param sourceFile The file to retrieve the ID for.
|
||||||
|
* @return A file ID unique to the given sourceFile.
|
||||||
|
*/
|
||||||
|
private int getOrCreateFileID(SourceFile sourceFile) {
|
||||||
|
int i = files.indexOf(sourceFile);
|
||||||
|
if (i != -1) {
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
files.add(sourceFile);
|
||||||
|
return files.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String buildShaderName() {
|
||||||
|
var components = componentNames.stream()
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.map(s -> s.replaceAll("/", "_")
|
||||||
|
.replaceAll(":", "\\$"))
|
||||||
|
.collect(Collectors.joining(";"));
|
||||||
|
return shaderType.name + glslVersion + ';' + components;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dumpSource(String source, String fileName) {
|
||||||
|
if (!Backend.DUMP_SHADER_SOURCE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File dir = new File(Minecraft.getInstance().gameDirectory, "flywheel_sources");
|
||||||
|
dir.mkdirs();
|
||||||
|
File file = new File(dir, fileName);
|
||||||
|
try (FileWriter writer = new FileWriter(file)) {
|
||||||
|
writer.write(source);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Backend.LOGGER.error("Could not dump source.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean compiledSuccessfully(int handle) {
|
||||||
|
return GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) == GL20.GL_TRUE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
||||||
|
|
||||||
|
public sealed interface CompilationResult {
|
||||||
|
@Nullable
|
||||||
|
default GlShader unwrap() {
|
||||||
|
if (this instanceof Success s) {
|
||||||
|
return s.shader();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
record Success(GlShader shader) implements CompilationResult {
|
||||||
|
}
|
||||||
|
|
||||||
|
record Failure(FailedCompilation failure) implements CompilationResult {
|
||||||
|
}
|
||||||
|
|
||||||
|
static CompilationResult success(GlShader program) {
|
||||||
|
return new Success(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CompilationResult failure(FailedCompilation failure) {
|
||||||
|
return new Failure(failure);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,10 @@
|
||||||
package com.jozufozu.flywheel.core.compile;
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
|
||||||
|
import static org.lwjgl.opengl.GL20.GL_TRUE;
|
||||||
|
import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
|
||||||
|
import static org.lwjgl.opengl.GL20.glGetProgrami;
|
||||||
|
import static org.lwjgl.opengl.GL20.glLinkProgram;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -7,6 +13,7 @@ import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
|
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||||
import com.jozufozu.flywheel.core.source.SourceFile;
|
import com.jozufozu.flywheel.core.source.SourceFile;
|
||||||
|
@ -17,9 +24,7 @@ public class CompileUtil {
|
||||||
public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$");
|
public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$");
|
||||||
|
|
||||||
public static String generateHeader(GLSLVersion version, ShaderType type) {
|
public static String generateHeader(GLSLVersion version, ShaderType type) {
|
||||||
return version.getVersionLine()
|
return version.getVersionLine() + type.getDefineStatement() + '\n';
|
||||||
+ type.getDefineStatement()
|
|
||||||
+ '\n';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getElementCount(String type) {
|
public static int getElementCount(String type) {
|
||||||
|
@ -59,4 +64,25 @@ public class CompileUtil {
|
||||||
.map(SourceFile::toString)
|
.map(SourceFile::toString)
|
||||||
.collect(Collectors.joining(" -> "));
|
.collect(Collectors.joining(" -> "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the program info log for errors.
|
||||||
|
*
|
||||||
|
* @param handle The handle of the program to check.
|
||||||
|
*/
|
||||||
|
public static void checkLinkLog(int handle) {
|
||||||
|
glLinkProgram(handle);
|
||||||
|
|
||||||
|
String log = glGetProgramInfoLog(handle);
|
||||||
|
|
||||||
|
if (!log.isEmpty()) {
|
||||||
|
Backend.LOGGER.debug("Program link log: " + log);
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = glGetProgrami(handle, GL_LINK_STATUS);
|
||||||
|
|
||||||
|
if (result != GL_TRUE) {
|
||||||
|
throw new RuntimeException("Shader program linking failed, see log for details");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
|
|
||||||
|
public record CullingContext(StructType<?> structType) {
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
|
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||||
|
|
||||||
|
public class CullingContextSet {
|
||||||
|
static CullingContextSet create() {
|
||||||
|
var builder = new CullingContextSet();
|
||||||
|
for (StructType<?> structType : ComponentRegistry.structTypes) {
|
||||||
|
builder.add(structType);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<CullingContext> contexts = new ArrayList<>();
|
||||||
|
private final List<CullingContext> contextView = Collections.unmodifiableList(contexts);
|
||||||
|
|
||||||
|
CullingContextSet() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CullingContext> all() {
|
||||||
|
return contextView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return contexts.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(StructType<?> structType) {
|
||||||
|
var ctx = new CullingContext(structType);
|
||||||
|
|
||||||
|
contexts.add(ctx);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.core.source.SourceFile;
|
||||||
|
import com.jozufozu.flywheel.core.source.SourceLines;
|
||||||
|
import com.jozufozu.flywheel.core.source.error.ErrorBuilder;
|
||||||
|
import com.jozufozu.flywheel.core.source.span.Span;
|
||||||
|
import com.jozufozu.flywheel.util.ConsoleColors;
|
||||||
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
|
||||||
|
public class FailedCompilation {
|
||||||
|
private static final Pattern ERROR_LINE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)");
|
||||||
|
private final List<SourceFile> files;
|
||||||
|
private final SourceLines generatedSource;
|
||||||
|
private final String errorLog;
|
||||||
|
private final String shaderName;
|
||||||
|
|
||||||
|
public FailedCompilation(String shaderName, List<SourceFile> files, String generatedSource, String errorLog) {
|
||||||
|
this.shaderName = shaderName;
|
||||||
|
this.files = files;
|
||||||
|
this.generatedSource = new SourceLines(generatedSource);
|
||||||
|
this.errorLog = errorLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return ConsoleColors.RED_BOLD_BRIGHT + "Failed to compile " + shaderName + ":\n" + errorString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String errorString() {
|
||||||
|
return errorStream().map(ErrorBuilder::build)
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Stream<ErrorBuilder> errorStream() {
|
||||||
|
return errorLog.lines()
|
||||||
|
.map(this::interpretErrorLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ErrorBuilder interpretErrorLine(String s) {
|
||||||
|
Matcher matcher = ERROR_LINE.matcher(s);
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
int fileId = Integer.parseInt(matcher.group(1));
|
||||||
|
int lineNo = Integer.parseInt(matcher.group(2));
|
||||||
|
var msg = StringUtil.trimPrefix(matcher.group(3), "error")
|
||||||
|
.stripLeading();
|
||||||
|
|
||||||
|
if (fileId == 0) {
|
||||||
|
return interpretGeneratedError(lineNo, msg);
|
||||||
|
} else {
|
||||||
|
return interpretSourceError(fileId, lineNo, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ErrorBuilder.create()
|
||||||
|
.error(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ErrorBuilder interpretSourceError(int fileId, int lineNo, String msg) {
|
||||||
|
var sourceFile = files.get(fileId - 1);
|
||||||
|
Span span = sourceFile.getLineSpanNoWhitespace(lineNo);
|
||||||
|
|
||||||
|
return ErrorBuilder.create()
|
||||||
|
.error(msg)
|
||||||
|
.pointAtFile(sourceFile)
|
||||||
|
.pointAt(span, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ErrorBuilder interpretGeneratedError(int lineNo, String msg) {
|
||||||
|
return ErrorBuilder.create()
|
||||||
|
.error(msg)
|
||||||
|
.pointAtFile("[in generated source]")
|
||||||
|
.pointAtLine(generatedSource, lineNo, 1)
|
||||||
|
.note("This generally indicates a bug in Flywheel, not your shader code.");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,207 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL20.glAttachShader;
|
||||||
|
import static org.lwjgl.opengl.GL20.glCreateProgram;
|
||||||
|
import static org.lwjgl.opengl.GL20.glLinkProgram;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
|
import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
||||||
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
|
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
|
||||||
|
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.ShaderType;
|
||||||
|
import com.jozufozu.flywheel.backend.instancing.indirect.IndirectComponent;
|
||||||
|
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||||
|
import com.jozufozu.flywheel.core.Pipelines;
|
||||||
|
import com.jozufozu.flywheel.core.SourceComponent;
|
||||||
|
import com.jozufozu.flywheel.core.context.SimpleContext;
|
||||||
|
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
|
||||||
|
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
|
||||||
|
import com.jozufozu.flywheel.core.source.ShaderSources;
|
||||||
|
import com.jozufozu.flywheel.core.source.generate.FnSignature;
|
||||||
|
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
|
||||||
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
|
||||||
|
public class FlwCompiler {
|
||||||
|
|
||||||
|
public static FlwCompiler INSTANCE;
|
||||||
|
|
||||||
|
final long compileStart = System.nanoTime();
|
||||||
|
private final ShaderSources sources;
|
||||||
|
private final UniformComponent uniformComponent;
|
||||||
|
private final MaterialAdapterComponent vertexMaterialComponent;
|
||||||
|
private final MaterialAdapterComponent fragmentMaterialComponent;
|
||||||
|
|
||||||
|
private final PipelineContextSet pipelineContexts;
|
||||||
|
private final CullingContextSet cullingContexts;
|
||||||
|
|
||||||
|
final ShaderCompiler shaderCompiler;
|
||||||
|
final Map<PipelineContext, GlProgram> pipelinePrograms = new HashMap<>();
|
||||||
|
final Map<StructType<?>, GlProgram> cullingPrograms = new HashMap<>();
|
||||||
|
final List<FailedCompilation> errors = new ArrayList<>();
|
||||||
|
|
||||||
|
public FlwCompiler(ShaderSources sources) {
|
||||||
|
this.shaderCompiler = ShaderCompiler.builder()
|
||||||
|
.errorConsumer(errors::add)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.sources = sources;
|
||||||
|
this.vertexMaterialComponent = MaterialAdapterComponent.builder(Flywheel.rl("vertex_material_adapter"))
|
||||||
|
.materialSources(ComponentRegistry.materials.vertexSources())
|
||||||
|
.adapt(FnSignature.ofVoid("flw_materialVertex"))
|
||||||
|
.switchOn(GlslExpr.variable("flw_materialVertexID"))
|
||||||
|
.build(sources);
|
||||||
|
this.fragmentMaterialComponent = MaterialAdapterComponent.builder(Flywheel.rl("fragment_material_adapter"))
|
||||||
|
.materialSources(ComponentRegistry.materials.fragmentSources())
|
||||||
|
.adapt(FnSignature.ofVoid("flw_materialFragment"))
|
||||||
|
.adapt(FnSignature.create()
|
||||||
|
.returnType("bool")
|
||||||
|
.name("flw_discardPredicate")
|
||||||
|
.arg("vec4", "color")
|
||||||
|
.build(), GlslExpr.literal(false))
|
||||||
|
.adapt(FnSignature.create()
|
||||||
|
.returnType("vec4")
|
||||||
|
.name("flw_fogFilter")
|
||||||
|
.arg("vec4", "color")
|
||||||
|
.build(), GlslExpr.variable("color"))
|
||||||
|
.switchOn(GlslExpr.variable("flw_materialFragmentID"))
|
||||||
|
.build(sources);
|
||||||
|
this.uniformComponent = UniformComponent.builder(Flywheel.rl("uniforms"))
|
||||||
|
.sources(ComponentRegistry.getAllUniformProviders()
|
||||||
|
.stream()
|
||||||
|
.map(ShaderUniforms::uniformShader)
|
||||||
|
.toList())
|
||||||
|
.build(sources);
|
||||||
|
|
||||||
|
this.pipelineContexts = PipelineContextSet.create();
|
||||||
|
this.cullingContexts = CullingContextSet.create();
|
||||||
|
|
||||||
|
doCompilation();
|
||||||
|
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doCompilation() {
|
||||||
|
for (var ctx : pipelineContexts.all()) {
|
||||||
|
compilePipelineContext(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var ctx : cullingContexts.all()) {
|
||||||
|
compileComputeCuller(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finish() {
|
||||||
|
long compileEnd = System.nanoTime();
|
||||||
|
int programCount = pipelineContexts.size() + ComponentRegistry.structTypes.size();
|
||||||
|
int shaderCount = shaderCompiler.shaderCount();
|
||||||
|
int errorCount = errors.size();
|
||||||
|
var elapsed = StringUtil.formatTime(compileEnd - compileStart);
|
||||||
|
|
||||||
|
Backend.LOGGER.info("Compiled " + programCount + " programs and " + shaderCount + " shaders in " + elapsed + " with " + errorCount + " errors.");
|
||||||
|
|
||||||
|
if (errorCount > 0) {
|
||||||
|
var details = errors.stream()
|
||||||
|
.map(FailedCompilation::getMessage)
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
throw new ShaderLoadingException("Compilation failed.\n" + details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
pipelinePrograms.values()
|
||||||
|
.forEach(GlProgram::delete);
|
||||||
|
cullingPrograms.values()
|
||||||
|
.forEach(GlProgram::delete);
|
||||||
|
shaderCompiler.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlProgram getPipelineProgram(VertexType vertexType, StructType<?> structType, SimpleContext contextShader, SimplePipeline pipelineShader) {
|
||||||
|
return pipelinePrograms.get(new PipelineContext(vertexType, structType, contextShader, pipelineShader));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlProgram getCullingProgram(StructType<?> structType) {
|
||||||
|
return cullingPrograms.get(structType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compilePipelineContext(PipelineContext ctx) {
|
||||||
|
var glslVersion = ctx.pipelineShader()
|
||||||
|
.glslVersion();
|
||||||
|
|
||||||
|
var vertex = shaderCompiler.compile(glslVersion, ShaderType.VERTEX, getVertexComponents(ctx));
|
||||||
|
var fragment = shaderCompiler.compile(glslVersion, ShaderType.FRAGMENT, getFragmentComponents(ctx));
|
||||||
|
|
||||||
|
if (vertex == null || fragment == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var glProgram = link(vertex.handle(), fragment.handle());
|
||||||
|
ctx.contextShader()
|
||||||
|
.onProgramLink(glProgram);
|
||||||
|
pipelinePrograms.put(ctx, glProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compileComputeCuller(CullingContext ctx) {
|
||||||
|
var computeComponents = getComputeComponents(ctx.structType());
|
||||||
|
var result = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, computeComponents);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cullingPrograms.put(ctx.structType(), link(result.handle()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private GlProgram link(int... shaders) {
|
||||||
|
var handle = glCreateProgram();
|
||||||
|
for (var shader : shaders) {
|
||||||
|
glAttachShader(handle, shader);
|
||||||
|
}
|
||||||
|
glLinkProgram(handle);
|
||||||
|
CompileUtil.checkLinkLog(handle);
|
||||||
|
return new GlProgram(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableList<SourceComponent> getVertexComponents(PipelineContext ctx) {
|
||||||
|
var instanceAssembly = ctx.pipelineShader()
|
||||||
|
.assemble(new Pipeline.InstanceAssemblerContext(sources, ctx.vertexType(), ctx.structType()));
|
||||||
|
|
||||||
|
var layout = sources.find(ctx.vertexType()
|
||||||
|
.layoutShader());
|
||||||
|
var instance = sources.find(ctx.structType()
|
||||||
|
.instanceShader());
|
||||||
|
var context = sources.find(ctx.contextShader()
|
||||||
|
.vertexShader());
|
||||||
|
var pipeline = sources.find(ctx.pipelineShader()
|
||||||
|
.vertexShader());
|
||||||
|
|
||||||
|
return ImmutableList.of(uniformComponent, vertexMaterialComponent, instanceAssembly, layout, instance, context, pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableList<SourceComponent> getFragmentComponents(PipelineContext ctx) {
|
||||||
|
var context = sources.find(ctx.contextShader()
|
||||||
|
.fragmentShader());
|
||||||
|
var pipeline = sources.find(ctx.pipelineShader()
|
||||||
|
.fragmentShader());
|
||||||
|
return ImmutableList.of(uniformComponent, fragmentMaterialComponent, context, pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableList<SourceComponent> getComputeComponents(StructType<?> structType) {
|
||||||
|
var instanceAssembly = new IndirectComponent(sources, structType);
|
||||||
|
var instance = sources.find(structType.instanceShader());
|
||||||
|
var pipeline = sources.find(Pipelines.Files.INDIRECT_CULL);
|
||||||
|
|
||||||
|
return ImmutableList.of(uniformComponent, instanceAssembly, instance, pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.jozufozu.flywheel.core.SourceComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A component of a ShaderCompiler, responsible for expanding root sources into the complete set of included sources.
|
||||||
|
*/
|
||||||
|
public interface Includer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand the given root sources into the complete set of included sources.
|
||||||
|
* <p> Each unique source will be seen exactly once.
|
||||||
|
*
|
||||||
|
* @param rootSources The root sources to expand.
|
||||||
|
* @param out A consumer to which all sources should be passed in the order they should be included.
|
||||||
|
*/
|
||||||
|
void expand(ImmutableList<SourceComponent> rootSources, Consumer<SourceComponent> out);
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.jozufozu.flywheel.core.SourceComponent;
|
||||||
|
import com.jozufozu.flywheel.core.source.ShaderSources;
|
||||||
|
import com.jozufozu.flywheel.core.source.generate.FnSignature;
|
||||||
|
import com.jozufozu.flywheel.core.source.generate.GlslBlock;
|
||||||
|
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
|
||||||
|
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
|
||||||
|
import com.jozufozu.flywheel.core.source.generate.GlslSwitch;
|
||||||
|
import com.jozufozu.flywheel.util.ResourceUtil;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public class MaterialAdapterComponent implements SourceComponent {
|
||||||
|
|
||||||
|
// TODO: material id handling in pipeline shader
|
||||||
|
private final ResourceLocation name;
|
||||||
|
private final GlslExpr switchArg;
|
||||||
|
private final List<AdaptedFn> functionsToAdapt;
|
||||||
|
private final List<StringSubstitutionSourceComponent> adaptedComponents;
|
||||||
|
|
||||||
|
public MaterialAdapterComponent(ResourceLocation name, GlslExpr switchArg, List<AdaptedFn> functionsToAdapt, List<StringSubstitutionSourceComponent> adaptedComponents) {
|
||||||
|
this.name = name;
|
||||||
|
this.switchArg = switchArg;
|
||||||
|
this.functionsToAdapt = functionsToAdapt;
|
||||||
|
this.adaptedComponents = adaptedComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder(ResourceLocation name) {
|
||||||
|
return new Builder(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends SourceComponent> included() {
|
||||||
|
return adaptedComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String source() {
|
||||||
|
var builder = new GlslBuilder();
|
||||||
|
|
||||||
|
for (var adaptedFunction : functionsToAdapt) {
|
||||||
|
builder.function()
|
||||||
|
.signature(adaptedFunction.signature())
|
||||||
|
.body(body -> generateAdapter(body, adaptedFunction));
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateAdapter(GlslBlock body, AdaptedFn adaptedFunction) {
|
||||||
|
var sw = GlslSwitch.on(switchArg);
|
||||||
|
var fnSignature = adaptedFunction.signature();
|
||||||
|
var fnName = fnSignature.name();
|
||||||
|
var isVoid = fnSignature.isVoid();
|
||||||
|
var fnArgs = fnSignature.createArgExpressions();
|
||||||
|
|
||||||
|
for (int i = 0; i < adaptedComponents.size(); i++) {
|
||||||
|
var component = adaptedComponents.get(i);
|
||||||
|
|
||||||
|
if (!component.replaces(fnName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var adaptedCall = GlslExpr.call(component.remapFnName(fnName), fnArgs);
|
||||||
|
|
||||||
|
var block = GlslBlock.create();
|
||||||
|
if (isVoid) {
|
||||||
|
block.eval(adaptedCall)
|
||||||
|
.breakStmt();
|
||||||
|
} else {
|
||||||
|
block.ret(adaptedCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.intCase(i, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isVoid) {
|
||||||
|
var defaultReturn = adaptedFunction.defaultReturn;
|
||||||
|
if (defaultReturn == null) {
|
||||||
|
throw new IllegalStateException("Function " + fnName + " is not void, but no default return value was provided");
|
||||||
|
}
|
||||||
|
sw.defaultCase(GlslBlock.create()
|
||||||
|
.ret(defaultReturn));
|
||||||
|
}
|
||||||
|
|
||||||
|
body.add(sw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record AdaptedFn(FnSignature signature, @Nullable GlslExpr defaultReturn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private final ResourceLocation name;
|
||||||
|
private final List<ResourceLocation> materialSources = new ArrayList<>();
|
||||||
|
private final List<AdaptedFn> adaptedFunctions = new ArrayList<>();
|
||||||
|
private GlslExpr switchArg;
|
||||||
|
|
||||||
|
public Builder(ResourceLocation name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder materialSources(List<ResourceLocation> sources) {
|
||||||
|
this.materialSources.addAll(sources);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder adapt(FnSignature function) {
|
||||||
|
adaptedFunctions.add(new AdaptedFn(function, null));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder adapt(FnSignature function, @Nonnull GlslExpr defaultReturn) {
|
||||||
|
adaptedFunctions.add(new AdaptedFn(function, defaultReturn));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder switchOn(GlslExpr expr) {
|
||||||
|
this.switchArg = expr;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MaterialAdapterComponent build(ShaderSources sources) {
|
||||||
|
if (switchArg == null) {
|
||||||
|
throw new NullPointerException("Switch argument must be set");
|
||||||
|
}
|
||||||
|
|
||||||
|
var transformed = ImmutableList.<StringSubstitutionSourceComponent>builder();
|
||||||
|
|
||||||
|
for (var rl : materialSources) {
|
||||||
|
var sourceFile = sources.find(rl);
|
||||||
|
var adapterMap = createAdapterMap(adaptedFunctions, getSuffix(rl));
|
||||||
|
transformed.add(new StringSubstitutionSourceComponent(sourceFile, adapterMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MaterialAdapterComponent(name, switchArg, adaptedFunctions, transformed.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static HashMap<String, String> createAdapterMap(List<AdaptedFn> adaptedFunctions, String suffix) {
|
||||||
|
HashMap<String, String> out = new HashMap<>();
|
||||||
|
|
||||||
|
for (var adapted : adaptedFunctions) {
|
||||||
|
var fnName = adapted.signature()
|
||||||
|
.name();
|
||||||
|
out.put(fnName, fnName + suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static String getSuffix(ResourceLocation rl) {
|
||||||
|
return '_' + ResourceUtil.toSafeString(rl);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.context.Context;
|
||||||
|
import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
||||||
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, Context contextShader,
|
||||||
|
Pipeline pipelineShader) {
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
import com.jozufozu.flywheel.core.BackendTypes;
|
||||||
|
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||||
|
import com.jozufozu.flywheel.core.Components;
|
||||||
|
import com.jozufozu.flywheel.core.context.SimpleContext;
|
||||||
|
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
|
||||||
|
|
||||||
|
public class PipelineContextSet {
|
||||||
|
static PipelineContextSet create() {
|
||||||
|
var builder = new PipelineContextSet();
|
||||||
|
for (SimplePipeline pipelineShader : BackendTypes.availablePipelineShaders()) {
|
||||||
|
for (StructType<?> structType : ComponentRegistry.structTypes) {
|
||||||
|
for (VertexType vertexType : ComponentRegistry.vertexTypes) {
|
||||||
|
builder.add(vertexType, structType, Components.WORLD, pipelineShader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<PipelineContext> contexts = new ArrayList<>();
|
||||||
|
private final List<PipelineContext> contextView = Collections.unmodifiableList(contexts);
|
||||||
|
|
||||||
|
PipelineContextSet() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PipelineContext> all() {
|
||||||
|
return contextView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return contexts.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(VertexType vertexType, StructType<?> structType, SimpleContext world, SimplePipeline pipelineShader) {
|
||||||
|
var ctx = new PipelineContext(vertexType, structType, world, pipelineShader);
|
||||||
|
|
||||||
|
|
||||||
|
contexts.add(ctx);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.jozufozu.flywheel.core.SourceComponent;
|
||||||
|
|
||||||
|
public class RecursiveIncluder implements Includer {
|
||||||
|
|
||||||
|
public static final RecursiveIncluder INSTANCE = new RecursiveIncluder();
|
||||||
|
|
||||||
|
private RecursiveIncluder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void expand(ImmutableList<SourceComponent> rootSources, Consumer<SourceComponent> out) {
|
||||||
|
var included = new LinkedHashSet<SourceComponent>(); // use hash set to deduplicate. linked to preserve order
|
||||||
|
for (var component : rootSources) {
|
||||||
|
recursiveDepthFirstInclude(included, component);
|
||||||
|
included.add(component);
|
||||||
|
}
|
||||||
|
included.forEach(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void recursiveDepthFirstInclude(Set<SourceComponent> included, SourceComponent component) {
|
||||||
|
for (var include : component.included()) {
|
||||||
|
recursiveDepthFirstInclude(included, include);
|
||||||
|
}
|
||||||
|
included.addAll(component.included());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
|
||||||
|
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.util.FlwUtil;
|
||||||
|
|
||||||
|
public class ShaderCompiler {
|
||||||
|
private final Map<ShaderKey, CompilationResult> shaderCache = new HashMap<>();
|
||||||
|
private final Consumer<FailedCompilation> errorConsumer;
|
||||||
|
private final CompilationFactory factory;
|
||||||
|
private final Includer includer;
|
||||||
|
|
||||||
|
public ShaderCompiler(Consumer<FailedCompilation> errorConsumer, CompilationFactory factory, Includer includer) {
|
||||||
|
this.errorConsumer = errorConsumer;
|
||||||
|
this.factory = factory;
|
||||||
|
this.includer = includer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int shaderCount() {
|
||||||
|
return shaderCache.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public GlShader compile(GLSLVersion glslVersion, ShaderType shaderType, ImmutableList<SourceComponent> sourceComponents) {
|
||||||
|
var key = new ShaderKey(glslVersion, shaderType, sourceComponents);
|
||||||
|
var cached = shaderCache.get(key);
|
||||||
|
if (cached != null) {
|
||||||
|
return cached.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationResult out = compileUncached(factory.create(glslVersion, shaderType), sourceComponents);
|
||||||
|
shaderCache.put(key, out);
|
||||||
|
return unwrapAndReportError(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
shaderCache.values()
|
||||||
|
.stream()
|
||||||
|
.map(CompilationResult::unwrap)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(GlShader::delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private GlShader unwrapAndReportError(CompilationResult result) {
|
||||||
|
if (result instanceof CompilationResult.Success s) {
|
||||||
|
return s.shader();
|
||||||
|
} else if (result instanceof CompilationResult.Failure f) {
|
||||||
|
errorConsumer.accept(f.failure());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private CompilationResult compileUncached(Compilation ctx, ImmutableList<SourceComponent> sourceComponents) {
|
||||||
|
ctx.enableExtension("GL_ARB_explicit_attrib_location");
|
||||||
|
ctx.enableExtension("GL_ARB_conservative_depth");
|
||||||
|
|
||||||
|
includer.expand(sourceComponents, ctx::appendComponent);
|
||||||
|
|
||||||
|
return ctx.compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private record ShaderKey(GLSLVersion glslVersion, ShaderType shaderType,
|
||||||
|
ImmutableList<SourceComponent> sourceComponents) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface CompilationFactory {
|
||||||
|
Compilation create(GLSLVersion version, ShaderType shaderType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private Consumer<FailedCompilation> errorConsumer = FlwUtil::noop;
|
||||||
|
private CompilationFactory factory = Compilation::new;
|
||||||
|
private Includer includer = RecursiveIncluder.INSTANCE;
|
||||||
|
|
||||||
|
public Builder errorConsumer(Consumer<FailedCompilation> errorConsumer) {
|
||||||
|
this.errorConsumer = errorConsumer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder compilationFactory(CompilationFactory factory) {
|
||||||
|
this.factory = factory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder includer(Includer includer) {
|
||||||
|
this.includer = includer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderCompiler build() {
|
||||||
|
return new ShaderCompiler(errorConsumer, factory, includer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
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 StringSubstitutionSourceComponent implements SourceComponent {
|
||||||
|
private final SourceComponent source;
|
||||||
|
private final Map<String, String> replacements;
|
||||||
|
private final String sourceString;
|
||||||
|
|
||||||
|
public StringSubstitutionSourceComponent(SourceComponent source, String find, String replace) {
|
||||||
|
this(source, Map.of(find, replace));
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringSubstitutionSourceComponent(SourceComponent source, Map<String, String> replacements) {
|
||||||
|
this.source = source;
|
||||||
|
this.replacements = replacements;
|
||||||
|
this.sourceString = source.source();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String remapFnName(String name) {
|
||||||
|
return replacements.getOrDefault(name, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean replaces(String name) {
|
||||||
|
return replacements.containsKey(name) && sourceString.contains(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String source() {
|
||||||
|
var source = sourceString;
|
||||||
|
|
||||||
|
for (var entry : replacements.entrySet()) {
|
||||||
|
source = source.replace(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation name() {
|
||||||
|
return ResourceUtil.subPath(source.name(), "_string_substitution");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends SourceComponent> included() {
|
||||||
|
return source.included();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.jozufozu.flywheel.core.SourceComponent;
|
||||||
|
import com.jozufozu.flywheel.core.source.ShaderSources;
|
||||||
|
import com.jozufozu.flywheel.core.source.SourceFile;
|
||||||
|
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public class UniformComponent implements SourceComponent {
|
||||||
|
|
||||||
|
private final ResourceLocation name;
|
||||||
|
private final ImmutableList<SourceFile> uniformShaders;
|
||||||
|
|
||||||
|
public static Builder builder(ResourceLocation uniforms) {
|
||||||
|
return new Builder(uniforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UniformComponent(ResourceLocation name, ImmutableList<SourceFile> uniformShaders) {
|
||||||
|
this.name = name;
|
||||||
|
this.uniformShaders = uniformShaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends SourceComponent> included() {
|
||||||
|
return uniformShaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String source() {
|
||||||
|
var builder = new GlslBuilder();
|
||||||
|
|
||||||
|
builder.uniformBlock()
|
||||||
|
.layout("std140")
|
||||||
|
.binding(0)
|
||||||
|
.name("FLWUniforms")
|
||||||
|
.member("flywheel_uniforms", "flywheel");
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private final ResourceLocation name;
|
||||||
|
private final List<ResourceLocation> uniformShaders = new ArrayList<>();
|
||||||
|
|
||||||
|
public Builder(ResourceLocation name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder sources(List<ResourceLocation> sources) {
|
||||||
|
this.uniformShaders.addAll(sources);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniformComponent build(ShaderSources sources) {
|
||||||
|
var out = ImmutableList.<SourceFile>builder();
|
||||||
|
|
||||||
|
for (var fileResolution : uniformShaders) {
|
||||||
|
out.add(sources.find(fileResolution));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UniformComponent(name, out.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.compile;
|
||||||
|
|
||||||
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
|
|
||||||
|
import net.minecraft.MethodsReturnNonnullByDefault;
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +1,7 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.indirect;
|
package com.jozufozu.flywheel.backend.instancing.indirect;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL45.glCreateBuffers;
|
import static org.lwjgl.opengl.GL45.glCreateBuffers;
|
||||||
import static org.lwjgl.opengl.GL46.GL_DRAW_INDIRECT_BUFFER;
|
import static org.lwjgl.opengl.GL46.*;
|
||||||
import static org.lwjgl.opengl.GL46.GL_DYNAMIC_STORAGE_BIT;
|
|
||||||
import static org.lwjgl.opengl.GL46.GL_MAP_FLUSH_EXPLICIT_BIT;
|
|
||||||
import static org.lwjgl.opengl.GL46.GL_MAP_PERSISTENT_BIT;
|
|
||||||
import static org.lwjgl.opengl.GL46.GL_MAP_WRITE_BIT;
|
|
||||||
import static org.lwjgl.opengl.GL46.GL_SHADER_STORAGE_BUFFER;
|
|
||||||
import static org.lwjgl.opengl.GL46.glBindBuffer;
|
|
||||||
import static org.lwjgl.opengl.GL46.glCopyNamedBufferSubData;
|
|
||||||
import static org.lwjgl.opengl.GL46.glDeleteBuffers;
|
|
||||||
import static org.lwjgl.opengl.GL46.glFlushMappedNamedBufferRange;
|
|
||||||
import static org.lwjgl.opengl.GL46.glNamedBufferStorage;
|
|
||||||
import static org.lwjgl.opengl.GL46.nglBindBuffersRange;
|
|
||||||
import static org.lwjgl.opengl.GL46.nglCreateBuffers;
|
|
||||||
import static org.lwjgl.opengl.GL46.nglDeleteBuffers;
|
|
||||||
import static org.lwjgl.opengl.GL46.nglMapNamedBufferRange;
|
|
||||||
import static org.lwjgl.opengl.GL46.nglNamedBufferSubData;
|
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
import org.lwjgl.system.Pointer;
|
import org.lwjgl.system.Pointer;
|
||||||
|
@ -30,7 +15,7 @@ public class IndirectBuffers {
|
||||||
public static final long PTR_SIZE = Pointer.POINTER_SIZE;
|
public static final long PTR_SIZE = Pointer.POINTER_SIZE;
|
||||||
|
|
||||||
// DRAW COMMAND
|
// DRAW COMMAND
|
||||||
public static final long DRAW_COMMAND_STRIDE = 36;
|
public static final long DRAW_COMMAND_STRIDE = 44;
|
||||||
public static final long DRAW_COMMAND_OFFSET = 0;
|
public static final long DRAW_COMMAND_OFFSET = 0;
|
||||||
|
|
||||||
// BITS
|
// BITS
|
||||||
|
@ -180,15 +165,16 @@ public class IndirectBuffers {
|
||||||
FlwMemoryTracker._freeGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE);
|
FlwMemoryTracker._freeGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindAll() {
|
public void bindForCompute() {
|
||||||
bindN(BUFFER_COUNT);
|
multiBind(BUFFER_COUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindObjectAndTarget() {
|
public void bindForDraw() {
|
||||||
bindN(2);
|
multiBind(BUFFER_COUNT);
|
||||||
|
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, draw);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindN(int bufferCount) {
|
private void multiBind(int bufferCount) {
|
||||||
if (bufferCount > BUFFER_COUNT) {
|
if (bufferCount > BUFFER_COUNT) {
|
||||||
throw new IllegalArgumentException("Can't bind more than " + BUFFER_COUNT + " buffers");
|
throw new IllegalArgumentException("Can't bind more than " + BUFFER_COUNT + " buffers");
|
||||||
}
|
}
|
||||||
|
@ -197,10 +183,6 @@ public class IndirectBuffers {
|
||||||
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, bufferCount, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
|
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, bufferCount, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bindIndirectBuffer() {
|
|
||||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, draw);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flushBatchIDs(long length) {
|
void flushBatchIDs(long length) {
|
||||||
glFlushMappedNamedBufferRange(batch, 0, length);
|
glFlushMappedNamedBufferRange(batch, 0, length);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,18 @@ package com.jozufozu.flywheel.backend.instancing.indirect;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
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.SourceComponent;
|
||||||
import com.jozufozu.flywheel.core.layout.LayoutItem;
|
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.FnSignature;
|
||||||
|
import com.jozufozu.flywheel.core.source.generate.GlslBlock;
|
||||||
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
|
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
|
||||||
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
|
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
|
||||||
|
|
||||||
|
@ -18,16 +23,24 @@ public class IndirectComponent implements SourceComponent {
|
||||||
|
|
||||||
private static final String UNPACK_ARG = "p";
|
private static final String UNPACK_ARG = "p";
|
||||||
private static final GlslExpr.Variable UNPACKING_VARIABLE = GlslExpr.variable(UNPACK_ARG);
|
private static final GlslExpr.Variable UNPACKING_VARIABLE = GlslExpr.variable(UNPACK_ARG);
|
||||||
|
private static final String STRUCT_NAME = "IndirectStruct";
|
||||||
|
private static final String PACKED_STRUCT_NAME = STRUCT_NAME + "_packed";
|
||||||
|
|
||||||
private final List<LayoutItem> layoutItems;
|
private final List<LayoutItem> layoutItems;
|
||||||
|
private final ImmutableList<SourceFile> included;
|
||||||
|
|
||||||
public IndirectComponent(List<LayoutItem> layoutItems) {
|
public IndirectComponent(Pipeline.InstanceAssemblerContext ctx) {
|
||||||
this.layoutItems = layoutItems;
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<? extends SourceComponent> included() {
|
public Collection<? extends SourceComponent> included() {
|
||||||
return List.of(Components.UTIL_TYPES.getFile());
|
return included;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -36,22 +49,20 @@ public class IndirectComponent implements SourceComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String source(CompilationContext ctx) {
|
public String source() {
|
||||||
var generated = generateIndirect("IndirectStruct");
|
return generateIndirect();
|
||||||
return ctx.generatedHeader(generated, name().toString()) + generated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generateIndirect(String structName) {
|
public String generateIndirect() {
|
||||||
var builder = new GlslBuilder();
|
var builder = new GlslBuilder();
|
||||||
final var packedStructName = structName + "_packed";
|
builder.define("FlwInstance", STRUCT_NAME);
|
||||||
builder.define("FlwInstance", structName);
|
builder.define("FlwPackedInstance", PACKED_STRUCT_NAME);
|
||||||
builder.define("FlwPackedInstance", packedStructName);
|
|
||||||
|
|
||||||
var packed = builder.struct();
|
var packed = builder.struct();
|
||||||
builder.blankLine();
|
builder.blankLine();
|
||||||
var instance = builder.struct();
|
var instance = builder.struct();
|
||||||
packed.setName(packedStructName);
|
packed.setName(PACKED_STRUCT_NAME);
|
||||||
instance.setName(structName);
|
instance.setName(STRUCT_NAME);
|
||||||
|
|
||||||
for (var field : layoutItems) {
|
for (var field : layoutItems) {
|
||||||
field.addPackedToStruct(packed);
|
field.addPackedToStruct(packed);
|
||||||
|
@ -60,18 +71,21 @@ public class IndirectComponent implements SourceComponent {
|
||||||
|
|
||||||
builder.blankLine();
|
builder.blankLine();
|
||||||
|
|
||||||
var func = builder.function()
|
builder.function()
|
||||||
.returnType(structName)
|
.signature(FnSignature.create()
|
||||||
|
.returnType(STRUCT_NAME)
|
||||||
.name("flw_unpackInstance")
|
.name("flw_unpackInstance")
|
||||||
.argumentIn(packedStructName, UNPACK_ARG);
|
.arg(PACKED_STRUCT_NAME, UNPACK_ARG)
|
||||||
|
.build())
|
||||||
var args = layoutItems.stream()
|
.body(this::generateUnpackingBody);
|
||||||
.map(layoutItem -> layoutItem.unpackField(UNPACKING_VARIABLE))
|
|
||||||
.map(GlslExpr::minPrint)
|
|
||||||
.collect(Collectors.joining(", "));
|
|
||||||
|
|
||||||
func.statement("return " + structName + "(" + args + ");");
|
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateUnpackingBody(GlslBlock b) {
|
||||||
|
var unpackedFields = layoutItems.stream()
|
||||||
|
.map(layoutItem -> layoutItem.unpackField(UNPACKING_VARIABLE))
|
||||||
|
.toList();
|
||||||
|
b.ret(GlslExpr.call(STRUCT_NAME, unpackedFields));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,31 +3,22 @@ package com.jozufozu.flywheel.backend.instancing.indirect;
|
||||||
import static org.lwjgl.opengl.GL42.GL_COMMAND_BARRIER_BIT;
|
import static org.lwjgl.opengl.GL42.GL_COMMAND_BARRIER_BIT;
|
||||||
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
|
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
|
||||||
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT;
|
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT;
|
||||||
import static org.lwjgl.opengl.GL46.glBindVertexArray;
|
import static org.lwjgl.opengl.GL46.*;
|
||||||
import static org.lwjgl.opengl.GL46.glCreateVertexArrays;
|
|
||||||
import static org.lwjgl.opengl.GL46.glDeleteVertexArrays;
|
|
||||||
import static org.lwjgl.opengl.GL46.glDispatchCompute;
|
|
||||||
import static org.lwjgl.opengl.GL46.glEnableVertexArrayAttrib;
|
|
||||||
import static org.lwjgl.opengl.GL46.glVertexArrayElementBuffer;
|
|
||||||
import static org.lwjgl.opengl.GL46.glVertexArrayVertexBuffer;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.RenderStage;
|
import com.jozufozu.flywheel.api.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
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.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
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.Components;
|
||||||
import com.jozufozu.flywheel.core.Materials;
|
import com.jozufozu.flywheel.core.Pipelines;
|
||||||
import com.jozufozu.flywheel.core.QuadConverter;
|
import com.jozufozu.flywheel.core.QuadConverter;
|
||||||
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
|
|
||||||
|
|
||||||
public class IndirectCullingGroup<T extends InstancedPart> {
|
public class IndirectCullingGroup<T extends InstancedPart> {
|
||||||
|
|
||||||
private static final int BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT;
|
private static final int BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT;
|
||||||
|
|
||||||
final StructWriter<T> storageBufferWriter;
|
|
||||||
final GlProgram compute;
|
final GlProgram compute;
|
||||||
final GlProgram draw;
|
final GlProgram draw;
|
||||||
private final VertexType vertexType;
|
private final VertexType vertexType;
|
||||||
|
@ -48,7 +39,6 @@ public class IndirectCullingGroup<T extends InstancedPart> {
|
||||||
|
|
||||||
IndirectCullingGroup(StructType<T> structType, VertexType vertexType) {
|
IndirectCullingGroup(StructType<T> structType, VertexType vertexType) {
|
||||||
this.vertexType = vertexType;
|
this.vertexType = vertexType;
|
||||||
storageBufferWriter = structType.getWriter();
|
|
||||||
|
|
||||||
objectStride = structType.getLayout()
|
objectStride = structType.getLayout()
|
||||||
.getStride();
|
.getStride();
|
||||||
|
@ -65,8 +55,8 @@ public class IndirectCullingGroup<T extends InstancedPart> {
|
||||||
.quads2Tris(2048).glBuffer;
|
.quads2Tris(2048).glBuffer;
|
||||||
setupVertexArray();
|
setupVertexArray();
|
||||||
|
|
||||||
compute = ComputeCullerCompiler.INSTANCE.get(structType);
|
compute = FlwCompiler.INSTANCE.getCullingProgram(structType);
|
||||||
draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.SHULKER, structType, Components.WORLD, Components.INDIRECT));
|
draw = FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, Components.WORLD, Pipelines.INDIRECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupVertexArray() {
|
private void setupVertexArray() {
|
||||||
|
@ -116,11 +106,8 @@ public class IndirectCullingGroup<T extends InstancedPart> {
|
||||||
uploadInstanceData();
|
uploadInstanceData();
|
||||||
uploadIndirectCommands();
|
uploadIndirectCommands();
|
||||||
|
|
||||||
UniformBuffer.getInstance()
|
|
||||||
.sync();
|
|
||||||
|
|
||||||
compute.bind();
|
compute.bind();
|
||||||
buffers.bindAll();
|
buffers.bindForCompute();
|
||||||
|
|
||||||
var groupCount = (instanceCountThisFrame + 31) >> 5; // ceil(instanceCount / 32)
|
var groupCount = (instanceCountThisFrame + 31) >> 5; // ceil(instanceCount / 32)
|
||||||
glDispatchCompute(groupCount, 1, 1);
|
glDispatchCompute(groupCount, 1, 1);
|
||||||
|
@ -134,11 +121,7 @@ public class IndirectCullingGroup<T extends InstancedPart> {
|
||||||
|
|
||||||
draw.bind();
|
draw.bind();
|
||||||
glBindVertexArray(vertexArray);
|
glBindVertexArray(vertexArray);
|
||||||
buffers.bindObjectAndTarget();
|
buffers.bindForDraw();
|
||||||
buffers.bindIndirectBuffer();
|
|
||||||
|
|
||||||
UniformBuffer.getInstance()
|
|
||||||
.sync();
|
|
||||||
|
|
||||||
memoryBarrier();
|
memoryBarrier();
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import org.lwjgl.system.MemoryUtil;
|
||||||
import com.jozufozu.flywheel.api.RenderStage;
|
import com.jozufozu.flywheel.api.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||||
|
|
||||||
public final class IndirectDraw<T extends InstancedPart> {
|
public final class IndirectDraw<T extends InstancedPart> {
|
||||||
final IndirectInstancer<T> instancer;
|
final IndirectInstancer<T> instancer;
|
||||||
|
@ -13,6 +14,9 @@ public final class IndirectDraw<T extends InstancedPart> {
|
||||||
final RenderStage stage;
|
final RenderStage stage;
|
||||||
int baseInstance = -1;
|
int baseInstance = -1;
|
||||||
|
|
||||||
|
final int vertexMaterialID;
|
||||||
|
final int fragmentMaterialID;
|
||||||
|
|
||||||
boolean needsFullWrite = true;
|
boolean needsFullWrite = true;
|
||||||
|
|
||||||
IndirectDraw(IndirectInstancer<T> instancer, Material material, RenderStage stage, IndirectMeshPool.BufferedMesh mesh) {
|
IndirectDraw(IndirectInstancer<T> instancer, Material material, RenderStage stage, IndirectMeshPool.BufferedMesh mesh) {
|
||||||
|
@ -20,6 +24,9 @@ public final class IndirectDraw<T extends InstancedPart> {
|
||||||
this.material = material;
|
this.material = material;
|
||||||
this.stage = stage;
|
this.stage = stage;
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
|
|
||||||
|
this.vertexMaterialID = ComponentRegistry.materials.getVertexID(material);
|
||||||
|
this.fragmentMaterialID = ComponentRegistry.materials.getFragmentID(material);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void prepare(int baseInstance) {
|
public void prepare(int baseInstance) {
|
||||||
|
@ -51,5 +58,8 @@ public final class IndirectDraw<T extends InstancedPart> {
|
||||||
MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance
|
MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance
|
||||||
|
|
||||||
boundingSphere.getToAddress(ptr + 20); // boundingSphere
|
boundingSphere.getToAddress(ptr + 20); // boundingSphere
|
||||||
|
MemoryUtil.memPutInt(ptr + 36, vertexMaterialID); // vertexMaterialID
|
||||||
|
MemoryUtil.memPutInt(ptr + 40, fragmentMaterialID); // fragmentMaterialID
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.util.List;
|
||||||
import com.jozufozu.flywheel.api.RenderStage;
|
import com.jozufozu.flywheel.api.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.util.Textures;
|
||||||
|
|
||||||
public class IndirectDrawSet<T extends InstancedPart> {
|
public class IndirectDrawSet<T extends InstancedPart> {
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ public class IndirectDrawSet<T extends InstancedPart> {
|
||||||
|
|
||||||
var material = batch.material;
|
var material = batch.material;
|
||||||
material.setup();
|
material.setup();
|
||||||
|
Textures.bindActiveTextures();
|
||||||
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, i * stride, 1, stride);
|
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, i * stride, 1, stride);
|
||||||
material.clear();
|
material.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import java.util.Set;
|
||||||
import org.lwjgl.opengl.GL32;
|
import org.lwjgl.opengl.GL32;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.RenderStage;
|
import com.jozufozu.flywheel.api.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.context.ContextShader;
|
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.instancer.Instancer;
|
import com.jozufozu.flywheel.api.instancer.Instancer;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
|
@ -19,6 +18,7 @@ import com.jozufozu.flywheel.backend.instancing.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
import com.jozufozu.flywheel.core.model.Model;
|
import com.jozufozu.flywheel.core.model.Model;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
|
import com.jozufozu.flywheel.core.context.SimpleContext;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
|
@ -37,12 +37,12 @@ public class IndirectEngine implements Engine {
|
||||||
*/
|
*/
|
||||||
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
|
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
|
||||||
|
|
||||||
protected final ContextShader context;
|
protected final SimpleContext context;
|
||||||
protected final int sqrMaxOriginDistance;
|
protected final int sqrMaxOriginDistance;
|
||||||
|
|
||||||
protected BlockPos originCoordinate = BlockPos.ZERO;
|
protected BlockPos originCoordinate = BlockPos.ZERO;
|
||||||
|
|
||||||
public IndirectEngine(ContextShader context, int sqrMaxOriginDistance) {
|
public IndirectEngine(SimpleContext context, int sqrMaxOriginDistance) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.sqrMaxOriginDistance = sqrMaxOriginDistance;
|
this.sqrMaxOriginDistance = sqrMaxOriginDistance;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,13 @@ package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
|
import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
||||||
import com.jozufozu.flywheel.core.SourceComponent;
|
import com.jozufozu.flywheel.core.SourceComponent;
|
||||||
import com.jozufozu.flywheel.core.layout.LayoutItem;
|
import com.jozufozu.flywheel.core.layout.LayoutItem;
|
||||||
import com.jozufozu.flywheel.core.source.CompilationContext;
|
import com.jozufozu.flywheel.core.source.generate.FnSignature;
|
||||||
|
import com.jozufozu.flywheel.core.source.generate.GlslBlock;
|
||||||
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
|
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
|
||||||
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
|
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
|
||||||
|
|
||||||
|
@ -16,13 +17,17 @@ import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public class InstancedArraysComponent implements SourceComponent {
|
public class InstancedArraysComponent implements SourceComponent {
|
||||||
private static final String ATTRIBUTE_SUFFIX = "_vertex_in";
|
private static final String ATTRIBUTE_SUFFIX = "_vertex_in";
|
||||||
|
private static final String STRUCT_NAME = "Instance";
|
||||||
|
|
||||||
private final List<LayoutItem> layoutItems;
|
private final List<LayoutItem> layoutItems;
|
||||||
private final int baseIndex;
|
private final int baseIndex;
|
||||||
|
|
||||||
public InstancedArraysComponent(List<LayoutItem> layoutItems, int baseIndex) {
|
public InstancedArraysComponent(Pipeline.InstanceAssemblerContext ctx) {
|
||||||
this.layoutItems = layoutItems;
|
this.layoutItems = ctx.structType()
|
||||||
this.baseIndex = baseIndex;
|
.getLayout().layoutItems;
|
||||||
|
this.baseIndex = ctx.vertexType()
|
||||||
|
.getLayout()
|
||||||
|
.getAttributeCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -30,20 +35,15 @@ public class InstancedArraysComponent implements SourceComponent {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String source(CompilationContext ctx) {
|
|
||||||
var generated = generateInstancedArrays("Instance");
|
|
||||||
return ctx.generatedHeader(generated, name().toString()) + generated;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResourceLocation name() {
|
public ResourceLocation name() {
|
||||||
return Flywheel.rl("generated_instanced_arrays");
|
return Flywheel.rl("generated_instanced_arrays");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generateInstancedArrays(String structName) {
|
@Override
|
||||||
|
public String source() {
|
||||||
var builder = new GlslBuilder();
|
var builder = new GlslBuilder();
|
||||||
builder.define("FlwInstance", structName);
|
builder.define("FlwInstance", STRUCT_NAME);
|
||||||
|
|
||||||
int i = baseIndex;
|
int i = baseIndex;
|
||||||
for (var field : layoutItems) {
|
for (var field : layoutItems) {
|
||||||
|
@ -60,7 +60,7 @@ public class InstancedArraysComponent implements SourceComponent {
|
||||||
builder.blankLine();
|
builder.blankLine();
|
||||||
|
|
||||||
var structBuilder = builder.struct();
|
var structBuilder = builder.struct();
|
||||||
structBuilder.setName(structName);
|
structBuilder.setName(STRUCT_NAME);
|
||||||
|
|
||||||
for (var field : layoutItems) {
|
for (var field : layoutItems) {
|
||||||
field.addToStruct(structBuilder);
|
field.addToStruct(structBuilder);
|
||||||
|
@ -68,18 +68,18 @@ public class InstancedArraysComponent implements SourceComponent {
|
||||||
|
|
||||||
builder.blankLine();
|
builder.blankLine();
|
||||||
|
|
||||||
var func = builder.function()
|
// unpacking function
|
||||||
.returnType(structName)
|
builder.function()
|
||||||
.name("flw_unpackInstance");
|
.signature(FnSignature.of(STRUCT_NAME, "flw_unpackInstance"))
|
||||||
|
.body(this::generateUnpackingBody);
|
||||||
var args = layoutItems.stream()
|
|
||||||
.map(it -> new GlslExpr.Variable(it.name() + ATTRIBUTE_SUFFIX))
|
|
||||||
.map(GlslExpr::minPrint)
|
|
||||||
.collect(Collectors.joining(", "));
|
|
||||||
|
|
||||||
func.statement("return " + structName + "(" + args + ");");
|
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateUnpackingBody(GlslBlock b) {
|
||||||
|
var fields = layoutItems.stream()
|
||||||
|
.map(it -> new GlslExpr.Variable(it.name() + ATTRIBUTE_SUFFIX))
|
||||||
|
.toList();
|
||||||
|
b.ret(GlslExpr.call(STRUCT_NAME, fields));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import java.util.Set;
|
||||||
import org.lwjgl.opengl.GL32;
|
import org.lwjgl.opengl.GL32;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.RenderStage;
|
import com.jozufozu.flywheel.api.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.context.ContextShader;
|
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.instancer.Instancer;
|
import com.jozufozu.flywheel.api.instancer.Instancer;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
|
@ -15,12 +14,13 @@ import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
|
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
|
||||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||||
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.TaskExecutor;
|
import com.jozufozu.flywheel.backend.instancing.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.core.Components;
|
import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler;
|
||||||
|
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||||
|
import com.jozufozu.flywheel.core.Pipelines;
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
|
import com.jozufozu.flywheel.core.context.SimpleContext;
|
||||||
import com.jozufozu.flywheel.core.model.Model;
|
import com.jozufozu.flywheel.core.model.Model;
|
||||||
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
|
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
|
@ -40,12 +40,12 @@ public class InstancingEngine implements Engine {
|
||||||
*/
|
*/
|
||||||
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
|
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
|
||||||
|
|
||||||
protected final ContextShader context;
|
protected final SimpleContext context;
|
||||||
protected final int sqrMaxOriginDistance;
|
protected final int sqrMaxOriginDistance;
|
||||||
|
|
||||||
protected BlockPos originCoordinate = BlockPos.ZERO;
|
protected BlockPos originCoordinate = BlockPos.ZERO;
|
||||||
|
|
||||||
public InstancingEngine(ContextShader context, int sqrMaxOriginDistance) {
|
public InstancingEngine(SimpleContext context, int sqrMaxOriginDistance) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.sqrMaxOriginDistance = sqrMaxOriginDistance;
|
this.sqrMaxOriginDistance = sqrMaxOriginDistance;
|
||||||
}
|
}
|
||||||
|
@ -116,12 +116,13 @@ public class InstancingEngine implements Engine {
|
||||||
var structType = desc.instance();
|
var structType = desc.instance();
|
||||||
var material = desc.material();
|
var material = desc.material();
|
||||||
|
|
||||||
var ctx = new PipelineCompiler.Context(vertexType, material, structType, context, Components.INSTANCED_ARRAYS);
|
var program = FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, context, Pipelines.INSTANCED_ARRAYS);
|
||||||
|
program.bind();
|
||||||
|
|
||||||
PipelineCompiler.INSTANCE.getProgram(ctx)
|
var uniformLocation = program.getUniformLocation("_flw_materialID_instancing");
|
||||||
.bind();
|
var vertexID = ComponentRegistry.materials.getVertexID(material);
|
||||||
UniformBuffer.getInstance()
|
var fragmentID = ComponentRegistry.materials.getFragmentID(material);
|
||||||
.sync();
|
GL32.glUniform2ui(uniformLocation, vertexID, fragmentID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,7 +8,7 @@ import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.BackendType;
|
import com.jozufozu.flywheel.backend.BackendType;
|
||||||
import com.jozufozu.flywheel.backend.SimpleBackendType;
|
import com.jozufozu.flywheel.backend.SimpleBackendType;
|
||||||
import com.jozufozu.flywheel.core.BackendTypes;
|
import com.jozufozu.flywheel.core.BackendTypes;
|
||||||
import com.jozufozu.flywheel.core.uniform.FrustumProvider;
|
import com.jozufozu.flywheel.core.uniform.FlwShaderUniforms;
|
||||||
import com.mojang.brigadier.Command;
|
import com.mojang.brigadier.Command;
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||||
|
@ -111,17 +111,17 @@ public class FlwCommands {
|
||||||
commandBuilder.command.then(Commands.literal("debugFrustum")
|
commandBuilder.command.then(Commands.literal("debugFrustum")
|
||||||
.then(Commands.literal("pause")
|
.then(Commands.literal("pause")
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
FrustumProvider.PAUSED = true;
|
FlwShaderUniforms.FRUSTUM_PAUSED = true;
|
||||||
return 1;
|
return 1;
|
||||||
}))
|
}))
|
||||||
.then(Commands.literal("unpause")
|
.then(Commands.literal("unpause")
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
FrustumProvider.PAUSED = false;
|
FlwShaderUniforms.FRUSTUM_PAUSED = false;
|
||||||
return 1;
|
return 1;
|
||||||
}))
|
}))
|
||||||
.then(Commands.literal("capture")
|
.then(Commands.literal("capture")
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
FrustumProvider.CAPTURE = true;
|
FlwShaderUniforms.FRUSTUM_CAPTURE = true;
|
||||||
return 1;
|
return 1;
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -14,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.batching.BatchingEngine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.indirect.IndirectEngine;
|
import com.jozufozu.flywheel.backend.instancing.indirect.IndirectEngine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
|
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
|
||||||
|
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
|
||||||
|
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.network.chat.TextComponent;
|
import net.minecraft.network.chat.TextComponent;
|
||||||
|
@ -55,6 +58,7 @@ public class BackendTypes {
|
||||||
.fallback(() -> BackendTypes.BATCHING)
|
.fallback(() -> BackendTypes.BATCHING)
|
||||||
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
||||||
.instancedArraysSupported())
|
.instancedArraysSupported())
|
||||||
|
.pipelineShader(Pipelines.INSTANCED_ARRAYS)
|
||||||
.register();
|
.register();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,6 +72,7 @@ public class BackendTypes {
|
||||||
.fallback(() -> BackendTypes.INSTANCING)
|
.fallback(() -> BackendTypes.INSTANCING)
|
||||||
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
||||||
.supportsIndirect())
|
.supportsIndirect())
|
||||||
|
.pipelineShader(Pipelines.INDIRECT)
|
||||||
.register();
|
.register();
|
||||||
|
|
||||||
public static BackendType register(BackendType type) {
|
public static BackendType register(BackendType type) {
|
||||||
|
@ -94,4 +99,12 @@ public class BackendTypes {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Collection<SimplePipeline> availablePipelineShaders() {
|
||||||
|
return BACKEND_TYPES.values()
|
||||||
|
.stream()
|
||||||
|
.filter(BackendType::supported)
|
||||||
|
.map(BackendType::pipelineShader)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,31 +3,34 @@ package com.jozufozu.flywheel.core;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.context.Context;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.api.uniform.UniformProvider;
|
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.api.context.ContextShader;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public class ComponentRegistry {
|
public class ComponentRegistry {
|
||||||
private static final Registry<UniformProvider> uniformProviders = new Registry<>();
|
private static final Registry<ShaderUniforms> 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<StructType<?>> structTypes = new HashSet<>();
|
||||||
public static final Set<VertexType> vertexTypes = new HashSet<>();
|
public static final Set<VertexType> vertexTypes = new HashSet<>();
|
||||||
public static final Set<ContextShader> contextShaders = new HashSet<>();
|
public static final Set<Context> contextShaders = new HashSet<>();
|
||||||
|
|
||||||
// TODO: fill out the rest of the registry
|
// TODO: fill out the rest of the registry
|
||||||
|
|
||||||
public static <T extends Material> T register(T material) {
|
public static <T extends Material> T register(T material) {
|
||||||
materials.add(material);
|
return materials.add(material);
|
||||||
return material;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends StructType<?>> T register(T type) {
|
public static <T extends StructType<?>> T register(T type) {
|
||||||
|
@ -40,31 +43,90 @@ public class ComponentRegistry {
|
||||||
return vertexType;
|
return vertexType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ContextShader register(ContextShader contextShader) {
|
public static <T extends Context> T register(T contextShader) {
|
||||||
contextShaders.add(contextShader);
|
contextShaders.add(contextShader);
|
||||||
return contextShader;
|
return contextShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends UniformProvider> T register(T provider) {
|
public static <T extends ShaderUniforms> T register(T provider) {
|
||||||
return uniformProviders.register(provider.getUniformShader()
|
return uniformProviders.register(provider.uniformShader(), provider);
|
||||||
.getFileLoc(), provider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Collection<UniformProvider> getAllUniformProviders() {
|
public static Collection<ShaderUniforms> getAllUniformProviders() {
|
||||||
return Collections.unmodifiableCollection(uniformProviders.objects);
|
return Collections.unmodifiableCollection(uniformProviders.objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static ShaderUniforms getUniformProvider(ResourceLocation loc) {
|
||||||
|
return uniformProviders.get(loc);
|
||||||
|
}
|
||||||
|
|
||||||
private static class Registry<T> {
|
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<>();
|
private final List<T> objects = new ArrayList<>();
|
||||||
|
|
||||||
public <O extends T> O register(ResourceLocation loc, O object) {
|
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);
|
throw new IllegalArgumentException("Shader file already registered: " + loc);
|
||||||
}
|
}
|
||||||
files.add(loc);
|
files.put(loc, object);
|
||||||
objects.add(object);
|
objects.add(object);
|
||||||
return 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.vertexShader());
|
||||||
|
fragmentSources.register(material.fragmentShader());
|
||||||
|
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a list of vertex shader sources where the index in the list is the shader's ID.
|
||||||
|
*/
|
||||||
|
public List<ResourceLocation> vertexSources() {
|
||||||
|
return vertexSources.sourceView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a list of fragment shader sources where the index in the list is the shader's ID.
|
||||||
|
*/
|
||||||
|
public List<ResourceLocation> fragmentSources() {
|
||||||
|
return fragmentSources.sourceView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVertexID(Material material) {
|
||||||
|
return vertexSources.orderedSources.indexOf(material.vertexShader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFragmentID(Material material) {
|
||||||
|
return fragmentSources.orderedSources.indexOf(material.fragmentShader());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MaterialSources {
|
||||||
|
private final Set<ResourceLocation> registered = new HashSet<>();
|
||||||
|
private final List<ResourceLocation> orderedSources = new ArrayList<>();
|
||||||
|
private final List<ResourceLocation> sourceView = Collections.unmodifiableList(orderedSources);
|
||||||
|
|
||||||
|
public void register(ResourceLocation vertexShader) {
|
||||||
|
if (registered.add(vertexShader)) {
|
||||||
|
orderedSources.add(vertexShader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,9 @@
|
||||||
package com.jozufozu.flywheel.core;
|
package com.jozufozu.flywheel.core;
|
||||||
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.api.context.ContextShader;
|
import com.jozufozu.flywheel.core.context.SimpleContext;
|
||||||
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;
|
|
||||||
import com.jozufozu.flywheel.core.source.SourceFile;
|
|
||||||
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
|
|
||||||
import com.jozufozu.flywheel.core.structs.StructTypes;
|
import com.jozufozu.flywheel.core.structs.StructTypes;
|
||||||
import com.jozufozu.flywheel.core.uniform.FogProvider;
|
import com.jozufozu.flywheel.core.uniform.FlwShaderUniforms;
|
||||||
import com.jozufozu.flywheel.core.uniform.FrustumProvider;
|
|
||||||
import com.jozufozu.flywheel.core.uniform.ViewProvider;
|
|
||||||
import com.jozufozu.flywheel.core.vertex.Formats;
|
import com.jozufozu.flywheel.core.vertex.Formats;
|
||||||
import com.jozufozu.flywheel.util.ResourceUtil;
|
import com.jozufozu.flywheel.util.ResourceUtil;
|
||||||
|
|
||||||
|
@ -25,107 +12,32 @@ import net.minecraft.resources.ResourceLocation;
|
||||||
public class Components {
|
public class Components {
|
||||||
|
|
||||||
|
|
||||||
public static final ViewProvider VIEW_PROVIDER = ComponentRegistry.register(new ViewProvider());
|
public static final FlwShaderUniforms UNIFORM_PROVIDER = ComponentRegistry.register(new FlwShaderUniforms());
|
||||||
public static final FogProvider FOG_PROVIDER = ComponentRegistry.register(new FogProvider());
|
public static final SimpleContext WORLD = ComponentRegistry.register(new SimpleContext(Files.WORLD_VERTEX, Files.WORLD_FRAGMENT));
|
||||||
public static final FrustumProvider FRUSTUM_PROVIDER = ComponentRegistry.register(new FrustumProvider());
|
public static final SimpleContext CRUMBLING = ComponentRegistry.register(new SimpleContext(Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT));
|
||||||
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() {
|
public static void init() {
|
||||||
Files.init();
|
|
||||||
Formats.init();
|
Formats.init();
|
||||||
StructTypes.init();
|
StructTypes.init();
|
||||||
Materials.init();
|
Materials.init();
|
||||||
}
|
Pipelines.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Files {
|
public static class Files {
|
||||||
|
|
||||||
public static final FileResolution VIEW_UNIFORMS = uniform(Flywheel.rl("uniform/view.glsl"));
|
public static final ResourceLocation UNIFORMS = Flywheel.rl("uniform/flywheel.glsl");
|
||||||
public static final FileResolution FOG_UNIFORMS = uniform(Flywheel.rl("uniform/fog.glsl"));
|
public static final ResourceLocation BLOCK_LAYOUT = ResourceUtil.subPath(Names.BLOCK, ".vert");
|
||||||
public static final FileResolution FRUSTUM_UNIFORMS = uniform(Flywheel.rl("uniform/frustum.glsl"));
|
public static final ResourceLocation POS_TEX_NORMAL_LAYOUT = ResourceUtil.subPath(Names.POS_TEX_NORMAL, ".vert");
|
||||||
public static final FileResolution BLOCK_LAYOUT = layoutVertex(ResourceUtil.subPath(Names.BLOCK, ".vert"));
|
public static final ResourceLocation TRANSFORMED = ResourceUtil.subPath(Names.TRANSFORMED, ".vert");
|
||||||
public static final FileResolution POS_TEX_NORMAL_LAYOUT = layoutVertex(ResourceUtil.subPath(Names.POS_TEX_NORMAL, ".vert"));
|
public static final ResourceLocation ORIENTED = ResourceUtil.subPath(Names.ORIENTED, ".vert");
|
||||||
public static final FileResolution TRANSFORMED = instanceVertex(ResourceUtil.subPath(Names.TRANSFORMED, ".vert"));
|
public static final ResourceLocation DEFAULT_VERTEX = ResourceUtil.subPath(Names.DEFAULT, ".vert");
|
||||||
public static final FileResolution ORIENTED = instanceVertex(ResourceUtil.subPath(Names.ORIENTED, ".vert"));
|
public static final ResourceLocation SHADED_VERTEX = ResourceUtil.subPath(Names.SHADED, ".vert");
|
||||||
public static final FileResolution DEFAULT_VERTEX = materialVertex(ResourceUtil.subPath(Names.DEFAULT, ".vert"));
|
public static final ResourceLocation DEFAULT_FRAGMENT = ResourceUtil.subPath(Names.DEFAULT, ".frag");
|
||||||
public static final FileResolution SHADED_VERTEX = materialVertex(ResourceUtil.subPath(Names.SHADED, ".vert"));
|
public static final ResourceLocation CUTOUT_FRAGMENT = ResourceUtil.subPath(Names.CUTOUT, ".frag");
|
||||||
public static final FileResolution DEFAULT_FRAGMENT = materialFragment(ResourceUtil.subPath(Names.DEFAULT, ".frag"));
|
public static final ResourceLocation WORLD_VERTEX = ResourceUtil.subPath(Names.WORLD, ".vert");
|
||||||
public static final FileResolution CUTOUT_FRAGMENT = materialFragment(ResourceUtil.subPath(Names.CUTOUT, ".frag"));
|
public static final ResourceLocation WORLD_FRAGMENT = ResourceUtil.subPath(Names.WORLD, ".frag");
|
||||||
public static final FileResolution WORLD_VERTEX = contextVertex(ResourceUtil.subPath(Names.WORLD, ".vert"));
|
public static final ResourceLocation CRUMBLING_VERTEX = ResourceUtil.subPath(Names.CRUMBLING, ".vert");
|
||||||
public static final FileResolution WORLD_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.WORLD, ".frag"));
|
public static final ResourceLocation CRUMBLING_FRAGMENT = ResourceUtil.subPath(Names.CRUMBLING, ".frag");
|
||||||
public static final FileResolution CRUMBLING_VERTEX = contextVertex(ResourceUtil.subPath(Names.CRUMBLING, ".vert"));
|
|
||||||
public static final FileResolution CRUMBLING_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.CRUMBLING, ".frag"));
|
|
||||||
|
|
||||||
private static FileResolution compute(ResourceLocation rl) {
|
|
||||||
return FileResolution.get(rl);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileResolution uniform(ResourceLocation location) {
|
|
||||||
return FileResolution.get(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileResolution layoutVertex(ResourceLocation location) {
|
|
||||||
return FileResolution.get(location)
|
|
||||||
.validateWith(Checks.LAYOUT_VERTEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileResolution instanceVertex(ResourceLocation location) {
|
|
||||||
return FileResolution.get(location); // .validateWith(Checks.INSTANCE_VERTEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileResolution materialVertex(ResourceLocation location) {
|
|
||||||
return FileResolution.get(location)
|
|
||||||
.validateWith(Checks.MATERIAL_VERTEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileResolution materialFragment(ResourceLocation location) {
|
|
||||||
return FileResolution.get(location)
|
|
||||||
.validateWith(Checks.MATERIAL_FRAGMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileResolution contextVertex(ResourceLocation location) {
|
|
||||||
return FileResolution.get(location)
|
|
||||||
.validateWith(Checks.CONTEXT_VERTEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileResolution contextFragment(ResourceLocation location) {
|
|
||||||
return FileResolution.get(location)
|
|
||||||
.validateWith(Checks.CONTEXT_FRAGMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
// noop, just in case
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Checks {
|
|
||||||
|
|
||||||
public static final BiConsumer<ErrorReporter, SourceFile> LAYOUT_VERTEX = SourceChecks.checkFunctionArity("flw_layoutVertex", 0);
|
|
||||||
public static final BiConsumer<ErrorReporter, SourceFile> INSTANCE_VERTEX = SourceChecks.checkFunctionParameterTypeExists("flw_instanceVertex", 1, 0);
|
|
||||||
public static final BiConsumer<ErrorReporter, SourceFile> MATERIAL_VERTEX = SourceChecks.checkFunctionArity("flw_materialVertex", 0);
|
|
||||||
public static final BiConsumer<ErrorReporter, SourceFile> MATERIAL_FRAGMENT = SourceChecks.checkFunctionArity("flw_materialFragment", 0);
|
|
||||||
public static final BiConsumer<ErrorReporter, SourceFile> CONTEXT_VERTEX = SourceChecks.checkFunctionArity("flw_contextVertex", 0);
|
|
||||||
public static final BiConsumer<ErrorReporter, SourceFile> CONTEXT_FRAGMENT = SourceChecks.checkFunctionArity("flw_contextFragment", 0)
|
|
||||||
.andThen(SourceChecks.checkFunctionArity("flw_initFragment", 0));
|
|
||||||
|
|
||||||
public static final BiConsumer<ErrorReporter, SourceFile> PIPELINE = SourceChecks.checkFunctionArity("main", 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Names {
|
public static class Names {
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
36
src/main/java/com/jozufozu/flywheel/core/Pipelines.java
Normal file
36
src/main/java/com/jozufozu/flywheel/core/Pipelines.java
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
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 net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
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 ResourceLocation DRAW_FRAGMENT = Flywheel.rl("pipeline/draw.frag");
|
||||||
|
public static final ResourceLocation INSTANCED_ARRAYS_DRAW = Flywheel.rl("pipeline/instanced_arrays_draw.vert");
|
||||||
|
public static final ResourceLocation INDIRECT_DRAW = Flywheel.rl("pipeline/indirect_draw.vert");
|
||||||
|
public static final ResourceLocation INDIRECT_CULL = Flywheel.rl("pipeline/indirect_cull.glsl");
|
||||||
|
public static final ResourceLocation UTIL_TYPES = Flywheel.rl("util/types.glsl");
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,14 +2,12 @@ package com.jozufozu.flywheel.core;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.source.CompilationContext;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public interface SourceComponent {
|
public interface SourceComponent {
|
||||||
Collection<? extends SourceComponent> included();
|
Collection<? extends SourceComponent> included();
|
||||||
|
|
||||||
String source(CompilationContext ctx);
|
String source();
|
||||||
|
|
||||||
ResourceLocation name();
|
ResourceLocation name();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
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?
|
|
||||||
protected int diffuseTex;
|
|
||||||
protected int overlayTex;
|
|
||||||
protected int lightTex;
|
|
||||||
|
|
||||||
public WorldProgram(ResourceLocation name, int handle) {
|
|
||||||
super(name, handle);
|
|
||||||
|
|
||||||
bind();
|
|
||||||
registerSamplers();
|
|
||||||
unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void registerSamplers() {
|
|
||||||
diffuseTex = setSamplerBinding("flw_diffuseTex", 0);
|
|
||||||
overlayTex = setSamplerBinding("flw_overlayTex", 1);
|
|
||||||
lightTex = setSamplerBinding("flw_lightTex", 2);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.compile;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.GL_TRUE;
|
|
||||||
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
|
|
||||||
import static org.lwjgl.opengl.GL20.glAttachShader;
|
|
||||||
import static org.lwjgl.opengl.GL20.glCreateProgram;
|
|
||||||
import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
|
|
||||||
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;
|
|
||||||
|
|
||||||
public class ProgramAssembler {
|
|
||||||
public final int program;
|
|
||||||
private final ResourceLocation name;
|
|
||||||
|
|
||||||
public ProgramAssembler(ResourceLocation name) {
|
|
||||||
this.name = name;
|
|
||||||
this.program = glCreateProgram();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Links the attached shaders to this program.
|
|
||||||
*/
|
|
||||||
public ProgramAssembler link() {
|
|
||||||
glLinkProgram(this.program);
|
|
||||||
|
|
||||||
String log = glGetProgramInfoLog(this.program);
|
|
||||||
|
|
||||||
if (!log.isEmpty()) {
|
|
||||||
Backend.LOGGER.debug("Program link log for " + name + ": " + log);
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = glGetProgrami(this.program, GL_LINK_STATUS);
|
|
||||||
|
|
||||||
if (result != GL_TRUE) {
|
|
||||||
throw new RuntimeException("Shader program linking failed, see log for details");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProgramAssembler attachShader(GlShader glShader) {
|
|
||||||
glAttachShader(this.program, glShader.handle());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlProgram build(GlProgram.Factory factory) {
|
|
||||||
return factory.create(name, program);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.compile;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.source.CompilationContext;
|
|
||||||
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
|
|
||||||
|
|
||||||
public class ShaderCompilationException extends ShaderLoadingException {
|
|
||||||
|
|
||||||
private final int shaderHandle;
|
|
||||||
|
|
||||||
public String errors = "";
|
|
||||||
|
|
||||||
public ShaderCompilationException(String message, int shaderHandle) {
|
|
||||||
super(message);
|
|
||||||
this.shaderHandle = shaderHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderLoadingException withErrorLog(CompilationContext ctx) {
|
|
||||||
if (this.shaderHandle == -1)
|
|
||||||
return this;
|
|
||||||
|
|
||||||
this.errors = ctx.parseErrors(GL20.glGetShaderInfoLog(this.shaderHandle));
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMessage() {
|
|
||||||
return super.getMessage() + '\n' + this.errors;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.jozufozu.flywheel.core.context;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.context.Context;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public record SimpleContext(ResourceLocation vertexShader, ResourceLocation fragmentShader) implements Context {
|
||||||
|
@Override
|
||||||
|
public void onProgramLink(GlProgram program) {
|
||||||
|
program.bind();
|
||||||
|
program.setSamplerBinding("flw_diffuseTex", 0);
|
||||||
|
program.setSamplerBinding("flw_overlayTex", 1);
|
||||||
|
program.setSamplerBinding("flw_lightTex", 2);
|
||||||
|
GlProgram.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation vertexShader() {
|
||||||
|
return vertexShader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation fragmentShader() {
|
||||||
|
return fragmentShader;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void registerSamplers() {
|
|
||||||
diffuseTex = setSamplerBinding("flw_diffuseTex", 0);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.jozufozu.flywheel.core.layout;
|
package com.jozufozu.flywheel.core.layout;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
|
|
||||||
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
|
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
|
||||||
|
import com.jozufozu.flywheel.core.source.generate.GlslStruct;
|
||||||
|
|
||||||
public record LayoutItem(InputType type, String name) {
|
public record LayoutItem(InputType type, String name) {
|
||||||
public GlslExpr unpackField(GlslExpr.Variable struct) {
|
public GlslExpr unpackField(GlslExpr.Variable struct) {
|
||||||
|
@ -9,11 +9,11 @@ public record LayoutItem(InputType type, String name) {
|
||||||
.transform(type()::unpack);
|
.transform(type()::unpack);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addToStruct(GlslBuilder.StructBuilder structBuilder) {
|
public void addToStruct(GlslStruct glslStruct) {
|
||||||
structBuilder.addField(type().typeName(), name());
|
glslStruct.addField(type().typeName(), name());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPackedToStruct(GlslBuilder.StructBuilder packed) {
|
public void addPackedToStruct(GlslStruct packed) {
|
||||||
packed.addField(type().packedTypeName(), name());
|
packed.addField(type().packedTypeName(), name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,20 @@ package com.jozufozu.flywheel.core.material;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.core.ComponentRegistry;
|
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||||
import com.jozufozu.flywheel.core.Components;
|
import com.jozufozu.flywheel.core.Components;
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.RenderStateShard;
|
import net.minecraft.client.renderer.RenderStateShard;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public class SimpleMaterial implements Material {
|
public class SimpleMaterial implements Material {
|
||||||
protected final FileResolution vertexShader;
|
protected final ResourceLocation vertexShader;
|
||||||
protected final FileResolution fragmentShader;
|
protected final ResourceLocation fragmentShader;
|
||||||
protected final Runnable setup;
|
protected final Runnable setup;
|
||||||
protected final Runnable clear;
|
protected final Runnable clear;
|
||||||
protected final RenderType batchingRenderType;
|
protected final RenderType batchingRenderType;
|
||||||
protected final VertexTransformer vertexTransformer;
|
protected final VertexTransformer vertexTransformer;
|
||||||
|
|
||||||
public SimpleMaterial(FileResolution vertexShader, FileResolution fragmentShader, Runnable setup, Runnable clear, RenderType batchingRenderType, VertexTransformer vertexTransformer) {
|
public SimpleMaterial(ResourceLocation vertexShader, ResourceLocation fragmentShader, Runnable setup, Runnable clear, RenderType batchingRenderType, VertexTransformer vertexTransformer) {
|
||||||
this.vertexShader = vertexShader;
|
this.vertexShader = vertexShader;
|
||||||
this.fragmentShader = fragmentShader;
|
this.fragmentShader = fragmentShader;
|
||||||
this.setup = setup;
|
this.setup = setup;
|
||||||
|
@ -30,12 +30,12 @@ public class SimpleMaterial implements Material {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileResolution getVertexShader() {
|
public ResourceLocation vertexShader() {
|
||||||
return vertexShader;
|
return vertexShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileResolution getFragmentShader() {
|
public ResourceLocation fragmentShader() {
|
||||||
return fragmentShader;
|
return fragmentShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,22 +60,23 @@ public class SimpleMaterial implements Material {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
protected FileResolution vertexShader = Components.Files.DEFAULT_VERTEX;
|
protected ResourceLocation vertexShader = Components.Files.DEFAULT_VERTEX;
|
||||||
protected FileResolution fragmentShader = Components.Files.DEFAULT_FRAGMENT;
|
protected ResourceLocation fragmentShader = Components.Files.DEFAULT_FRAGMENT;
|
||||||
protected Runnable setup = () -> {};
|
protected Runnable setup = () -> {};
|
||||||
protected Runnable clear = () -> {};
|
protected Runnable clear = () -> {};
|
||||||
protected RenderType batchingRenderType = RenderType.solid();
|
protected RenderType batchingRenderType = RenderType.solid();
|
||||||
protected VertexTransformer vertexTransformer = (vertexList, level) -> {};
|
protected VertexTransformer vertexTransformer = (vertexList, level) -> {
|
||||||
|
};
|
||||||
|
|
||||||
public Builder() {
|
public Builder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder vertexShader(FileResolution vertexShader) {
|
public Builder vertexShader(ResourceLocation vertexShader) {
|
||||||
this.vertexShader = vertexShader;
|
this.vertexShader = vertexShader;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder fragmentShader(FileResolution fragmentShader) {
|
public Builder fragmentShader(ResourceLocation fragmentShader) {
|
||||||
this.fragmentShader = fragmentShader;
|
this.fragmentShader = fragmentShader;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
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 net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public final class SimplePipeline implements Pipeline {
|
||||||
|
private final GLSLVersion glslVersion;
|
||||||
|
private final ResourceLocation vertex;
|
||||||
|
private final ResourceLocation fragment;
|
||||||
|
private final InstanceAssemblerFactory factory;
|
||||||
|
|
||||||
|
public SimplePipeline(GLSLVersion glslVersion, ResourceLocation vertex, ResourceLocation 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 ResourceLocation vertexShader() {
|
||||||
|
return vertex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation fragmentShader() {
|
||||||
|
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 ResourceLocation vertex;
|
||||||
|
private ResourceLocation fragment;
|
||||||
|
private InstanceAssemblerFactory factory;
|
||||||
|
|
||||||
|
public Builder glslVersion(GLSLVersion glslVersion) {
|
||||||
|
this.glslVersion = glslVersion;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder vertex(ResourceLocation vertex) {
|
||||||
|
this.vertex = vertex;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder fragment(ResourceLocation 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,95 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.source;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.source.error.ErrorBuilder;
|
|
||||||
import com.jozufozu.flywheel.core.source.span.Span;
|
|
||||||
|
|
||||||
public class CompilationContext {
|
|
||||||
public final List<SourceFile> files = new ArrayList<>();
|
|
||||||
|
|
||||||
private String generatedSource = "";
|
|
||||||
private int generatedLines = 0;
|
|
||||||
|
|
||||||
|
|
||||||
String sourceHeader(SourceFile sourceFile) {
|
|
||||||
return "#line " + 0 + ' ' + getOrCreateFileID(sourceFile) + " // " + sourceFile.name + '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
public String generatedHeader(String generatedCode, @Nullable String comment) {
|
|
||||||
generatedSource += generatedCode;
|
|
||||||
int lines = generatedCode.split("\n").length;
|
|
||||||
|
|
||||||
var out = "#line " + generatedLines + ' ' + 0;
|
|
||||||
|
|
||||||
generatedLines += lines;
|
|
||||||
|
|
||||||
if (comment != null) {
|
|
||||||
out += " // " + comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
return out + '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean contains(SourceFile sourceFile) {
|
|
||||||
return files.contains(sourceFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an arbitrary file ID for use this compilation context, or generates one if missing.
|
|
||||||
*
|
|
||||||
* @param sourceFile The file to retrieve the ID for.
|
|
||||||
* @return A file ID unique to the given sourceFile.
|
|
||||||
*/
|
|
||||||
private int getOrCreateFileID(SourceFile sourceFile) {
|
|
||||||
int i = files.indexOf(sourceFile);
|
|
||||||
if (i != -1) {
|
|
||||||
return i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
files.add(sourceFile);
|
|
||||||
return files.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Span getLineSpan(int fileId, int lineNo) {
|
|
||||||
if (fileId == 0) {
|
|
||||||
// TODO: Valid spans for generated code.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return getFile(fileId).getLineSpanNoWhitespace(lineNo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SourceFile getFile(int fileId) {
|
|
||||||
return files.get(fileId - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String parseErrors(String log) {
|
|
||||||
List<String> lines = log.lines()
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
StringBuilder errors = new StringBuilder();
|
|
||||||
for (String line : lines) {
|
|
||||||
ErrorBuilder builder = parseCompilerError(line);
|
|
||||||
|
|
||||||
if (builder != null) {
|
|
||||||
errors.append(builder.build());
|
|
||||||
} else {
|
|
||||||
errors.append(line).append('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return errors.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private ErrorBuilder parseCompilerError(String line) {
|
|
||||||
try {
|
|
||||||
return ErrorBuilder.fromLogLine(this, line);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,186 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.source;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.source.error.ErrorBuilder;
|
|
||||||
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
|
|
||||||
import com.jozufozu.flywheel.core.source.span.Span;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A reference to a source file that might not be loaded when the owning object is created.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* FileResolutions are used primarily while parsing import statements. {@link FileResolution#file} is initially
|
|
||||||
* null, but will be populated later on, after <em>all</em> SourceFiles are loaded (assuming
|
|
||||||
* {@link FileResolution#fileLoc} references an actual file).
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class FileResolution {
|
|
||||||
|
|
||||||
private static final Map<ResourceLocation, FileResolution> ALL = new HashMap<>();
|
|
||||||
private static final Map<ResourceLocation, FileResolution> WEAK = new HashMap<>();
|
|
||||||
private static boolean tooLate = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extra info about where this resolution is required. Includes shader Spans.
|
|
||||||
*/
|
|
||||||
private final List<Span> neededAt = new ArrayList<>();
|
|
||||||
private final List<BiConsumer<ErrorReporter, SourceFile>> checks = new ArrayList<>();
|
|
||||||
|
|
||||||
private final ResourceLocation fileLoc;
|
|
||||||
private final boolean weak;
|
|
||||||
|
|
||||||
private SourceFile file;
|
|
||||||
|
|
||||||
private FileResolution(ResourceLocation fileLoc, boolean weak) {
|
|
||||||
this.fileLoc = fileLoc;
|
|
||||||
this.weak = weak;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FileResolution get(ResourceLocation file) {
|
|
||||||
if (!tooLate) {
|
|
||||||
return ALL.computeIfAbsent(file, loc -> new FileResolution(loc, false));
|
|
||||||
} else {
|
|
||||||
// Lock the map after resolution has run.
|
|
||||||
FileResolution fileResolution = ALL.get(file);
|
|
||||||
|
|
||||||
// ...so crash immediately if the file isn't found.
|
|
||||||
if (fileResolution == null) {
|
|
||||||
throw new ShaderLoadingException("could not find source for file: " + file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileResolution;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Weak resolutions don't persist through resource reloads.<p>
|
|
||||||
* This should be used inside parsing code.
|
|
||||||
*
|
|
||||||
* @param file The location of the file to resolve.
|
|
||||||
* @return A weak resolution for the given file.
|
|
||||||
*/
|
|
||||||
public static FileResolution weak(ResourceLocation file) {
|
|
||||||
FileResolution fileResolution = ALL.get(file);
|
|
||||||
|
|
||||||
if (fileResolution != null) {
|
|
||||||
return fileResolution;
|
|
||||||
}
|
|
||||||
// never too late for weak resolutions.
|
|
||||||
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) {
|
|
||||||
builder.pointAtFile(location.getSourceFile())
|
|
||||||
.pointAt(location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runChecks(ErrorReporter errorReporter) {
|
|
||||||
for (var check : checks) {
|
|
||||||
check.accept(errorReporter, file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResourceLocation getFileLoc() {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store the given span so this resolution can know all the places that reference the file.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Used for error reporting.
|
|
||||||
* </p>
|
|
||||||
* @param span A span where this file is referenced.
|
|
||||||
*/
|
|
||||||
public FileResolution addSpan(Span span) {
|
|
||||||
neededAt.add(span);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileResolution validateWith(BiConsumer<ErrorReporter, SourceFile> check) {
|
|
||||||
checks.add(check);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "FileResolution[" + fileLoc + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileResolution that = (FileResolution) o;
|
|
||||||
|
|
||||||
return fileLoc.equals(that.fileLoc);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
// FileResolutions are interned and therefore can be hashed based on object identity.
|
|
||||||
// Overriding this to make it explicit.
|
|
||||||
return System.identityHashCode(this);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,4 +5,8 @@ public class ShaderLoadingException extends RuntimeException {
|
||||||
public ShaderLoadingException(String message) {
|
public ShaderLoadingException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ShaderLoadingException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package com.jozufozu.flywheel.core.source;
|
package com.jozufozu.flywheel.core.source;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import javax.annotation.Nonnull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
|
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
|
||||||
|
@ -15,72 +16,74 @@ import com.jozufozu.flywheel.util.ResourceUtil;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.resources.Resource;
|
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main object for loading and parsing source files.
|
* 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 String SHADER_DIR = "flywheel/";
|
||||||
public static final ArrayList<String> EXTENSIONS = Lists.newArrayList(".vert", ".vsh", ".frag", ".fsh", ".glsl");
|
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 where we are in the mutual recursion to detect circular imports.
|
||||||
|
*/
|
||||||
|
private final Deque<ResourceLocation> findStack = new ArrayDeque<>();
|
||||||
|
|
||||||
public final long loadTimeNs;
|
private final ResourceManager manager;
|
||||||
public final long indexTimeNs;
|
private final ErrorReporter errorReporter;
|
||||||
public final long totalTimeNs;
|
|
||||||
|
|
||||||
public ShaderSources(ErrorReporter errorReporter, ResourceManager manager) {
|
public ShaderSources(ErrorReporter errorReporter, ResourceManager manager) {
|
||||||
long loadStart = System.nanoTime();
|
this.errorReporter = errorReporter;
|
||||||
|
this.manager = manager;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postResolve() {
|
@Nonnull
|
||||||
for (SourceFile file : shaderSources.values()) {
|
public SourceFile find(ResourceLocation location) {
|
||||||
file.postResolve();
|
pushFindStack(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);
|
||||||
|
}
|
||||||
|
popFindStack();
|
||||||
|
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
|
private void generateRecursiveImportException(ResourceLocation location) {
|
||||||
@Nullable
|
findStack.add(location);
|
||||||
public SourceFile findSource(ResourceLocation name) {
|
String path = findStack.stream()
|
||||||
return shaderSources.get(name);
|
.dropWhile(l -> !l.equals(location))
|
||||||
|
.map(ResourceLocation::toString)
|
||||||
|
.collect(Collectors.joining(" -> "));
|
||||||
|
findStack.clear();
|
||||||
|
throw new ShaderLoadingException("recursive import: " + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private void pushFindStack(ResourceLocation location) {
|
||||||
private static Collection<ResourceLocation> getValidShaderFiles(ResourceManager manager) {
|
if (findStack.contains(location)) {
|
||||||
return manager.listResources(SHADER_DIR, s -> {
|
generateRecursiveImportException(location);
|
||||||
for (String ext : EXTENSIONS) {
|
|
||||||
if (s.endsWith(ext)) return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
findStack.add(location);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLoadTime() {
|
private void popFindStack() {
|
||||||
return StringUtil.formatTime(totalTimeNs);
|
findStack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,17 @@ import com.jozufozu.flywheel.core.source.parse.ShaderVariable;
|
||||||
|
|
||||||
public class SourceChecks {
|
public class SourceChecks {
|
||||||
|
|
||||||
|
// TODO: recycle to be invoked by the shader compiler
|
||||||
|
|
||||||
|
public static final BiConsumer<ErrorReporter, SourceFile> LAYOUT_VERTEX = checkFunctionArity("flw_layoutVertex", 0);
|
||||||
|
public static final BiConsumer<ErrorReporter, SourceFile> INSTANCE_VERTEX = checkFunctionParameterTypeExists("flw_instanceVertex", 1, 0);
|
||||||
|
public static final BiConsumer<ErrorReporter, SourceFile> MATERIAL_VERTEX = checkFunctionArity("flw_materialVertex", 0);
|
||||||
|
public static final BiConsumer<ErrorReporter, SourceFile> MATERIAL_FRAGMENT = checkFunctionArity("flw_materialFragment", 0);
|
||||||
|
public static final BiConsumer<ErrorReporter, SourceFile> CONTEXT_VERTEX = checkFunctionArity("flw_contextVertex", 0);
|
||||||
|
public static final BiConsumer<ErrorReporter, SourceFile> CONTEXT_FRAGMENT = checkFunctionArity("flw_contextFragment", 0).andThen(checkFunctionArity("flw_initFragment", 0));
|
||||||
|
public static final BiConsumer<ErrorReporter, SourceFile> PIPELINE = checkFunctionArity("main", 0);
|
||||||
|
|
||||||
|
|
||||||
public static BiConsumer<ErrorReporter, SourceFile> checkFunctionArity(String name, int arity) {
|
public static BiConsumer<ErrorReporter, SourceFile> checkFunctionArity(String name, int arity) {
|
||||||
return (errorReporter, file) -> checkFunctionArity(errorReporter, file, name, arity);
|
return (errorReporter, file) -> checkFunctionArity(errorReporter, file, name, arity);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,11 @@
|
||||||
package com.jozufozu.flywheel.core.source;
|
package com.jozufozu.flywheel.core.source;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
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 java.util.regex.Matcher;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.jozufozu.flywheel.core.SourceComponent;
|
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.Import;
|
||||||
import com.jozufozu.flywheel.core.source.parse.ShaderField;
|
import com.jozufozu.flywheel.core.source.parse.ShaderField;
|
||||||
import com.jozufozu.flywheel.core.source.parse.ShaderFunction;
|
import com.jozufozu.flywheel.core.source.parse.ShaderFunction;
|
||||||
|
@ -37,9 +28,8 @@ public class SourceFile implements SourceComponent {
|
||||||
public final ResourceLocation name;
|
public final ResourceLocation name;
|
||||||
|
|
||||||
public final ShaderSources parent;
|
public final ShaderSources parent;
|
||||||
public final String source;
|
|
||||||
|
|
||||||
public final SourceLines lines;
|
public final SourceLines source;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function lookup by name.
|
* Function lookup by name.
|
||||||
|
@ -57,30 +47,42 @@ public class SourceFile implements SourceComponent {
|
||||||
public final ImmutableList<Import> imports;
|
public final ImmutableList<Import> imports;
|
||||||
public final ImmutableMap<String, ShaderField> fields;
|
public final ImmutableMap<String, ShaderField> fields;
|
||||||
|
|
||||||
// POST-RESOLUTION
|
public final List<SourceFile> included;
|
||||||
public List<SourceFile> flattenedImports;
|
|
||||||
|
|
||||||
public SourceFile(ErrorReporter errorReporter, ShaderSources parent, ResourceLocation name, String source) {
|
public SourceFile(ShaderSources sourceFinder, ResourceLocation name, String source) {
|
||||||
this.parent = parent;
|
this.parent = sourceFinder;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.source = source;
|
|
||||||
|
|
||||||
this.lines = new SourceLines(source);
|
this.source = new SourceLines(source);
|
||||||
|
|
||||||
this.imports = parseImports(errorReporter);
|
this.imports = parseImports(source);
|
||||||
this.functions = parseFunctions();
|
this.functions = parseFunctions(source);
|
||||||
this.structs = parseStructs();
|
this.structs = parseStructs(source);
|
||||||
this.fields = parseFields();
|
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);
|
||||||
|
sink.accept(sourceFile);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<? extends SourceComponent> included() {
|
public Collection<? extends SourceComponent> included() {
|
||||||
return flattenedImports;
|
return included;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String source(CompilationContext ctx) {
|
public String source() {
|
||||||
return ctx.sourceHeader(this) + this.elideImports();
|
return this.genFinalSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -88,52 +90,23 @@ public class SourceFile implements SourceComponent {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postResolve() {
|
public Span getLineSpan(int lineNo) {
|
||||||
this.flattenImports();
|
int begin = source.lineStartIndex(lineNo);
|
||||||
}
|
int end = begin + source.lineString(lineNo)
|
||||||
|
.length();
|
||||||
private void flattenImports() {
|
return new StringSpan(this, source.getCharPos(begin), source.getCharPos(end));
|
||||||
// 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 getLineSpanNoWhitespace(int line) {
|
public Span getLineSpanNoWhitespace(int line) {
|
||||||
int begin = lines.getLineStart(line);
|
int begin = source.lineStartIndex(line);
|
||||||
int end = begin + lines.getLine(line).length();
|
int end = begin + source.lineString(line)
|
||||||
|
.length();
|
||||||
|
|
||||||
while (begin < end && Character.isWhitespace(source.charAt(begin))) {
|
while (begin < end && Character.isWhitespace(source.charAt(begin))) {
|
||||||
begin++;
|
begin++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new StringSpan(this, lines.getCharPos(begin), lines.getCharPos(end));
|
return new StringSpan(this, source.getCharPos(begin), source.getCharPos(end));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,12 +115,14 @@ public class SourceFile implements SourceComponent {
|
||||||
* @param name The name of the struct to find.
|
* @param name The name of the struct to find.
|
||||||
* @return null if no definition matches the name.
|
* @return null if no definition matches the name.
|
||||||
*/
|
*/
|
||||||
public Optional<ShaderStruct> findStruct(String name) {
|
public Optional<ShaderStruct> findStructByName(String name) {
|
||||||
ShaderStruct struct = structs.get(name);
|
ShaderStruct struct = structs.get(name);
|
||||||
|
|
||||||
if (struct != null) return Optional.of(struct);
|
if (struct != null) {
|
||||||
|
return Optional.of(struct);
|
||||||
|
}
|
||||||
|
|
||||||
for (var include : flattenedImports) {
|
for (var include : included) {
|
||||||
var external = include.structs.get(name);
|
var external = include.structs.get(name);
|
||||||
|
|
||||||
if (external != null) {
|
if (external != null) {
|
||||||
|
@ -169,7 +144,7 @@ public class SourceFile implements SourceComponent {
|
||||||
|
|
||||||
if (local != null) return Optional.of(local);
|
if (local != null) return Optional.of(local);
|
||||||
|
|
||||||
for (var include : flattenedImports) {
|
for (var include : included) {
|
||||||
var external = include.functions.get(name);
|
var external = include.functions.get(name);
|
||||||
|
|
||||||
if (external != null) {
|
if (external != null) {
|
||||||
|
@ -185,10 +160,10 @@ public class SourceFile implements SourceComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String printSource() {
|
public String printSource() {
|
||||||
return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers();
|
return "Source for shader '" + name + "':\n" + source.printLinesWithNumbers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CharSequence elideImports() {
|
private String genFinalSource() {
|
||||||
StringBuilder out = new StringBuilder();
|
StringBuilder out = new StringBuilder();
|
||||||
|
|
||||||
int lastEnd = 0;
|
int lastEnd = 0;
|
||||||
|
@ -203,13 +178,50 @@ public class SourceFile implements SourceComponent {
|
||||||
|
|
||||||
out.append(this.source, lastEnd, this.source.length());
|
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.
|
* 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);
|
Matcher matcher = ShaderFunction.PATTERN.matcher(source);
|
||||||
|
|
||||||
Map<String, ShaderFunction> functions = new HashMap<>();
|
Map<String, ShaderFunction> functions = new HashMap<>();
|
||||||
|
@ -220,7 +232,7 @@ public class SourceFile implements SourceComponent {
|
||||||
Span args = Span.fromMatcher(this, matcher, 3);
|
Span args = Span.fromMatcher(this, matcher, 3);
|
||||||
|
|
||||||
int blockStart = matcher.end();
|
int blockStart = matcher.end();
|
||||||
int blockEnd = findEndOfBlock(blockStart);
|
int blockEnd = findEndOfBlock(source, blockStart);
|
||||||
|
|
||||||
Span self;
|
Span self;
|
||||||
Span body;
|
Span body;
|
||||||
|
@ -243,7 +255,7 @@ public class SourceFile implements SourceComponent {
|
||||||
/**
|
/**
|
||||||
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
|
* 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);
|
Matcher matcher = ShaderStruct.PATTERN.matcher(source);
|
||||||
|
|
||||||
ImmutableMap.Builder<String, ShaderStruct> structs = ImmutableMap.builder();
|
ImmutableMap.Builder<String, ShaderStruct> structs = ImmutableMap.builder();
|
||||||
|
@ -251,8 +263,9 @@ public class SourceFile implements SourceComponent {
|
||||||
Span self = Span.fromMatcher(this, matcher);
|
Span self = Span.fromMatcher(this, matcher);
|
||||||
Span name = Span.fromMatcher(this, matcher, 1);
|
Span name = Span.fromMatcher(this, matcher, 1);
|
||||||
Span body = Span.fromMatcher(this, matcher, 2);
|
Span body = Span.fromMatcher(this, matcher, 2);
|
||||||
|
Span variableName = Span.fromMatcher(this, matcher, 3);
|
||||||
|
|
||||||
ShaderStruct shaderStruct = new ShaderStruct(self, name, body);
|
ShaderStruct shaderStruct = new ShaderStruct(self, name, body, variableName);
|
||||||
|
|
||||||
structs.put(name.get(), shaderStruct);
|
structs.put(name.get(), shaderStruct);
|
||||||
}
|
}
|
||||||
|
@ -263,7 +276,7 @@ public class SourceFile implements SourceComponent {
|
||||||
/**
|
/**
|
||||||
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
|
* 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);
|
Matcher matcher = ShaderField.PATTERN.matcher(source);
|
||||||
|
|
||||||
ImmutableMap.Builder<String, ShaderField> fields = ImmutableMap.builder();
|
ImmutableMap.Builder<String, ShaderField> fields = ImmutableMap.builder();
|
||||||
|
@ -280,36 +293,10 @@ public class SourceFile implements SourceComponent {
|
||||||
return fields.build();
|
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.
|
* 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;
|
int blockDepth = 0;
|
||||||
for (int i = start + 1; i < source.length(); i++) {
|
for (int i = start + 1; i < source.length(); i++) {
|
||||||
char ch = source.charAt(i);
|
char ch = source.charAt(i);
|
||||||
|
@ -324,20 +311,4 @@ public class SourceFile implements SourceComponent {
|
||||||
|
|
||||||
return -1;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -3,14 +3,16 @@ package com.jozufozu.flywheel.core.source;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.core.source.span.CharPos;
|
import com.jozufozu.flywheel.core.source.span.CharPos;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntLists;
|
||||||
|
|
||||||
public class SourceLines {
|
public class SourceLines implements CharSequence {
|
||||||
|
|
||||||
private static final Pattern newLine = Pattern.compile("(\\r\\n|\\r|\\n)");
|
private static final Pattern newLine = Pattern.compile("(\\r\\n|\\r|\\n)");
|
||||||
|
|
||||||
|
@ -23,22 +25,23 @@ public class SourceLines {
|
||||||
* 0-indexed line lookup
|
* 0-indexed line lookup
|
||||||
*/
|
*/
|
||||||
private final ImmutableList<String> lines;
|
private final ImmutableList<String> lines;
|
||||||
|
public final String raw;
|
||||||
|
|
||||||
public SourceLines(String source) {
|
public SourceLines(String raw) {
|
||||||
this.lineStarts = createLineLookup(source);
|
this.raw = raw;
|
||||||
this.lines = getLines(source, lineStarts);
|
this.lineStarts = createLineLookup(raw);
|
||||||
|
this.lines = getLines(raw, lineStarts);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLineCount() {
|
public int count() {
|
||||||
return lines.size();
|
return lines.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLine(int lineNo) {
|
public String lineString(int lineNo) {
|
||||||
return lines.get(lineNo);
|
return lines.get(lineNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLineStart(int lineNo) {
|
public int lineStartIndex(int lineNo) {
|
||||||
|
|
||||||
return lineStarts.getInt(lineNo);
|
return lineStarts.getInt(lineNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,9 +76,12 @@ public class SourceLines {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scan the source for line breaks, recording the position of the first character of each line.
|
* Scan the source for line breaks, recording the position of the first character of each line.
|
||||||
* @param source
|
|
||||||
*/
|
*/
|
||||||
private static IntList createLineLookup(String source) {
|
private static IntList createLineLookup(String source) {
|
||||||
|
if (source.isEmpty()) {
|
||||||
|
return IntLists.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
IntList l = new IntArrayList();
|
IntList l = new IntArrayList();
|
||||||
l.add(0); // first line is always at position 0
|
l.add(0); // first line is always at position 0
|
||||||
|
|
||||||
|
@ -84,6 +90,7 @@ public class SourceLines {
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
l.add(matcher.end());
|
l.add(matcher.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,9 +101,53 @@ public class SourceLines {
|
||||||
int start = lines.getInt(i - 1);
|
int start = lines.getInt(i - 1);
|
||||||
int end = lines.getInt(i);
|
int end = lines.getInt(i);
|
||||||
|
|
||||||
builder.add(StringUtil.trimEnd(source.substring(start, end)));
|
builder.add(source.substring(start, end)
|
||||||
|
.stripTrailing());
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.build();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int lineStartCol(int spanLine) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int lineWidth(int spanLine) {
|
||||||
|
return lines.get(spanLine)
|
||||||
|
.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int lineStartColTrimmed(final int line) {
|
||||||
|
final var lineString = lineString(line);
|
||||||
|
final int end = lineString.length();
|
||||||
|
|
||||||
|
int col = 0;
|
||||||
|
while (col < end && Character.isWhitespace(charAt(col))) {
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int lineStartPosTrimmed(final int line) {
|
||||||
|
return lineStartIndex(line) + lineStartColTrimmed(line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,9 @@ package com.jozufozu.flywheel.core.source.error;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.source.CompilationContext;
|
|
||||||
import com.jozufozu.flywheel.core.source.SourceFile;
|
import com.jozufozu.flywheel.core.source.SourceFile;
|
||||||
import com.jozufozu.flywheel.core.source.SourceLines;
|
import com.jozufozu.flywheel.core.source.SourceLines;
|
||||||
import com.jozufozu.flywheel.core.source.error.lines.ErrorLine;
|
import com.jozufozu.flywheel.core.source.error.lines.ErrorLine;
|
||||||
|
@ -17,72 +14,61 @@ import com.jozufozu.flywheel.core.source.error.lines.SourceLine;
|
||||||
import com.jozufozu.flywheel.core.source.error.lines.SpanHighlightLine;
|
import com.jozufozu.flywheel.core.source.error.lines.SpanHighlightLine;
|
||||||
import com.jozufozu.flywheel.core.source.error.lines.TextLine;
|
import com.jozufozu.flywheel.core.source.error.lines.TextLine;
|
||||||
import com.jozufozu.flywheel.core.source.span.Span;
|
import com.jozufozu.flywheel.core.source.span.Span;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.ConsoleColors;
|
||||||
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
|
||||||
public class ErrorBuilder {
|
public class ErrorBuilder {
|
||||||
|
|
||||||
private static final Pattern ERROR_LINE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)");
|
|
||||||
|
|
||||||
private final List<ErrorLine> lines = new ArrayList<>();
|
private final List<ErrorLine> lines = new ArrayList<>();
|
||||||
|
|
||||||
public ErrorBuilder() {
|
private ErrorBuilder() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ErrorBuilder error(CharSequence msg) {
|
public static ErrorBuilder create() {
|
||||||
return new ErrorBuilder()
|
return new ErrorBuilder();
|
||||||
.header(ErrorLevel.ERROR, msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ErrorBuilder compError(CharSequence msg) {
|
public ErrorBuilder error(String msg) {
|
||||||
return new ErrorBuilder()
|
return header(ErrorLevel.ERROR, msg);
|
||||||
.extra(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ErrorBuilder warn(CharSequence msg) {
|
public ErrorBuilder warn(String msg) {
|
||||||
return new ErrorBuilder()
|
return header(ErrorLevel.WARN, msg);
|
||||||
.header(ErrorLevel.WARN, msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public ErrorBuilder hint(String msg) {
|
||||||
public static ErrorBuilder fromLogLine(CompilationContext env, String s) {
|
return header(ErrorLevel.HINT, msg);
|
||||||
Matcher matcher = ERROR_LINE.matcher(s);
|
|
||||||
|
|
||||||
if (matcher.find()) {
|
|
||||||
String fileId = matcher.group(1);
|
|
||||||
String lineNo = matcher.group(2);
|
|
||||||
String msg = matcher.group(3);
|
|
||||||
Span span = env.getLineSpan(Integer.parseInt(fileId), Integer.parseInt(lineNo));
|
|
||||||
|
|
||||||
if (span == null) {
|
|
||||||
return ErrorBuilder.compError("Error in generated code");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrorBuilder.compError(msg)
|
public ErrorBuilder note(String msg) {
|
||||||
.pointAtFile(span.getSourceFile())
|
return header(ErrorLevel.NOTE, msg);
|
||||||
.pointAt(span, 1);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder header(ErrorLevel level, CharSequence msg) {
|
public ErrorBuilder header(ErrorLevel level, String msg) {
|
||||||
lines.add(new HeaderLine(level.toString(), msg));
|
lines.add(new HeaderLine(level.toString(), msg));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder extra(CharSequence msg) {
|
public ErrorBuilder extra(String msg) {
|
||||||
lines.add(new TextLine(msg.toString()));
|
lines.add(new TextLine(msg));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder pointAtFile(SourceFile file) {
|
public ErrorBuilder pointAtFile(SourceFile file) {
|
||||||
lines.add(new FileLine(file.name.toString()));
|
return pointAtFile(file.name.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorBuilder pointAtFile(String file) {
|
||||||
|
lines.add(new FileLine(file));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorBuilder hintIncludeFor(@Nullable Span span, String msg) {
|
||||||
|
if (span == null) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder hintIncludeFor(@Nullable Span span, CharSequence msg) {
|
|
||||||
if (span == null) return this;
|
|
||||||
SourceFile sourceFile = span.getSourceFile();
|
SourceFile sourceFile = span.getSourceFile();
|
||||||
|
|
||||||
String builder = "add " + sourceFile.importStatement() + ' ' + msg + "\n defined here:";
|
String builder = "add " + sourceFile.importStatement() + ' ' + msg + "\n defined here:";
|
||||||
|
@ -98,22 +84,32 @@ public class ErrorBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder pointAt(Span span, int ctxLines) {
|
public ErrorBuilder pointAt(Span span, int ctxLines) {
|
||||||
|
|
||||||
if (span.lines() == 1) {
|
if (span.lines() == 1) {
|
||||||
SourceLines lines = span.getSourceFile().lines;
|
SourceLines lines = span.getSourceFile().source;
|
||||||
|
|
||||||
int spanLine = span.firstLine();
|
int spanLine = span.firstLine();
|
||||||
|
|
||||||
int firstLine = Math.max(0, spanLine - ctxLines);
|
|
||||||
int lastLine = Math.min(lines.getLineCount(), spanLine + ctxLines);
|
|
||||||
|
|
||||||
int firstCol = span.getStart()
|
int firstCol = span.getStart()
|
||||||
.col();
|
.col();
|
||||||
int lastCol = span.getEnd()
|
int lastCol = span.getEnd()
|
||||||
.col();
|
.col();
|
||||||
|
|
||||||
|
pointAtLine(lines, spanLine, ctxLines, firstCol, lastCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorBuilder pointAtLine(SourceLines lines, int spanLine, int ctxLines) {
|
||||||
|
return pointAtLine(lines, spanLine, ctxLines, lines.lineStartColTrimmed(spanLine), lines.lineWidth(spanLine));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorBuilder pointAtLine(SourceLines lines, int spanLine, int ctxLines, int firstCol, int lastCol) {
|
||||||
|
int firstLine = Math.max(0, spanLine - ctxLines);
|
||||||
|
int lastLine = Math.min(lines.count(), spanLine + ctxLines);
|
||||||
|
|
||||||
|
|
||||||
for (int i = firstLine; i <= lastLine; i++) {
|
for (int i = firstLine; i <= lastLine; i++) {
|
||||||
CharSequence line = lines.getLine(i);
|
CharSequence line = lines.lineString(i);
|
||||||
|
|
||||||
this.lines.add(SourceLine.numbered(i + 1, line.toString()));
|
this.lines.add(SourceLine.numbered(i + 1, line.toString()));
|
||||||
|
|
||||||
|
@ -121,29 +117,30 @@ public class ErrorBuilder {
|
||||||
this.lines.add(new SpanHighlightLine(firstCol, lastCol));
|
this.lines.add(new SpanHighlightLine(firstCol, lastCol));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String build() {
|
public String build() {
|
||||||
|
int maxMargin = -1;
|
||||||
int maxLength = -1;
|
|
||||||
for (ErrorLine line : lines) {
|
for (ErrorLine line : lines) {
|
||||||
int length = line.neededMargin();
|
int neededMargin = line.neededMargin();
|
||||||
|
|
||||||
if (length > maxLength) {
|
if (neededMargin > maxMargin) {
|
||||||
maxLength = length;
|
maxMargin = neededMargin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.append('\n');
|
|
||||||
for (ErrorLine line : lines) {
|
for (ErrorLine line : lines) {
|
||||||
int length = line.neededMargin();
|
int neededMargin = line.neededMargin();
|
||||||
|
|
||||||
builder.append(FlwUtil.repeatChar(' ', maxLength - length))
|
if (neededMargin >= 0) {
|
||||||
.append(line.build())
|
builder.append(StringUtil.repeatChar(' ', maxMargin - neededMargin));
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(line.build())
|
||||||
|
.append(ConsoleColors.RESET)
|
||||||
.append('\n');
|
.append('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package com.jozufozu.flywheel.core.source.error;
|
package com.jozufozu.flywheel.core.source.error;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.util.ConsoleColors;
|
||||||
|
|
||||||
public enum ErrorLevel {
|
public enum ErrorLevel {
|
||||||
WARN("warn"),
|
WARN(ConsoleColors.YELLOW + "warn"),
|
||||||
ERROR("error"),
|
ERROR(ConsoleColors.RED + "error"),
|
||||||
HINT("hint"),
|
HINT(ConsoleColors.WHITE_BRIGHT + "hint"),
|
||||||
|
NOTE(ConsoleColors.WHITE_BRIGHT + "note"),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String error;
|
private final String error;
|
||||||
|
|
|
@ -2,17 +2,14 @@ package com.jozufozu.flywheel.core.source.error;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
|
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
|
||||||
import com.jozufozu.flywheel.core.source.SourceFile;
|
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.core.source.span.Span;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
|
||||||
public class ErrorReporter {
|
public class ErrorReporter {
|
||||||
|
|
||||||
|
@ -23,15 +20,15 @@ public class ErrorReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateMissingStruct(SourceFile file, Span vertexName, CharSequence msg, CharSequence hint) {
|
public void generateMissingStruct(SourceFile file, Span vertexName, CharSequence msg, CharSequence hint) {
|
||||||
Optional<Span> span = file.parent.index.getStructDefinitionsMatching(vertexName)
|
// Optional<Span> span = file.parent.index.getStructDefinitionsMatching(vertexName)
|
||||||
.stream()
|
// .stream()
|
||||||
.findFirst()
|
// .findFirst()
|
||||||
.map(ShaderStruct::getName);
|
// .map(ShaderStruct::getName);
|
||||||
|
//
|
||||||
this.error(msg)
|
// this.error(msg)
|
||||||
.pointAtFile(file)
|
// .pointAtFile(file)
|
||||||
.pointAt(vertexName, 1)
|
// .pointAt(vertexName, 1)
|
||||||
.hintIncludeFor(span.orElse(null), hint);
|
// .hintIncludeFor(span.orElse(null), hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg) {
|
public void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg) {
|
||||||
|
@ -39,14 +36,14 @@ public class ErrorReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg, CharSequence hint) {
|
public void generateMissingFunction(SourceFile file, CharSequence functionName, CharSequence msg, CharSequence hint) {
|
||||||
Optional<Span> span = file.parent.index.getFunctionDefinitionsMatching(functionName)
|
// Optional<Span> span = file.parent.index.getFunctionDefinitionsMatching(functionName)
|
||||||
.stream()
|
// .stream()
|
||||||
.findFirst()
|
// .findFirst()
|
||||||
.map(ShaderFunction::getName);
|
// .map(ShaderFunction::getName);
|
||||||
|
//
|
||||||
this.error(msg)
|
// this.error(msg)
|
||||||
.pointAtFile(file)
|
// .pointAtFile(file)
|
||||||
.hintIncludeFor(span.orElse(null), hint);
|
// .hintIncludeFor(span.orElse(null), hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder generateFunctionArgumentCountError(String name, int requiredArguments, Span span) {
|
public ErrorBuilder generateFunctionArgumentCountError(String name, int requiredArguments, Span span) {
|
||||||
|
@ -64,18 +61,17 @@ public class ErrorReporter {
|
||||||
public ErrorBuilder generateSpanError(Span span, String message) {
|
public ErrorBuilder generateSpanError(Span span, String message) {
|
||||||
SourceFile file = span.getSourceFile();
|
SourceFile file = span.getSourceFile();
|
||||||
|
|
||||||
return error(message)
|
return error(message).pointAtFile(file)
|
||||||
.pointAtFile(file)
|
|
||||||
.pointAt(span, 2);
|
.pointAt(span, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder generateFileError(SourceFile file, String message) {
|
public ErrorBuilder generateFileError(SourceFile file, String message) {
|
||||||
return error(message)
|
return error(message).pointAtFile(file);
|
||||||
.pointAtFile(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ErrorBuilder error(CharSequence msg) {
|
public ErrorBuilder error(String msg) {
|
||||||
var out = ErrorBuilder.error(msg);
|
var out = ErrorBuilder.create()
|
||||||
|
.error(msg);
|
||||||
reportedErrors.add(out);
|
reportedErrors.add(out);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -92,9 +88,7 @@ public class ErrorReporter {
|
||||||
return new ShaderLoadingException(allErrors);
|
return new ShaderLoadingException(allErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void printLines(CharSequence source) {
|
public static void printLines(String string) {
|
||||||
String string = source.toString();
|
|
||||||
|
|
||||||
List<String> lines = string.lines()
|
List<String> lines = string.lines()
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
@ -107,7 +101,7 @@ public class ErrorReporter {
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
|
|
||||||
builder.append(i)
|
builder.append(i)
|
||||||
.append(FlwUtil.repeatChar(' ', maxWidth - FlwUtil.numDigits(i)))
|
.append(StringUtil.repeatChar(' ', maxWidth - FlwUtil.numDigits(i)))
|
||||||
.append("| ")
|
.append("| ")
|
||||||
.append(lines.get(i))
|
.append(lines.get(i))
|
||||||
.append('\n');
|
.append('\n');
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.jozufozu.flywheel.core.source.error.lines;
|
||||||
|
|
||||||
public enum Divider {
|
public enum Divider {
|
||||||
BAR(" | "),
|
BAR(" | "),
|
||||||
ARROW("-->"),
|
ARROW("-> "),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String s;
|
private final String s;
|
||||||
|
|
|
@ -4,7 +4,7 @@ public record FileLine(String fileName) implements ErrorLine {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String left() {
|
public String left() {
|
||||||
return "--";
|
return "-";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,7 +4,7 @@ public record HeaderLine(String level, CharSequence message) implements ErrorLin
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int neededMargin() {
|
public int neededMargin() {
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.jozufozu.flywheel.core.source.generate;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.jozufozu.flywheel.util.Pair;
|
||||||
|
|
||||||
|
public record FnSignature(String returnType, String name, ImmutableList<Pair<String, String>> args) {
|
||||||
|
|
||||||
|
public static Builder create() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FnSignature of(String returnType, String name) {
|
||||||
|
return FnSignature.create()
|
||||||
|
.returnType(returnType)
|
||||||
|
.name(name)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FnSignature ofVoid(String name) {
|
||||||
|
return new FnSignature("void", name, ImmutableList.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<? extends GlslExpr> createArgExpressions() {
|
||||||
|
return args.stream()
|
||||||
|
.map(Pair::second)
|
||||||
|
.map(GlslExpr::variable)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVoid() {
|
||||||
|
return "void".equals(returnType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String fullDeclaration() {
|
||||||
|
return returnType + ' ' + name + '(' + args.stream()
|
||||||
|
.map(p -> p.first() + ' ' + p.second())
|
||||||
|
.collect(Collectors.joining(", ")) + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
public String signatureDeclaration() {
|
||||||
|
return returnType + ' ' + name + '(' + args.stream()
|
||||||
|
.map(Pair::first)
|
||||||
|
.collect(Collectors.joining(", ")) + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private String returnType;
|
||||||
|
private String name;
|
||||||
|
private final ImmutableList.Builder<Pair<String, String>> args = ImmutableList.builder();
|
||||||
|
|
||||||
|
public Builder returnType(String returnType) {
|
||||||
|
this.returnType = returnType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder name(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder arg(String type, String name) {
|
||||||
|
args.add(Pair.of(type, name));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FnSignature build() {
|
||||||
|
if (returnType == null) {
|
||||||
|
throw new IllegalStateException("returnType not set");
|
||||||
|
}
|
||||||
|
if (name == null) {
|
||||||
|
throw new IllegalStateException("name not set");
|
||||||
|
}
|
||||||
|
return new FnSignature(returnType, name, args.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.jozufozu.flywheel.core.source.generate;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class GlslBlock {
|
||||||
|
private final List<GlslStmt> body = new ArrayList<>();
|
||||||
|
|
||||||
|
public static GlslBlock create() {
|
||||||
|
return new GlslBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslBlock add(GlslStmt stmt) {
|
||||||
|
body.add(stmt);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslBlock eval(GlslExpr expr) {
|
||||||
|
return add(GlslStmt.eval(expr));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslBlock ret(GlslExpr call) {
|
||||||
|
add(GlslStmt.ret(call));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslBlock breakStmt() {
|
||||||
|
add(GlslStmt.BREAK);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String prettyPrint() {
|
||||||
|
return body.stream()
|
||||||
|
.map(GlslStmt::prettyPrint)
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,29 +4,34 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
|
||||||
|
|
||||||
public class GlslBuilder {
|
public class GlslBuilder {
|
||||||
|
private final List<Declaration> elements = new ArrayList<>();
|
||||||
private final List<SourceElement> elements = new ArrayList<>();
|
|
||||||
|
|
||||||
public void define(String name, String value) {
|
public void define(String name, String value) {
|
||||||
add(new Define(name, value));
|
add(new Define(name, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public StructBuilder struct() {
|
public void undef(String key) {
|
||||||
return add(new StructBuilder());
|
add(new Undef(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionBuilder function() {
|
public GlslStruct struct() {
|
||||||
return add(new FunctionBuilder());
|
return add(new GlslStruct());
|
||||||
}
|
}
|
||||||
|
|
||||||
public VertexInputBuilder vertexInput() {
|
public GlslFn function() {
|
||||||
return add(new VertexInputBuilder());
|
return add(new GlslFn());
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends SourceElement> T add(T element) {
|
public GlslVertexInput vertexInput() {
|
||||||
|
return add(new GlslVertexInput());
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslUniformBlock uniformBlock() {
|
||||||
|
return add(new GlslUniformBlock());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Declaration> T add(T element) {
|
||||||
elements.add(element);
|
elements.add(element);
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
@ -35,17 +40,21 @@ public class GlslBuilder {
|
||||||
elements.add(Separators.BLANK_LINE);
|
elements.add(Separators.BLANK_LINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void _addRaw(String sourceString) {
|
||||||
|
elements.add(() -> sourceString);
|
||||||
|
}
|
||||||
|
|
||||||
public String build() {
|
public String build() {
|
||||||
return elements.stream()
|
return elements.stream()
|
||||||
.map(SourceElement::build)
|
.map(Declaration::prettyPrint)
|
||||||
.collect(Collectors.joining("\n"));
|
.collect(Collectors.joining("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface SourceElement {
|
public interface Declaration {
|
||||||
String build();
|
String prettyPrint();
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Separators implements SourceElement {
|
public enum Separators implements Declaration {
|
||||||
BLANK_LINE(""),
|
BLANK_LINE(""),
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -54,125 +63,25 @@ public class GlslBuilder {
|
||||||
Separators(String separator) {
|
Separators(String separator) {
|
||||||
this.separator = separator;
|
this.separator = separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String build() {
|
public String prettyPrint() {
|
||||||
return separator;
|
return separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Define(String name, String value) implements SourceElement {
|
public record Define(String name, String value) implements Declaration {
|
||||||
@Override
|
@Override
|
||||||
public String build() {
|
public String prettyPrint() {
|
||||||
return "#define " + name + " " + value;
|
return "#define " + name + " " + value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class VertexInputBuilder implements SourceElement {
|
public record Undef(String name) implements Declaration {
|
||||||
|
|
||||||
private int binding;
|
|
||||||
private String type;
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
public VertexInputBuilder binding(int binding) {
|
|
||||||
this.binding = binding;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VertexInputBuilder type(String type) {
|
|
||||||
this.type = type;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VertexInputBuilder name(String name) {
|
|
||||||
this.name = name;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String build() {
|
public String prettyPrint() {
|
||||||
return "layout(location = " + binding + ") in " + type + " " + name + ";";
|
return "#undef " + name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class StructBuilder implements SourceElement {
|
|
||||||
|
|
||||||
private final List<Pair<String, String>> fields = new ArrayList<>();
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addField(String type, String name) {
|
|
||||||
fields.add(Pair.of(type, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildFields() {
|
|
||||||
return fields.stream()
|
|
||||||
.map(p -> '\t' + p.first() + ' ' + p.second() + ';')
|
|
||||||
.collect(Collectors.joining("\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String build() {
|
|
||||||
return """
|
|
||||||
struct %s {
|
|
||||||
%s
|
|
||||||
};
|
|
||||||
""".formatted(name, buildFields());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class FunctionBuilder implements SourceElement {
|
|
||||||
private final List<Pair<String, String>> arguments = new ArrayList<>();
|
|
||||||
private final List<String> body = new ArrayList<>();
|
|
||||||
private String returnType;
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
public FunctionBuilder returnType(String returnType) {
|
|
||||||
this.returnType = returnType;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionBuilder name(String name) {
|
|
||||||
this.name = name;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionBuilder argument(String type, String name) {
|
|
||||||
arguments.add(Pair.of(type, name));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionBuilder argumentIn(String type, String name) {
|
|
||||||
arguments.add(Pair.of("in " + type, name));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionBuilder statement(String statement) {
|
|
||||||
this.body.add(statement);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String build() {
|
|
||||||
return """
|
|
||||||
%s %s(%s) {
|
|
||||||
%s
|
|
||||||
}
|
|
||||||
""".formatted(returnType, name, buildArguments(), buildBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildBody() {
|
|
||||||
return body.stream()
|
|
||||||
.map(s -> '\t' + s)
|
|
||||||
.collect(Collectors.joining("\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildArguments() {
|
|
||||||
return arguments.stream()
|
|
||||||
.map(p -> p.first() + ' ' + p.second())
|
|
||||||
.collect(Collectors.joining(", "));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package com.jozufozu.flywheel.core.source.generate;
|
package com.jozufozu.flywheel.core.source.generate;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public sealed interface GlslExpr {
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
public interface GlslExpr {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a glsl variable with the given name.
|
* Create a glsl variable with the given name.
|
||||||
|
@ -14,7 +18,21 @@ public sealed interface GlslExpr {
|
||||||
return new Variable(name);
|
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 IntLiteral(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GlslExpr literal(boolean expr) {
|
||||||
|
return new BoolLiteral(expr);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call a one-parameter function with the given name on this expression.
|
* Call a one-parameter function with the given name on this expression.
|
||||||
|
@ -56,31 +74,66 @@ public sealed interface GlslExpr {
|
||||||
return f.apply(this);
|
return f.apply(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String prettyPrint();
|
||||||
|
|
||||||
record Variable(String name) implements GlslExpr {
|
record Variable(String name) implements GlslExpr {
|
||||||
@Override
|
@Override
|
||||||
public String minPrint() {
|
public String prettyPrint() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
record FunctionCall(String name, GlslExpr target) implements GlslExpr {
|
record FunctionCall(String name, Collection<? extends GlslExpr> args) implements GlslExpr {
|
||||||
@Override
|
public FunctionCall(String name, GlslExpr target) {
|
||||||
public String minPrint() {
|
this(name, ImmutableList.of(target));
|
||||||
return name + "(" + target.minPrint() + ")";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 {
|
record Swizzle(GlslExpr target, String selection) implements GlslExpr {
|
||||||
@Override
|
@Override
|
||||||
public String minPrint() {
|
public String prettyPrint() {
|
||||||
return target.minPrint() + "." + selection;
|
return target.prettyPrint() + "." + selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
record Access(GlslExpr target, String argName) implements GlslExpr {
|
record Access(GlslExpr target, String argName) implements GlslExpr {
|
||||||
@Override
|
@Override
|
||||||
public String minPrint() {
|
public String prettyPrint() {
|
||||||
return target.minPrint() + "." + argName;
|
return target.prettyPrint() + "." + argName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
record IntLiteral(int value) implements GlslExpr {
|
||||||
|
@Override
|
||||||
|
public String prettyPrint() {
|
||||||
|
return Integer.toString(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record BoolLiteral(boolean value) implements GlslExpr {
|
||||||
|
@Override
|
||||||
|
public String prettyPrint() {
|
||||||
|
return Boolean.toString(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.jozufozu.flywheel.core.source.generate;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
|
||||||
|
public class GlslFn implements GlslBuilder.Declaration {
|
||||||
|
private final GlslBlock body = new GlslBlock();
|
||||||
|
private FnSignature signature;
|
||||||
|
|
||||||
|
public GlslFn signature(FnSignature signature) {
|
||||||
|
this.signature = signature;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslFn body(Consumer<GlslBlock> f) {
|
||||||
|
f.accept(body);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String prettyPrint() {
|
||||||
|
return """
|
||||||
|
%s {
|
||||||
|
%s
|
||||||
|
}
|
||||||
|
""".formatted(signature.fullDeclaration(), StringUtil.indent(body.prettyPrint(), 4));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.jozufozu.flywheel.core.source.generate;
|
||||||
|
|
||||||
|
public interface GlslStmt {
|
||||||
|
GlslStmt BREAK = () -> "break;";
|
||||||
|
GlslStmt CONTINUE = () -> "continue;";
|
||||||
|
GlslStmt RETURN = () -> "return;";
|
||||||
|
|
||||||
|
static GlslStmt eval(GlslExpr expr) {
|
||||||
|
return new Eval(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GlslStmt ret(GlslExpr value) {
|
||||||
|
return new Return(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
String prettyPrint();
|
||||||
|
|
||||||
|
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() + ";";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.jozufozu.flywheel.core.source.generate;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.util.Pair;
|
||||||
|
|
||||||
|
public class GlslStruct implements GlslBuilder.Declaration {
|
||||||
|
|
||||||
|
private final List<Pair<String, String>> fields = new ArrayList<>();
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addField(String type, String name) {
|
||||||
|
fields.add(Pair.of(type, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildFields() {
|
||||||
|
return fields.stream()
|
||||||
|
.map(p -> p.first() + ' ' + p.second() + ';')
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String prettyPrint() {
|
||||||
|
return """
|
||||||
|
struct %s {
|
||||||
|
%s
|
||||||
|
};
|
||||||
|
""".formatted(name, buildFields().indent(4));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.jozufozu.flywheel.core.source.generate;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.util.Pair;
|
||||||
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
|
||||||
|
public class GlslSwitch implements GlslStmt {
|
||||||
|
|
||||||
|
private final GlslExpr on;
|
||||||
|
|
||||||
|
private final List<Pair<GlslExpr, GlslBlock>> cases = new ArrayList<>();
|
||||||
|
private GlslBlock defaultCase = null;
|
||||||
|
|
||||||
|
private GlslSwitch(GlslExpr on) {
|
||||||
|
this.on = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GlslSwitch on(GlslExpr on) {
|
||||||
|
return new GlslSwitch(on);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void intCase(int expr, GlslBlock block) {
|
||||||
|
cases.add(Pair.of(GlslExpr.literal(expr), block));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void defaultCase(GlslBlock block) {
|
||||||
|
defaultCase = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String prettyPrint() {
|
||||||
|
return """
|
||||||
|
switch (%s) {
|
||||||
|
%s
|
||||||
|
}""".formatted(on.prettyPrint(), formatCases());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String formatCases() {
|
||||||
|
var cases = this.cases.stream()
|
||||||
|
.map(GlslSwitch::prettyPrintCase)
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
if (defaultCase != null) {
|
||||||
|
cases += "\ndefault:\n" + StringUtil.indent(defaultCase.prettyPrint(), 4);
|
||||||
|
}
|
||||||
|
return cases;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String prettyPrintCase(Pair<GlslExpr, GlslBlock> p) {
|
||||||
|
var variant = p.first()
|
||||||
|
.prettyPrint();
|
||||||
|
var block = p.second()
|
||||||
|
.prettyPrint();
|
||||||
|
return """
|
||||||
|
case %s:
|
||||||
|
%s""".formatted(variant, StringUtil.indent(block, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.jozufozu.flywheel.core.source.generate;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.util.Pair;
|
||||||
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
|
||||||
|
public class GlslUniformBlock implements GlslBuilder.Declaration {
|
||||||
|
|
||||||
|
|
||||||
|
private String qualifier;
|
||||||
|
private int binding;
|
||||||
|
private String name;
|
||||||
|
private final List<Pair<String, String>> members = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String prettyPrint() {
|
||||||
|
return """
|
||||||
|
layout(%s, binding = %d) uniform %s {
|
||||||
|
%s
|
||||||
|
};
|
||||||
|
""".formatted(qualifier, binding, name, StringUtil.indent(formatMembers(), 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatMembers() {
|
||||||
|
return members.stream()
|
||||||
|
.map(p -> p.first() + " " + p.second() + ";")
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslUniformBlock layout(String qualifier) {
|
||||||
|
this.qualifier = qualifier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslUniformBlock binding(int i) {
|
||||||
|
binding = i;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslUniformBlock name(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslUniformBlock member(String typeName, String variableName) {
|
||||||
|
members.add(Pair.of(typeName, variableName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.jozufozu.flywheel.core.source.generate;
|
||||||
|
|
||||||
|
public class GlslVertexInput implements GlslBuilder.Declaration {
|
||||||
|
|
||||||
|
private int binding;
|
||||||
|
private String type;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public GlslVertexInput binding(int binding) {
|
||||||
|
this.binding = binding;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslVertexInput type(String type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlslVertexInput name(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String prettyPrint() {
|
||||||
|
return "layout(location = " + binding + ") in " + type + " " + name + ";";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||||
package com.jozufozu.flywheel.core.compile;
|
package com.jozufozu.flywheel.core.source.generate;
|
||||||
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,52 +1,19 @@
|
||||||
package com.jozufozu.flywheel.core.source.parse;
|
package com.jozufozu.flywheel.core.source.parse;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.regex.Pattern;
|
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 com.jozufozu.flywheel.core.source.span.Span;
|
||||||
|
|
||||||
import net.minecraft.ResourceLocationException;
|
public class Import {
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
public class Import extends AbstractShaderElement {
|
|
||||||
|
|
||||||
public static final Pattern PATTERN = Pattern.compile("^\\s*#\\s*use\\s+\"(.*)\"", Pattern.MULTILINE);
|
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) {
|
public Import(Span self, Span file) {
|
||||||
super(self);
|
this.self = self;
|
||||||
this.resolution = resolution.addSpan(file);
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,17 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.source.span.Span;
|
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 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 Span location;
|
||||||
public final @Nullable Decoration decoration;
|
public final @Nullable Decoration decoration;
|
||||||
public final Span type;
|
public final Span type;
|
||||||
public final Span name;
|
public final Span name;
|
||||||
|
public final Span self;
|
||||||
|
|
||||||
public ShaderField(Span self, Span location, Span inOut, Span type, Span name) {
|
public ShaderField(Span self, Span location, Span inOut, Span type, Span name) {
|
||||||
super(self);
|
this.self = self;
|
||||||
|
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.decoration = Decoration.fromSpan(inOut);
|
this.decoration = Decoration.fromSpan(inOut);
|
||||||
|
|
|
@ -7,13 +7,14 @@ import java.util.stream.Collectors;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.core.source.span.Span;
|
import com.jozufozu.flywheel.core.source.span.Span;
|
||||||
|
|
||||||
public class ShaderFunction extends AbstractShaderElement {
|
public class ShaderFunction {
|
||||||
|
|
||||||
// https://regexr.com/60n3d
|
// https://regexr.com/60n3d
|
||||||
public static final Pattern PATTERN = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{");
|
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 argument = Pattern.compile("(?:(inout|in|out) )?(\\w+)\\s+(\\w+)");
|
||||||
public static final Pattern assignment = Pattern.compile("(\\w+)\\s*=");
|
public static final Pattern assignment = Pattern.compile("(\\w+)\\s*=");
|
||||||
|
public final Span self;
|
||||||
|
|
||||||
private final Span type;
|
private final Span type;
|
||||||
private final Span name;
|
private final Span name;
|
||||||
|
@ -23,7 +24,7 @@ public class ShaderFunction extends AbstractShaderElement {
|
||||||
private final ImmutableList<ShaderVariable> parameters;
|
private final ImmutableList<ShaderVariable> parameters;
|
||||||
|
|
||||||
public ShaderFunction(Span self, Span type, Span name, Span args, Span body) {
|
public ShaderFunction(Span self, Span type, Span name, Span args, Span body) {
|
||||||
super(self);
|
this.self = self;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
|
|
|
@ -7,21 +7,24 @@ import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.jozufozu.flywheel.core.source.span.Span;
|
import com.jozufozu.flywheel.core.source.span.Span;
|
||||||
|
|
||||||
public class ShaderStruct extends AbstractShaderElement {
|
public class ShaderStruct {
|
||||||
|
|
||||||
// https://regexr.com/61rpe
|
// https://regexr.com/61rpe
|
||||||
public static final Pattern PATTERN = Pattern.compile("struct\\s+([\\w\\d]*)\\s*\\{([\\w\\d\\s,;]*)}\\s*;\\s");
|
public static final Pattern PATTERN = Pattern.compile("struct\\s+([\\w_]*)\\s*\\{(.*?)}\\s*([\\w_]*)?\\s*;\\s", Pattern.DOTALL);
|
||||||
|
|
||||||
public final Span name;
|
public final Span name;
|
||||||
public final Span body;
|
public final Span body;
|
||||||
|
public final Span self;
|
||||||
|
public final Span variableName;
|
||||||
|
|
||||||
private final ImmutableList<StructField> fields;
|
private final ImmutableList<StructField> fields;
|
||||||
private final ImmutableMap<String, Span> fields2Types;
|
private final ImmutableMap<String, Span> fields2Types;
|
||||||
|
|
||||||
public ShaderStruct(Span self, Span name, Span body) {
|
public ShaderStruct(Span self, Span name, Span body, Span variableName) {
|
||||||
super(self);
|
this.self = self;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
|
this.variableName = variableName;
|
||||||
this.fields = parseFields();
|
this.fields = parseFields();
|
||||||
this.fields2Types = createTypeLookup();
|
this.fields2Types = createTypeLookup();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,16 @@ package com.jozufozu.flywheel.core.source.parse;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.source.span.Span;
|
import com.jozufozu.flywheel.core.source.span.Span;
|
||||||
|
|
||||||
public class ShaderVariable extends AbstractShaderElement {
|
public class ShaderVariable {
|
||||||
|
|
||||||
public final Span qualifierSpan;
|
public final Span qualifierSpan;
|
||||||
public final Span type;
|
public final Span type;
|
||||||
public final Span name;
|
public final Span name;
|
||||||
public final Qualifier qualifier;
|
public final Qualifier qualifier;
|
||||||
|
public final Span self;
|
||||||
|
|
||||||
public ShaderVariable(Span self, Span qualifier, Span type, Span name) {
|
public ShaderVariable(Span self, Span qualifier, Span type, Span name) {
|
||||||
super(self);
|
this.self = self;
|
||||||
this.qualifierSpan = qualifier;
|
this.qualifierSpan = qualifier;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
|
@ -4,14 +4,15 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.source.span.Span;
|
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 static final Pattern fieldPattern = Pattern.compile("(\\S+)\\s*(\\S+);");
|
||||||
|
public final Span self;
|
||||||
|
|
||||||
public Span type;
|
public Span type;
|
||||||
public Span name;
|
public Span name;
|
||||||
|
|
||||||
public StructField(Span self, Span type, Span name) {
|
public StructField(Span self, Span type, Span name) {
|
||||||
super(self);
|
this.self = self;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ public abstract class Span implements CharSequence, Comparable<Span> {
|
||||||
protected final CharPos end;
|
protected final CharPos end;
|
||||||
|
|
||||||
public Span(SourceFile in, int start, int 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) {
|
public Span(SourceFile in, CharPos start, CharPos end) {
|
||||||
|
@ -131,7 +131,7 @@ public abstract class Span implements CharSequence, Comparable<Span> {
|
||||||
if (isErr()) {
|
if (isErr()) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
return in.findStruct(this.toString());
|
return in.findStructByName(this.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<ShaderFunction> findFunction() {
|
public Optional<ShaderFunction> findFunction() {
|
||||||
|
|
|
@ -19,8 +19,7 @@ public class StringSpan extends Span {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String get() {
|
public String get() {
|
||||||
return in.source
|
return in.source.raw.substring(start.pos(), end.pos());
|
||||||
.substring(start.pos(), end.pos());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -5,7 +5,6 @@ import com.jozufozu.flywheel.api.struct.StructWriter;
|
||||||
import com.jozufozu.flywheel.core.Components;
|
import com.jozufozu.flywheel.core.Components;
|
||||||
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
||||||
import com.jozufozu.flywheel.core.layout.CommonItems;
|
import com.jozufozu.flywheel.core.layout.CommonItems;
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
|
||||||
import com.jozufozu.flywheel.util.RenderMath;
|
import com.jozufozu.flywheel.util.RenderMath;
|
||||||
import com.mojang.math.Matrix3f;
|
import com.mojang.math.Matrix3f;
|
||||||
import com.mojang.math.Matrix4f;
|
import com.mojang.math.Matrix4f;
|
||||||
|
@ -13,6 +12,8 @@ import com.mojang.math.Quaternion;
|
||||||
import com.mojang.math.Vector3f;
|
import com.mojang.math.Vector3f;
|
||||||
import com.mojang.math.Vector4f;
|
import com.mojang.math.Vector4f;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public class OrientedType implements StructType<OrientedPart> {
|
public class OrientedType implements StructType<OrientedPart> {
|
||||||
|
|
||||||
public static final BufferLayout FORMAT = BufferLayout.builder()
|
public static final BufferLayout FORMAT = BufferLayout.builder()
|
||||||
|
@ -39,7 +40,7 @@ public class OrientedType implements StructType<OrientedPart> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileResolution getInstanceShader() {
|
public ResourceLocation instanceShader() {
|
||||||
return Components.Files.ORIENTED;
|
return Components.Files.ORIENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,12 @@ import com.jozufozu.flywheel.api.struct.StructWriter;
|
||||||
import com.jozufozu.flywheel.core.Components;
|
import com.jozufozu.flywheel.core.Components;
|
||||||
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
||||||
import com.jozufozu.flywheel.core.layout.CommonItems;
|
import com.jozufozu.flywheel.core.layout.CommonItems;
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
|
||||||
import com.jozufozu.flywheel.util.RenderMath;
|
import com.jozufozu.flywheel.util.RenderMath;
|
||||||
import com.mojang.math.Vector3f;
|
import com.mojang.math.Vector3f;
|
||||||
import com.mojang.math.Vector4f;
|
import com.mojang.math.Vector4f;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public class TransformedType implements StructType<TransformedPart> {
|
public class TransformedType implements StructType<TransformedPart> {
|
||||||
|
|
||||||
public static final BufferLayout FORMAT = BufferLayout.builder()
|
public static final BufferLayout FORMAT = BufferLayout.builder()
|
||||||
|
@ -35,7 +36,7 @@ public class TransformedType implements StructType<TransformedPart> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileResolution getInstanceShader() {
|
public ResourceLocation instanceShader() {
|
||||||
return Components.Files.TRANSFORMED;
|
return Components.Files.TRANSFORMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
package com.jozufozu.flywheel.core.uniform;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
|
||||||
|
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||||
|
import com.jozufozu.flywheel.core.Components;
|
||||||
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
|
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
||||||
|
import com.jozufozu.flywheel.util.MatrixUtil;
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
|
import net.minecraft.core.Vec3i;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
|
||||||
|
public class FlwShaderUniforms implements ShaderUniforms {
|
||||||
|
|
||||||
|
public static final int SIZE = 224;
|
||||||
|
|
||||||
|
public static boolean FRUSTUM_PAUSED = false;
|
||||||
|
public static boolean FRUSTUM_CAPTURE = false;
|
||||||
|
public static boolean FOG_UPDATE = true;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int byteSize() {
|
||||||
|
return SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation uniformShader() {
|
||||||
|
return Components.Files.UNIFORMS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Provider activate(long ptr) {
|
||||||
|
return new Active(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Active implements Provider, Consumer<BeginFrameEvent> {
|
||||||
|
private final long ptr;
|
||||||
|
|
||||||
|
private boolean dirty;
|
||||||
|
|
||||||
|
public Active(long ptr) {
|
||||||
|
this.ptr = ptr;
|
||||||
|
MinecraftForge.EVENT_BUS.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete() {
|
||||||
|
MinecraftForge.EVENT_BUS.unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean poll() {
|
||||||
|
boolean updated = maybeUpdateFog();
|
||||||
|
updated |= dirty;
|
||||||
|
dirty = false;
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(BeginFrameEvent event) {
|
||||||
|
if (ptr == MemoryUtil.NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RenderContext context = event.getContext();
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
// don't want to mutate viewProjection
|
||||||
|
var vp = context.viewProjection()
|
||||||
|
.copy();
|
||||||
|
vp.multiplyWithTranslation(-camX, -camY, -camZ);
|
||||||
|
|
||||||
|
MatrixUtil.writeUnsafe(vp, ptr + 32);
|
||||||
|
MemoryUtil.memPutFloat(ptr + 96, camX);
|
||||||
|
MemoryUtil.memPutFloat(ptr + 100, camY);
|
||||||
|
MemoryUtil.memPutFloat(ptr + 104, camZ);
|
||||||
|
MemoryUtil.memPutFloat(ptr + 108, 0f); // vec4 alignment
|
||||||
|
MemoryUtil.memPutInt(ptr + 112, getConstantAmbientLightFlag(context));
|
||||||
|
|
||||||
|
updateFrustum(context, camX, camY, camZ);
|
||||||
|
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getConstantAmbientLightFlag(RenderContext context) {
|
||||||
|
var constantAmbientLight = context.level()
|
||||||
|
.effects()
|
||||||
|
.constantAmbientLight();
|
||||||
|
return constantAmbientLight ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean maybeUpdateFog() {
|
||||||
|
if (!FOG_UPDATE || ptr == MemoryUtil.NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
FOG_UPDATE = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFrustum(RenderContext context, float camX, float camY, float camZ) {
|
||||||
|
if (FRUSTUM_PAUSED && !FRUSTUM_CAPTURE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var shiftedCuller = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ);
|
||||||
|
|
||||||
|
shiftedCuller.getJozuPackedPlanes(ptr + 128);
|
||||||
|
|
||||||
|
FRUSTUM_CAPTURE = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,39 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.uniform;
|
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.uniform.UniformProvider;
|
|
||||||
import com.jozufozu.flywheel.core.Components;
|
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
|
|
||||||
public class FogProvider extends UniformProvider {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getActualByteSize() {
|
|
||||||
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 getUniformShader() {
|
|
||||||
return Components.Files.FOG_UNIFORMS;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.uniform;
|
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.uniform.UniformProvider;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
|
||||||
import com.jozufozu.flywheel.core.Components;
|
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
|
||||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
|
||||||
|
|
||||||
import net.minecraft.core.Vec3i;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
|
||||||
|
|
||||||
public class FrustumProvider extends UniformProvider {
|
|
||||||
|
|
||||||
public static boolean PAUSED = false;
|
|
||||||
public static boolean CAPTURE = false;
|
|
||||||
|
|
||||||
public FrustumProvider() {
|
|
||||||
MinecraftForge.EVENT_BUS.addListener(this::beginFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getActualByteSize() {
|
|
||||||
return 96;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FileResolution getUniformShader() {
|
|
||||||
return Components.Files.FRUSTUM_UNIFORMS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void beginFrame(BeginFrameEvent event) {
|
|
||||||
update(event.getContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +1,17 @@
|
||||||
package com.jozufozu.flywheel.core.uniform;
|
package com.jozufozu.flywheel.core.uniform;
|
||||||
|
|
||||||
import java.util.BitSet;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL32;
|
import org.lwjgl.opengl.GL32;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.api.uniform.UniformProvider;
|
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
||||||
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
|
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
|
||||||
import com.jozufozu.flywheel.core.ComponentRegistry;
|
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||||
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
import com.jozufozu.flywheel.util.RenderMath;
|
import com.jozufozu.flywheel.util.RenderMath;
|
||||||
|
|
||||||
public class UniformBuffer {
|
public class UniformBuffer {
|
||||||
|
@ -22,7 +22,7 @@ public class UniformBuffer {
|
||||||
private static final boolean PO2_ALIGNMENT = RenderMath.isPowerOf2(OFFSET_ALIGNMENT);
|
private static final boolean PO2_ALIGNMENT = RenderMath.isPowerOf2(OFFSET_ALIGNMENT);
|
||||||
|
|
||||||
private static UniformBuffer instance;
|
private static UniformBuffer instance;
|
||||||
private final List<Allocated> allocatedProviders;
|
private final ProviderSet providerSet;
|
||||||
|
|
||||||
public static UniformBuffer getInstance() {
|
public static UniformBuffer getInstance() {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
|
@ -32,51 +32,18 @@ public class UniformBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final GlBuffer buffer;
|
private final GlBuffer buffer;
|
||||||
private final MemoryBlock data;
|
|
||||||
|
|
||||||
private final BitSet changedBytes;
|
|
||||||
|
|
||||||
private UniformBuffer() {
|
private UniformBuffer() {
|
||||||
buffer = new GlBuffer(GlBufferType.UNIFORM_BUFFER);
|
buffer = new GlBuffer(GlBufferType.UNIFORM_BUFFER);
|
||||||
|
providerSet = new ProviderSet(ComponentRegistry.getAllUniformProviders());
|
||||||
Collection<UniformProvider> providers = ComponentRegistry.getAllUniformProviders();
|
|
||||||
|
|
||||||
var builder = ImmutableList.<Allocated>builder();
|
|
||||||
int totalBytes = 0;
|
|
||||||
int index = 0;
|
|
||||||
for (UniformProvider provider : providers) {
|
|
||||||
int size = alignPo2(provider.getActualByteSize(), 16);
|
|
||||||
|
|
||||||
builder.add(new Allocated(provider, totalBytes, size, index));
|
|
||||||
|
|
||||||
totalBytes = alignUniformBuffer(totalBytes + size);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocatedProviders = builder.build();
|
|
||||||
|
|
||||||
data = MemoryBlock.mallocTracked(totalBytes);
|
|
||||||
changedBytes = new BitSet(totalBytes);
|
|
||||||
|
|
||||||
for (Allocated p : allocatedProviders) {
|
|
||||||
p.updatePtr(data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sync() {
|
public void sync() {
|
||||||
if (changedBytes.isEmpty()) {
|
if (providerSet.pollUpdates()) {
|
||||||
return;
|
buffer.upload(providerSet.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: upload only changed bytes
|
GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, 0, buffer.handle(), 0, providerSet.data.size());
|
||||||
changedBytes.clear();
|
|
||||||
|
|
||||||
buffer.upload(data);
|
|
||||||
|
|
||||||
int handle = buffer.handle();
|
|
||||||
for (Allocated p : allocatedProviders) {
|
|
||||||
GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, p.index, handle, p.offset, p.size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/3407012/rounding-up-to-the-nearest-multiple-of-a-number
|
// https://stackoverflow.com/questions/3407012/rounding-up-to-the-nearest-multiple-of-a-number
|
||||||
|
@ -88,46 +55,61 @@ public class UniformBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int alignPo2(int numToRound, int alignment) {
|
private static class LiveProvider {
|
||||||
return (numToRound + alignment - 1) & -alignment;
|
private final ShaderUniforms provider;
|
||||||
}
|
|
||||||
|
|
||||||
private class Allocated implements UniformProvider.Notifier {
|
|
||||||
private final UniformProvider provider;
|
|
||||||
private final int offset;
|
private final int offset;
|
||||||
private final int size;
|
private final int size;
|
||||||
private final int index;
|
private ShaderUniforms.Provider activeProvider;
|
||||||
|
|
||||||
private Allocated(UniformProvider provider, int offset, int size, int index) {
|
private LiveProvider(ShaderUniforms provider, int offset, int size) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void signalChanged() {
|
|
||||||
changedBytes.set(offset, offset + size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePtr(MemoryBlock bufferBase) {
|
private void updatePtr(MemoryBlock bufferBase) {
|
||||||
provider.updatePtr(bufferBase.ptr() + offset, this);
|
if (activeProvider != null) {
|
||||||
|
activeProvider.delete();
|
||||||
|
}
|
||||||
|
activeProvider = provider.activate(bufferBase.ptr() + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UniformProvider provider() {
|
public boolean maybePoll() {
|
||||||
return provider;
|
return activeProvider != null && activeProvider.poll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int offset() {
|
private static class ProviderSet {
|
||||||
return offset;
|
private final List<LiveProvider> allocatedProviders;
|
||||||
|
|
||||||
|
private final MemoryBlock data;
|
||||||
|
|
||||||
|
private ProviderSet(final Collection<ShaderUniforms> providers) {
|
||||||
|
var builder = ImmutableList.<LiveProvider>builder();
|
||||||
|
int totalBytes = 0;
|
||||||
|
for (ShaderUniforms provider : providers) {
|
||||||
|
int size = FlwUtil.align16(provider.byteSize());
|
||||||
|
|
||||||
|
builder.add(new LiveProvider(provider, totalBytes, size));
|
||||||
|
|
||||||
|
totalBytes += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
allocatedProviders = builder.build();
|
||||||
return size;
|
|
||||||
|
data = MemoryBlock.mallocTracked(totalBytes);
|
||||||
|
|
||||||
|
for (LiveProvider p : allocatedProviders) {
|
||||||
|
p.updatePtr(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int index() {
|
public boolean pollUpdates() {
|
||||||
return index;
|
boolean changed = false;
|
||||||
|
for (LiveProvider p : allocatedProviders) {
|
||||||
|
changed |= p.maybePoll();
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.uniform;
|
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.uniform.UniformProvider;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
|
||||||
import com.jozufozu.flywheel.core.Components;
|
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
|
||||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
|
||||||
import com.jozufozu.flywheel.util.MatrixUtil;
|
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
import net.minecraft.core.Vec3i;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
|
||||||
|
|
||||||
public class ViewProvider extends UniformProvider {
|
|
||||||
|
|
||||||
public ViewProvider() {
|
|
||||||
MinecraftForge.EVENT_BUS.addListener(this::beginFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void beginFrame(BeginFrameEvent event) {
|
|
||||||
update(event.getContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
|
|
||||||
MatrixUtil.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() {
|
|
||||||
return Components.Files.VIEW_UNIFORMS;
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue