Inderinstanced

This commit is contained in:
Jozufozu 2022-08-29 21:21:52 -07:00
parent 61c2c76b47
commit bf03f084c3
42 changed files with 895 additions and 302 deletions

View file

@ -1,8 +1,26 @@
package com.jozufozu.flywheel.api.pipeline;
import java.util.function.Function;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.FileResolution;
public record PipelineShader(GLSLVersion glslVersion, FileResolution vertex, FileResolution fragment) {
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(StructType<?> structType) {
return factory.apply(structType);
}
public interface InstanceAssemblerFactory extends Function<StructType<?>, SourceComponent> {
}
}

View file

@ -31,8 +31,6 @@ public interface StructType<S extends InstancedPart> {
StorageBufferWriter<S> getStorageBufferWriter();
FileResolution getIndirectShader();
interface VertexTransformer<S extends InstancedPart> {
void transform(MutableVertexList vertexList, S struct, ClientLevel level);
}

View file

@ -88,6 +88,9 @@ public class Backend {
private static BackendType chooseEngine() {
var preferred = FlwConfig.get()
.getBackendType();
if (preferred == null) {
return BackendTypes.defaultForCurrentPC();
}
var actual = preferred.findFallback();
if (preferred != actual) {

View file

@ -1,14 +1,20 @@
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.core.ComponentRegistry;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
import com.jozufozu.flywheel.backend.instancing.indirect.ComputeCullerCompiler;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.compile.ShaderCompilationException;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
import com.jozufozu.flywheel.core.source.ShaderSources;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.util.StringUtil;
@ -63,21 +69,45 @@ public class Loader implements ResourceManagerReloadListener {
Backend.LOGGER.info("All shaders passed checks.");
long compileStart = System.nanoTime();
int programCounter = 0;
boolean crash = false;
for (Material material : ComponentRegistry.materials) {
for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) {
for (ContextShader contextShader : ComponentRegistry.contextShaders) {
var ctx = new PipelineCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader, Components.INSTANCED_ARRAYS);
PipelineCompiler.INSTANCE.getProgram(ctx);
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 all programs in " + StringUtil.formatTime(compileEnd - compileStart));
Backend.LOGGER.info("Compiled " + programCounter + " programs in " + StringUtil.formatTime(compileEnd - compileStart));
if (crash) {
throw new ShaderLoadingException("Compilation failed");
}
ClientLevel world = Minecraft.getInstance().level;
if (Backend.canUseInstancing(world)) {

View file

@ -82,7 +82,7 @@ public class GlVertexArray extends GlObject {
int i = startAttrib;
final int stride = type.getStride();
for (var attribute : type.getAttributes()) {
for (var attribute : type.attributes()) {
targets[i] = targetBuffer;
attributes[i] = attribute;
offsets[i] = offset;

View file

@ -1,23 +1,24 @@
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.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import net.minecraft.resources.ResourceLocation;
@ -68,8 +69,8 @@ public class PipelineCompiler extends Memoizer<PipelineCompiler.Context, GlProgr
var vertex = new ShaderCompiler.Context(glslVersion, ShaderType.VERTEX, ctx.getVertexComponents());
var fragment = new ShaderCompiler.Context(glslVersion, ShaderType.FRAGMENT, ctx.getFragmentComponents());
return new ProgramAssembler(ctx.instanceShader.getFileLoc())
.attachShader(shaderCompiler.get(vertex))
return new ProgramAssembler(ctx.structType.getInstanceShader()
.getFileLoc()).attachShader(shaderCompiler.get(vertex))
.attachShader(shaderCompiler.get(fragment))
.link()
.build(ctx.contextShader.factory());
@ -87,22 +88,35 @@ public class PipelineCompiler extends Memoizer<PipelineCompiler.Context, GlProgr
/**
* 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 instanceShader The instance shader to use.
* @param contextShader The context shader to use.
* @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, FileResolution instanceShader,
public record Context(VertexType vertexType, Material material, StructType<?> structType,
ContextShader contextShader, PipelineShader pipelineShader) {
ImmutableList<SourceFile> getVertexComponents() {
return ImmutableList.of(vertexType.getLayoutShader().getFile(), instanceShader.getFile(), material.getVertexShader().getFile(),
contextShader.getVertexShader(), pipelineShader.vertex().getFile());
ImmutableList<SourceComponent> getVertexComponents() {
var layout = vertexType.getLayoutShader()
.getFile();
var instanceAssembly = pipelineShader.assemble(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<SourceFile> getFragmentComponents() {
return ImmutableList.of(material.getFragmentShader().getFile(), contextShader.getFragmentShader(),
pipelineShader.fragment().getFile());
ImmutableList<SourceComponent> getFragmentComponents() {
var material = this.material.getFragmentShader()
.getFile();
var context = contextShader.getFragmentShader();
var pipeline = pipelineShader.fragment()
.getFile();
return ImmutableList.of(material, context, pipeline);
}
}
@ -116,9 +130,7 @@ public class PipelineCompiler extends Memoizer<PipelineCompiler.Context, GlProgr
@Override
protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(key.generateHeader());
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");
finalSource.append("#extension GL_ARB_enhanced_layouts : enable\n");
@ -126,9 +138,18 @@ public class PipelineCompiler extends Memoizer<PipelineCompiler.Context, GlProgr
var ctx = new CompilationContext();
var names = ImmutableList.<ResourceLocation>builder();
for (var file : key.components) {
finalSource.append(file.generateFinalSource(ctx));
names.add(file.name);
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 {
@ -144,14 +165,15 @@ public class PipelineCompiler extends Memoizer<PipelineCompiler.Context, GlProgr
}
/**
* @param glslVersion The GLSL version to use.
* @param components A list of shader components to stitch together, in order.
* @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<SourceFile> components) {
public record Context(GLSLVersion glslVersion, ShaderType shaderType, List<SourceComponent> sourceComponents) {
public String generateHeader() {
return CompileUtil.generateHeader(glslVersion, shaderType);
}
}
}
}

View file

@ -1,20 +1,26 @@
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.core.source.FileResolution;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
public class ComputeCullerCompiler extends Memoizer<FileResolution, GlProgram> {
import net.minecraft.resources.ResourceLocation;
public class ComputeCullerCompiler extends Memoizer<StructType<?>, GlProgram> {
public static final ComputeCullerCompiler INSTANCE = new ComputeCullerCompiler();
@ -22,22 +28,35 @@ public class ComputeCullerCompiler extends Memoizer<FileResolution, GlProgram> {
}
@Override
protected GlProgram _create(FileResolution file) {
protected GlProgram _create(StructType<?> structType) {
var location = structType.getInstanceShader();
var finalSource = new StringBuilder();
CompilationContext context = new CompilationContext();
var components = List.of(structType.getLayout()
.getIndirectComponent(), 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));
finalSource.append(file.getFile()
.generateFinalSource(context));
for (var include : included) {
finalSource.append(include.source(context));
}
finalSource.append(Components.Pipeline.INDIRECT_CULL.getFile()
.generateFinalSource(context));
for (var component : components) {
finalSource.append(component.source(context));
}
try {
var shader = new GlShader(finalSource.toString(), ShaderType.COMPUTE, ImmutableList.of(file.getFileLoc()));
var fileLoc = location.getFileLoc();
var shader = new GlShader(finalSource.toString(), ShaderType.COMPUTE, ImmutableList.of(fileLoc));
return new ProgramAssembler(file.getFileLoc()).attachShader(shader)
return new ProgramAssembler(fileLoc).attachShader(shader)
.link()
.build(GlProgram::new);
} catch (ShaderCompilationException e) {

View file

@ -64,9 +64,8 @@ public class IndirectCullingGroup<T extends InstancedPart> {
.quads2Tris(2048).buffer.handle();
setupVertexArray();
var indirectShader = structType.getIndirectShader();
compute = ComputeCullerCompiler.INSTANCE.get(indirectShader);
draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.SHULKER, indirectShader, Components.WORLD, Components.INDIRECT));
compute = ComputeCullerCompiler.INSTANCE.get(structType);
draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(vertexType, Materials.SHULKER, structType, Components.WORLD, Components.INDIRECT));
}
private void setupVertexArray() {
@ -75,7 +74,7 @@ public class IndirectCullingGroup<T extends InstancedPart> {
var meshLayout = vertexType.getLayout();
var meshAttribs = meshLayout.getAttributeCount();
var attributes = meshLayout.getAttributes();
var attributes = meshLayout.attributes();
long offset = 0;
for (int i = 0; i < meshAttribs; i++) {

View file

@ -9,10 +9,9 @@ import org.jetbrains.annotations.NotNull;
import org.lwjgl.opengl.GL32;
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.material.Material;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
@ -20,8 +19,6 @@ import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
import com.jozufozu.flywheel.util.WeakHashSet;
import com.mojang.blaze3d.systems.RenderSystem;
@ -118,16 +115,16 @@ public class InstancingEngine implements Engine {
}
protected void setup(ShaderState desc) {
VertexType vertexType = desc.vertex();
FileResolution instanceShader = desc.instance()
.getInstanceShader();
Material material = desc.material();
var vertexType = desc.vertex();
var structType = desc.instance();
var material = desc.material();
var ctx = new PipelineCompiler.Context(vertexType, material, instanceShader, context, Components.INSTANCED_ARRAYS);
var ctx = new PipelineCompiler.Context(vertexType, material, structType, context, Components.INSTANCED_ARRAYS);
PipelineCompiler.INSTANCE.getProgram(ctx)
.bind();
UniformBuffer.getInstance().sync();
UniformBuffer.getInstance()
.sync();
}
@Override

View file

@ -1,6 +1,7 @@
package com.jozufozu.flywheel.config;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.backend.BackendType;
import com.jozufozu.flywheel.core.BackendTypes;
@ -29,6 +30,7 @@ public class FlwConfig {
return INSTANCE;
}
@Nullable
public BackendType getBackendType() {
return BackendTypes.getBackendType(client.backend.get());
}

View file

@ -2,6 +2,7 @@ package com.jozufozu.flywheel.core;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
@ -55,7 +56,7 @@ public class BackendTypes {
@Nullable
public static BackendType getBackendType(String name) {
return BACKEND_TYPES.get(name);
return BACKEND_TYPES.get(name.toLowerCase(Locale.ROOT));
}
/**

View file

@ -29,8 +29,11 @@ public class Components {
public static final ContextShader WORLD = ComponentRegistry.register(new ContextShader(WorldProgram::new, Files.WORLD_VERTEX, Files.WORLD_FRAGMENT));
public static final ContextShader CRUMBLING = ComponentRegistry.register(new ContextShader(CrumblingProgram::new, Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT));
public static final PipelineShader INSTANCED_ARRAYS = new PipelineShader(GLSLVersion.V420, Pipeline.INSTANCED_ARRAYS_DRAW, Pipeline.DRAW_FRAGMENT);
public static final PipelineShader INDIRECT = new PipelineShader(GLSLVersion.V460, Pipeline.INDIRECT_DRAW, Pipeline.DRAW_FRAGMENT);
public static final PipelineShader INSTANCED_ARRAYS = new PipelineShader(GLSLVersion.V420, Pipeline.INSTANCED_ARRAYS_DRAW, Pipeline.DRAW_FRAGMENT, (structType) -> structType.getLayout()
.getInstancedArraysComponent());
public static final PipelineShader INDIRECT = new PipelineShader(GLSLVersion.V460, Pipeline.INDIRECT_DRAW, Pipeline.DRAW_FRAGMENT, (structType) -> structType.getLayout()
.getIndirectComponent());
public static final FileResolution UTIL_TYPES = FileResolution.get(Flywheel.rl("util/types.glsl"));
public static void init() {
Files.init();
@ -59,9 +62,7 @@ public class Components {
public static final FileResolution BLOCK_LAYOUT = layoutVertex(ResourceUtil.subPath(Names.BLOCK, ".vert"));
public static final FileResolution POS_TEX_NORMAL_LAYOUT = layoutVertex(ResourceUtil.subPath(Names.POS_TEX_NORMAL, ".vert"));
public static final FileResolution TRANSFORMED = instanceVertex(ResourceUtil.subPath(Names.TRANSFORMED, ".vert"));
public static final FileResolution TRANSFORMED_INDIRECT = instanceVertex(ResourceUtil.subPath(Names.TRANSFORMED, "_indirect.glsl"));
public static final FileResolution ORIENTED = instanceVertex(ResourceUtil.subPath(Names.ORIENTED, ".vert"));
public static final FileResolution ORIENTED_INDIRECT = instanceVertex(ResourceUtil.subPath(Names.ORIENTED, "_indirect.glsl"));
public static final FileResolution DEFAULT_VERTEX = materialVertex(ResourceUtil.subPath(Names.DEFAULT, ".vert"));
public static final FileResolution SHADED_VERTEX = materialVertex(ResourceUtil.subPath(Names.SHADED, ".vert"));
public static final FileResolution DEFAULT_FRAGMENT = materialFragment(ResourceUtil.subPath(Names.DEFAULT, ".frag"));

View file

@ -19,7 +19,7 @@ public class FullscreenQuad {
public static final Lazy<FullscreenQuad> INSTANCE = Lazy.of(FullscreenQuad::new);
private static final BufferLayout LAYOUT = BufferLayout.builder()
.addItems(CommonItems.VEC4)
.addItem(CommonItems.VEC4, "posTex")
.build();
private static final float[] vertices = {

View file

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

View file

@ -24,7 +24,9 @@ public class CompileUtil {
public static int getElementCount(String type) {
Matcher vec = vecType.matcher(type);
if (vec.find()) return Integer.parseInt(vec.group(1));
if (vec.find()) {
return Integer.parseInt(vec.group(1));
}
Matcher mat = matType.matcher(type);
if (mat.find()) {
@ -32,7 +34,9 @@ public class CompileUtil {
String m = mat.group(2);
if (m != null) return Integer.parseInt(m) * n;
if (m != null) {
return Integer.parseInt(m) * n;
}
return n;
}

View file

@ -68,11 +68,17 @@ public class DebugCompiler extends Memoizer<DebugCompiler.Context, GlProgram> {
protected GlShader _create(Context ctx) {
var index = new CompilationContext();
String source = CompileUtil.generateHeader(GLSLVersion.V420, ctx.type) + ctx.source.getFile()
.generateFinalSource(index);
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, ctx.type, ImmutableList.of(ctx.source.getFileLoc()));
return new GlShader(source.toString(), ctx.type, ImmutableList.of(ctx.source.getFileLoc()));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(index);
}

View file

@ -9,7 +9,7 @@ public class ShaderCompilationException extends ShaderLoadingException {
private final int shaderHandle;
private String errors = "";
public String errors = "";
public ShaderCompilationException(String message, int shaderHandle) {
super(message);

View file

@ -1,11 +1,21 @@
package com.jozufozu.flywheel.core.layout;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.generate.GlslBuilder;
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
import net.minecraft.resources.ResourceLocation;
/**
* Classic Vertex Format struct with a clever name.
@ -19,23 +29,25 @@ import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
*/
public class BufferLayout {
private final List<VertexAttribute> attributes;
public final List<LayoutItem> layoutItems;
public final List<VertexAttribute> attributes;
private final int stride;
public BufferLayout(List<LayoutItem> layoutItems, int padding) {
this.layoutItems = ImmutableList.copyOf(layoutItems);
ImmutableList.Builder<VertexAttribute> attributes = ImmutableList.builder();
for (LayoutItem item : layoutItems) {
item.provideAttributes(attributes::add);
for (var item : layoutItems) {
item.type.provideAttributes(attributes::add);
}
this.attributes = attributes.build();
this.stride = calculateStride(this.attributes) + padding;
}
public List<VertexAttribute> getAttributes() {
public List<VertexAttribute> attributes() {
return attributes;
}
@ -47,6 +59,14 @@ public class BufferLayout {
return stride;
}
public InstancedArraysComponent getInstancedArraysComponent() {
return new InstancedArraysComponent();
}
public IndirectComponent getIndirectComponent() {
return new IndirectComponent();
}
public static Builder builder() {
return new Builder();
}
@ -67,8 +87,8 @@ public class BufferLayout {
allItems = ImmutableList.builder();
}
public Builder addItems(LayoutItem... attributes) {
allItems.add(attributes);
public Builder addItem(InputType type, String name) {
allItems.add(new LayoutItem(type, name));
return this;
}
@ -82,4 +102,128 @@ public class BufferLayout {
}
}
public record LayoutItem(InputType type, String name) {
public String unpack(String argName) {
return argName + '.' + name;
}
}
public class InstancedArraysComponent implements SourceComponent {
@Override
public Collection<? extends SourceComponent> included() {
return Collections.emptyList();
}
@Override
public String source(CompilationContext ctx) {
return generateInstancedArrays(5, "Instance");
}
@Override
public ResourceLocation name() {
return Flywheel.rl("generated_instanced_arrays");
}
public String generateInstancedArrays(int baseIndex, String structName) {
var builder = new GlslBuilder();
builder.define("FlwInstance", structName);
int i = baseIndex;
final var attributeSuffix = "_vertex_in";
for (var field : layoutItems) {
builder.vertexInput()
.binding(i)
.type(field.type.typeName())
.name(field.name + attributeSuffix);
i += field.type.attributeCount();
}
builder.blankLine();
var structBuilder = builder.struct();
structBuilder.setName(structName);
for (var field : layoutItems) {
structBuilder.addField(field.type.typeName(), field.name);
}
builder.blankLine();
var func = builder.function()
.returnType(structName)
.name("flw_unpackInstance");
var args = layoutItems.stream()
.map(it -> new GlslExpr.Variable(it.name + attributeSuffix))
.map(GlslExpr::minPrint)
.collect(Collectors.joining(", "));
func.statement("return " + structName + "(" + args + ");");
return builder.build();
}
}
public class IndirectComponent implements SourceComponent {
private static final String UNPACK_ARG = "p";
private static GlslExpr intoGlsl(LayoutItem layoutItem) {
return GlslExpr.variable(UNPACK_ARG)
.access(layoutItem.name)
.transform(layoutItem.type::unpack);
}
@Override
public Collection<? extends SourceComponent> included() {
return List.of(Components.UTIL_TYPES.getFile());
}
@Override
public ResourceLocation name() {
return Flywheel.rl("generated_indirect");
}
@Override
public String source(CompilationContext ctx) {
var content = generateIndirect("IndirectStruct");
return ctx.generatedHeader(content, name().toString()) + content;
}
public String generateIndirect(String structName) {
var builder = new GlslBuilder();
final var packedStructName = structName + "_packed";
builder.define("FlwInstance", structName);
builder.define("FlwPackedInstance", packedStructName);
var packed = builder.struct();
builder.blankLine();
var instance = builder.struct();
packed.setName(packedStructName);
instance.setName(structName);
for (var field : layoutItems) {
packed.addField(field.type.packedTypeName(), field.name);
instance.addField(field.type.typeName(), field.name);
}
builder.blankLine();
var func = builder.function()
.returnType(structName)
.name("flw_unpackInstance")
.argumentIn(packedStructName, UNPACK_ARG);
var args = layoutItems.stream()
.map(IndirectComponent::intoGlsl)
.map(GlslExpr::minPrint)
.collect(Collectors.joining(", "));
func.statement("return " + structName + "(" + args + ");");
return builder.build();
}
}
}

View file

@ -6,34 +6,71 @@ import com.jozufozu.flywheel.backend.gl.array.VertexAttributeI;
public class CommonItems {
public static final PrimitiveItem VEC4 = primitiveF(GlNumericType.FLOAT, 4);
public static final PrimitiveItem VEC3 = primitiveF(GlNumericType.FLOAT, 3);
public static final PrimitiveItem VEC2 = primitiveF(GlNumericType.FLOAT, 2);
public static final PrimitiveItem FLOAT = primitiveF(GlNumericType.FLOAT, 1);
private static final String VEC3_TYPE = "vec3";
private static final String VEC4_TYPE = "vec4";
private static final String VEC2_TYPE = "vec2";
private static final String FLOAT_TYPE = "float";
public static final PrimitiveItem FLOAT = PrimitiveItem.builder()
.setAttribute(new VertexAttributeF(GlNumericType.FLOAT, 1, false))
.setTypeName(FLOAT_TYPE)
.setPackedTypeName(FLOAT_TYPE)
.createPrimitiveItem();
private static final String UINT_TYPE = "uint";
public static final PrimitiveItem NORM_3x8 = PrimitiveItem.builder()
.setAttribute(new VertexAttributeF(GlNumericType.BYTE, 3, true))
.setTypeName(VEC3_TYPE)
.setPackedTypeName(UINT_TYPE)
.unpack(expr -> expr.callFunction("unpackSnorm4x8")
.swizzle("xyz"))
.createPrimitiveItem();
public static final PrimitiveItem UNORM_4x8 = PrimitiveItem.builder()
.setAttribute(new VertexAttributeF(GlNumericType.UBYTE, 4, true))
.setTypeName(VEC4_TYPE)
.setPackedTypeName(UINT_TYPE)
.unpack(expr -> expr.callFunction("unpackUnorm4x8"))
.createPrimitiveItem();
public static final PrimitiveItem UNORM_3x8 = PrimitiveItem.builder()
.setAttribute(new VertexAttributeF(GlNumericType.UBYTE, 3, true))
.setTypeName(VEC3_TYPE)
.setPackedTypeName(UINT_TYPE)
.unpack(expr -> expr.callFunction("unpackUnorm4x8")
.swizzle("xyz"))
.createPrimitiveItem();
private static final String IVEC2_TYPE = "ivec2";
private static final String VEC4F_TYPE = "Vec4F";
public static final PrimitiveItem VEC4 = PrimitiveItem.builder()
.setAttribute(new VertexAttributeF(GlNumericType.FLOAT, 4, false))
.setTypeName(VEC4_TYPE)
.setPackedTypeName(VEC4F_TYPE)
.unpack(expr -> expr.callFunction("unpackVec4F"))
.createPrimitiveItem();
private static final String VEC3F_TYPE = "Vec3F";
public static final PrimitiveItem VEC3 = PrimitiveItem.builder()
.setAttribute(new VertexAttributeF(GlNumericType.FLOAT, 3, false))
.setTypeName(VEC3_TYPE)
.setPackedTypeName(VEC3F_TYPE)
.unpack(expr -> expr.callFunction("unpackVec3F"))
.createPrimitiveItem();
private static final String VEC2F_TYPE = "Vec2F";
public static final PrimitiveItem VEC2 = PrimitiveItem.builder()
.setAttribute(new VertexAttributeF(GlNumericType.FLOAT, 2, false))
.setTypeName(VEC2_TYPE)
.setPackedTypeName(VEC2F_TYPE)
.unpack(expr -> expr.callFunction("unpackVec2F"))
.createPrimitiveItem();
private static final String LIGHT_COORD_TYPE = "LightCoord";
public static final PrimitiveItem LIGHT_COORD = PrimitiveItem.builder()
.setAttribute(new VertexAttributeI(GlNumericType.USHORT, 2))
.setTypeName(VEC2_TYPE)
.setPackedTypeName(LIGHT_COORD_TYPE)
.unpack(expr -> expr.callFunction("unpackLightCoord"))
.createPrimitiveItem();
public static final PrimitiveItem QUATERNION = primitiveF(GlNumericType.FLOAT, 4);
public static final PrimitiveItem NORMAL = primitiveF(GlNumericType.BYTE, 3, true);
public static final PrimitiveItem UV = primitiveF(GlNumericType.FLOAT, 2);
public static final PrimitiveItem RGBA = primitiveF(GlNumericType.UBYTE, 4, true);
public static final PrimitiveItem RGB = primitiveF(GlNumericType.UBYTE, 3, true);
public static final PrimitiveItem LIGHT = primitiveI(GlNumericType.UBYTE, 2);
public static final PrimitiveItem LIGHT_SHORT = primitiveI(GlNumericType.USHORT, 2);
public static final MatrixItem MAT3 = new MatrixItem(3, 3, "mat3", "Mat3F", "unpackMat3F");
public static final MatrixItem MAT4 = new MatrixItem(4, 4, "mat4", "Mat4F", "unpackMat4F");
public static final PrimitiveItem NORMALIZED_BYTE = primitiveF(GlNumericType.BYTE, 1, true);
private static class Unpacking {
public static final MatrixItem MAT3 = new MatrixItem(3, 3);
public static final MatrixItem MAT4 = new MatrixItem(4, 4);
private static PrimitiveItem primitiveF(GlNumericType type, int count, boolean normalized) {
return new PrimitiveItem(new VertexAttributeF(type, count, normalized));
}
private static PrimitiveItem primitiveF(GlNumericType type, int count) {
return primitiveF(type, count, false);
}
private static PrimitiveItem primitiveI(GlNumericType type, int count) {
return new PrimitiveItem(new VertexAttributeI(type, count));
}
}

View file

@ -3,9 +3,17 @@ package com.jozufozu.flywheel.core.layout;
import java.util.function.Consumer;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
public interface LayoutItem {
public interface InputType {
void provideAttributes(Consumer<VertexAttribute> consumer);
String typeName();
String packedTypeName();
int attributeCount();
GlslExpr unpack(GlslExpr packed);
}

View file

@ -5,8 +5,10 @@ import java.util.function.Consumer;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.backend.gl.array.VertexAttributeF;
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
public record MatrixItem(int rows, int cols) implements LayoutItem {
public record MatrixItem(int rows, int cols, String typeName, String packedTypeName,
String unpackingFunction) implements InputType {
@Override
public void provideAttributes(Consumer<VertexAttribute> consumer) {
@ -15,4 +17,13 @@ public record MatrixItem(int rows, int cols) implements LayoutItem {
}
}
@Override
public int attributeCount() {
return rows;
}
@Override
public GlslExpr unpack(GlslExpr packed) {
return packed.callFunction(unpackingFunction);
}
}

View file

@ -1,15 +1,23 @@
package com.jozufozu.flywheel.core.layout;
import java.util.function.Consumer;
import java.util.function.Function;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.core.source.generate.GlslExpr;
public class PrimitiveItem implements LayoutItem {
public class PrimitiveItem implements InputType {
private final VertexAttribute attribute;
private final String typeName;
private final String packedTypeName;
private final Function<GlslExpr, GlslExpr> unpackingFunction;
public PrimitiveItem(VertexAttribute attribute) {
public PrimitiveItem(VertexAttribute attribute, String typeName, String packedTypeName, Function<GlslExpr, GlslExpr> unpackingFunction) {
this.attribute = attribute;
this.typeName = typeName;
this.packedTypeName = packedTypeName;
this.unpackingFunction = unpackingFunction;
}
@Override
@ -17,4 +25,58 @@ public class PrimitiveItem implements LayoutItem {
consumer.accept(attribute);
}
@Override
public String typeName() {
return typeName;
}
@Override
public String packedTypeName() {
return packedTypeName;
}
@Override
public int attributeCount() {
return 1;
}
public static PrimitiveItemBuilder builder() {
return new PrimitiveItemBuilder();
}
@Override
public GlslExpr unpack(GlslExpr packed) {
return unpackingFunction.apply(packed);
}
public static class PrimitiveItemBuilder {
private VertexAttribute attribute;
private String typeName;
private String packedTypeName;
private Function<GlslExpr, GlslExpr> unpackingFunction = Function.identity();
public PrimitiveItemBuilder setAttribute(VertexAttribute attribute) {
this.attribute = attribute;
return this;
}
public PrimitiveItemBuilder setTypeName(String typeName) {
this.typeName = typeName;
return this;
}
public PrimitiveItemBuilder setPackedTypeName(String packedTypeName) {
this.packedTypeName = packedTypeName;
return this;
}
public PrimitiveItemBuilder unpack(Function<GlslExpr, GlslExpr> f) {
this.unpackingFunction = f;
return this;
}
public PrimitiveItem createPrimitiveItem() {
return new PrimitiveItem(attribute, typeName, packedTypeName, unpackingFunction);
}
}
}

View file

@ -11,28 +11,58 @@ import com.jozufozu.flywheel.core.source.span.Span;
public class CompilationContext {
public final List<SourceFile> files = new ArrayList<>();
/**
* 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.
*/
public int getFileID(SourceFile sourceFile) {
int i = files.indexOf(sourceFile);
if (i != -1) {
return i;
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;
}
int size = files.size();
files.add(sourceFile);
return size;
return out + '\n';
}
public boolean contains(SourceFile sourceFile) {
return files.contains(sourceFile);
}
public SourceFile getFile(int fileId) {
return files.get(fileId);
/**
* 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) {
@ -61,8 +91,4 @@ public class CompilationContext {
return null;
}
public Span getLineSpan(int fileId, int lineNo) {
return getFile(fileId).getLineSpanNoWhitespace(lineNo);
}
}

View file

@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.core.source.parse.ShaderFunction;
import com.jozufozu.flywheel.core.source.parse.Variable;
import com.jozufozu.flywheel.core.source.parse.ShaderVariable;
public class SourceChecks {
@ -46,7 +46,7 @@ public class SourceChecks {
}
ShaderFunction func = maybeFunc.get();
ImmutableList<Variable> params = func.getParameters();
ImmutableList<ShaderVariable> params = func.getParameters();
if (params.size() != arity) {
errorReporter.generateFunctionArgumentCountError(name, arity, func.getArgs());
return null;

View file

@ -1,8 +1,8 @@
package com.jozufozu.flywheel.core.source;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -13,6 +13,7 @@ import java.util.regex.Matcher;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.core.source.parse.Import;
import com.jozufozu.flywheel.core.source.parse.ShaderField;
@ -21,7 +22,6 @@ import com.jozufozu.flywheel.core.source.parse.ShaderStruct;
import com.jozufozu.flywheel.core.source.span.ErrorSpan;
import com.jozufozu.flywheel.core.source.span.Span;
import com.jozufozu.flywheel.core.source.span.StringSpan;
import com.jozufozu.flywheel.util.Pair;
import net.minecraft.resources.ResourceLocation;
@ -29,10 +29,10 @@ import net.minecraft.resources.ResourceLocation;
* Immutable class representing a shader file.
*
* <p>
* This class parses shader files and generates what is effectively a high level AST of the source.
* This class parses shader files and generates what is effectively a high level AST of the source.
* </p>
*/
public class SourceFile {
public class SourceFile implements SourceComponent {
public final ResourceLocation name;
@ -56,10 +56,9 @@ public class SourceFile {
*/
public final ImmutableList<Import> imports;
public final ImmutableMap<String, ShaderField> fields;
private final List<Span> elisions;
// POST-RESOLUTION
private List<Import> flattenedImports;
public List<SourceFile> flattenedImports;
public SourceFile(ErrorReporter errorReporter, ShaderSources parent, ResourceLocation name, String source) {
this.parent = parent;
@ -68,14 +67,27 @@ public class SourceFile {
this.lines = new SourceLines(source);
this.elisions = new ArrayList<>();
this.imports = parseImports(errorReporter);
this.functions = parseFunctions();
this.structs = parseStructs();
this.fields = parseFields();
}
@Override
public Collection<? extends SourceComponent> included() {
return flattenedImports;
}
@Override
public String source(CompilationContext ctx) {
return ctx.sourceHeader(this) + this.elideImports();
}
@Override
public ResourceLocation name() {
return name;
}
public void postResolve() {
this.flattenImports();
}
@ -91,7 +103,7 @@ public class SourceFile {
return;
}
List<Import> flat = new ArrayList<>(this.imports.size());
ArrayList<SourceFile> flat = new ArrayList<>(this.imports.size());
for (Import include : this.imports) {
SourceFile file = include.resolution.getFile();
@ -99,7 +111,7 @@ public class SourceFile {
file.flattenImports();
flat.addAll(file.flattenedImports);
flat.add(include);
flat.add(file);
}
this.flattenedImports = flat.stream()
@ -135,14 +147,8 @@ public class SourceFile {
if (struct != null) return Optional.of(struct);
for (Import include : flattenedImports) {
var file = include.getFile();
if (file == null) {
continue;
}
var external = file.structs.get(name);
for (var include : flattenedImports) {
var external = include.structs.get(name);
if (external != null) {
return Optional.of(external);
@ -163,14 +169,8 @@ public class SourceFile {
if (local != null) return Optional.of(local);
for (Import include : imports) {
var file = include.getFile();
if (file == null) {
continue;
}
var external = file.functions.get(name);
for (var include : flattenedImports) {
var external = include.functions.get(name);
if (external != null) {
return Optional.of(external);
@ -184,59 +184,19 @@ public class SourceFile {
return "#use " + '"' + name + '"';
}
public String generateFinalSource(CompilationContext context) {
List<Pair<Span, String>> replacements = Collections.emptyList();
var out = new StringBuilder();
for (Import include : flattenedImports) {
SourceFile file = include.getFile();
if (file == null || context.contains(file)) {
continue;
}
out.append(file.generateLineHeader(context))
.append(file.replaceAndElide(replacements));
}
out.append(this.generateLineHeader(context))
.append(this.replaceAndElide(replacements));
return out.toString();
}
private String generateLineHeader(CompilationContext env) {
return "#line " + 0 + ' ' + env.getFileID(this) + " // " + name + '\n';
}
public String printSource() {
return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers();
}
private CharSequence replaceAndElide(List<Pair<Span, String>> replacements) {
var replacementsAndElisions = new ArrayList<>(replacements);
for (var include : imports) {
replacementsAndElisions.add(Pair.of(include.self, ""));
}
return this.replace(replacementsAndElisions);
}
private CharSequence replace(List<Pair<Span, String>> replacements) {
private CharSequence elideImports() {
StringBuilder out = new StringBuilder();
int lastEnd = 0;
replacements.sort(Comparator.comparing(Pair::first));
for (var replacement : replacements) {
var loc = replacement.first();
if (loc.getSourceFile() != this) {
continue;
}
for (var include : imports) {
var loc = include.self;
out.append(this.source, lastEnd, loc.getStartPos());
out.append(replacement.second());
lastEnd = loc.getEndPos();
}

View file

@ -53,6 +53,11 @@ public class ErrorBuilder {
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)
.pointAtFile(span.getSourceFile())
.pointAt(span, 1);

View file

@ -0,0 +1,179 @@
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 GlslBuilder {
private final List<SourceElement> elements = new ArrayList<>();
public void define(String name, String value) {
elements.add(new Define(name, value));
}
public StructBuilder struct() {
return add(new StructBuilder());
}
public FunctionBuilder function() {
return add(new FunctionBuilder());
}
public VertexInputBuilder vertexInput() {
return add(new VertexInputBuilder());
}
public <T extends SourceElement> T add(T element) {
elements.add(element);
return element;
}
public String build() {
return elements.stream()
.map(SourceElement::build)
.collect(Collectors.joining("\n"));
}
public void blankLine() {
elements.add(Separators.BLANK_LINE);
}
public enum Separators implements SourceElement {
BLANK_LINE(""),
;
private final String separator;
Separators(String separator) {
this.separator = separator;
}
@Override
public String build() {
return separator;
}
}
public interface SourceElement {
String build();
}
public record Define(String name, String value) implements SourceElement {
@Override
public String build() {
return "#define " + name + " " + value;
}
}
public static class VertexInputBuilder implements SourceElement {
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
public String build() {
return "layout(location = " + binding + ") in " + type + " " + 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(", "));
}
}
}

View file

@ -0,0 +1,86 @@
package com.jozufozu.flywheel.core.source.generate;
import java.util.function.Function;
public sealed interface GlslExpr {
/**
* Create a glsl variable with the given name.
*
* @param name The name of the variable.
* @return A new variable expression.
*/
static Variable variable(String name) {
return new Variable(name);
}
String minPrint();
/**
* Call a one-parameter function with the given name on this expression.
*
* @param name The name of the function to call.
* @return A new glsl function call expression.
*/
default FunctionCall callFunction(String name) {
return new FunctionCall(name, this);
}
/**
* Swizzle the components of this expression.
*
* @param selection The components to select. For example, "xyz", "zyx", or "zzzw".
* @return A new glsl swizzle expression.
*/
default Swizzle swizzle(String selection) {
return new Swizzle(this, selection);
}
/**
* Access the given member of this expression.
*
* @param member The name of the member to access.
* @return A new glsl member access expression.
*/
default Access access(String member) {
return new Access(this, member);
}
/**
* Catchall method for applying external transformations to this expression.
*
* @param f The transformation to apply.
* @return A new expression.
*/
default GlslExpr transform(Function<GlslExpr, GlslExpr> f) {
return f.apply(this);
}
record Variable(String name) implements GlslExpr {
@Override
public String minPrint() {
return name;
}
}
record FunctionCall(String name, GlslExpr target) implements GlslExpr {
@Override
public String minPrint() {
return name + "(" + target.minPrint() + ")";
}
}
record Swizzle(GlslExpr target, String selection) implements GlslExpr {
@Override
public String minPrint() {
return target.minPrint() + "." + selection;
}
}
record Access(GlslExpr target, String argName) implements GlslExpr {
@Override
public String minPrint() {
return target.minPrint() + "." + argName;
}
}
}

View file

@ -20,7 +20,7 @@ public class ShaderFunction extends AbstractShaderElement {
private final Span args;
private final Span body;
private final ImmutableList<Variable> parameters;
private final ImmutableList<ShaderVariable> parameters;
public ShaderFunction(Span self, Span type, Span name, Span args, Span body) {
super(self);
@ -56,7 +56,7 @@ public class ShaderFunction extends AbstractShaderElement {
return parameters.get(index).type;
}
public ImmutableList<Variable> getParameters() {
public ImmutableList<ShaderVariable> getParameters() {
return parameters;
}
@ -64,12 +64,12 @@ public class ShaderFunction extends AbstractShaderElement {
return type.get();
}
protected ImmutableList<Variable> parseArguments() {
protected ImmutableList<ShaderVariable> parseArguments() {
if (args.isErr() || args.isEmpty()) return ImmutableList.of();
Matcher arguments = argument.matcher(args.get());
ImmutableList.Builder<Variable> builder = ImmutableList.builder();
ImmutableList.Builder<ShaderVariable> builder = ImmutableList.builder();
while (arguments.find()) {
Span self = Span.fromMatcher(args, arguments);
@ -77,7 +77,7 @@ public class ShaderFunction extends AbstractShaderElement {
Span type = Span.fromMatcher(args, arguments, 2);
Span name = Span.fromMatcher(args, arguments, 3);
builder.add(new Variable(self, qualifier, type, name));
builder.add(new ShaderVariable(self, qualifier, type, name));
}
return builder.build();

View file

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

View file

@ -15,8 +15,11 @@ import com.mojang.math.Vector4f;
public class OrientedType implements StructType<OrientedPart> {
public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.LIGHT, CommonItems.RGBA)
.addItems(CommonItems.VEC3, CommonItems.VEC3, CommonItems.QUATERNION)
.addItem(CommonItems.LIGHT_COORD, "light")
.addItem(CommonItems.UNORM_4x8, "color")
.addItem(CommonItems.VEC3, "position")
.addItem(CommonItems.VEC3, "pivot")
.addItem(CommonItems.VEC4, "rotation")
.build();
@Override
@ -44,11 +47,6 @@ public class OrientedType implements StructType<OrientedPart> {
return Components.Files.ORIENTED;
}
@Override
public FileResolution getIndirectShader() {
return Components.Files.ORIENTED_INDIRECT;
}
@Override
public VertexTransformer<OrientedPart> getVertexTransformer() {
return (vertexList, struct, level) -> {

View file

@ -13,8 +13,10 @@ import com.mojang.math.Vector4f;
public class TransformedType implements StructType<TransformedPart> {
public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.LIGHT, CommonItems.RGBA)
.addItems(CommonItems.MAT4, CommonItems.MAT3)
.addItem(CommonItems.LIGHT_COORD, "light")
.addItem(CommonItems.UNORM_4x8, "color")
.addItem(CommonItems.MAT4, "pose")
.addItem(CommonItems.MAT3, "normal")
.build();
@Override
@ -42,11 +44,6 @@ public class TransformedType implements StructType<TransformedPart> {
return Components.Files.TRANSFORMED;
}
@Override
public FileResolution getIndirectShader() {
return Components.Files.TRANSFORMED_INDIRECT;
}
@Override
public VertexTransformer<TransformedPart> getVertexTransformer() {
return (vertexList, struct, level) -> {

View file

@ -8,11 +8,11 @@ import com.jozufozu.flywheel.core.source.FileResolution;
public class BlockVertex implements VertexType {
public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.VEC3,
CommonItems.RGBA,
CommonItems.UV,
CommonItems.LIGHT_SHORT,
CommonItems.NORMAL)
.addItem(CommonItems.VEC3, "position")
.addItem(CommonItems.UNORM_4x8, "color")
.addItem(CommonItems.VEC2, "tex")
.addItem(CommonItems.LIGHT_COORD, "light")
.addItem(CommonItems.NORM_3x8, "normal")
.withPadding(1)
.build();

View file

@ -8,7 +8,9 @@ import com.jozufozu.flywheel.core.source.FileResolution;
public class PosTexNormalVertex implements VertexType {
public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.VEC3, CommonItems.UV, CommonItems.NORMAL)
.addItem(CommonItems.VEC3, "position")
.addItem(CommonItems.VEC2, "tex")
.addItem(CommonItems.NORM_3x8, "normal")
.build();
@Override

View file

@ -1,15 +1,20 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/quaternion.glsl"
#use "flywheel:util/types.glsl"
layout(location = FLW_INSTANCE_BASE_INDEX + 0) in ivec2 oriented_light;
layout(location = FLW_INSTANCE_BASE_INDEX + 1) in vec4 oriented_color;
layout(location = FLW_INSTANCE_BASE_INDEX + 2) in vec3 oriented_pos;
layout(location = FLW_INSTANCE_BASE_INDEX + 3) in vec3 oriented_pivot;
layout(location = FLW_INSTANCE_BASE_INDEX + 4) in vec4 oriented_rotation;
void flw_transformBoundingSphere(in FlwInstance i, inout vec3 center, inout float radius) {
vec4 rotation = i.rotation;
vec3 pivot = i.pivot;
vec3 pos = i.position;
void flw_instanceVertex() {
flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - oriented_pivot, oriented_rotation) + oriented_pivot + oriented_pos, 1.0);
flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, oriented_rotation);
flw_vertexColor = oriented_color;
flw_vertexLight = oriented_light / 15.0;
center = rotateVertexByQuat(center - pivot, rotation) + pivot + pos;
}
#ifdef VERTEX_SHADER
void flw_instanceVertex(in FlwInstance i) {
flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - i.pivot, i.rotation) + i.pivot + i.position, 1.0);
flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, i.rotation);
flw_vertexColor = i.color;
flw_vertexLight = i.light;
}
#endif

View file

@ -1,32 +0,0 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/quaternion.glsl"
#use "flywheel:util/types.glsl"
#define FLW_INSTANCE_STRUCT Instance
struct Instance {
Vec4F rotation;
Vec3F pos;
Vec3F pivot;
uint light;
uint color;
};
void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) {
vec4 rotation = unpackVec4F(i.rotation);
vec3 pivot = unpackVec3F(i.pivot);
vec3 pos = unpackVec3F(i.pos);
center = rotateVertexByQuat(center - pivot, rotation) + pivot + pos;
}
#ifdef VERTEX_SHADER
void flw_instanceVertex(Instance i) {
vec4 rotation = unpackVec4F(i.rotation);
vec3 pivot = unpackVec3F(i.pivot);
vec3 pos = unpackVec3F(i.pos);
flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - pivot, rotation) + pivot + pos, 1.0);
flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, rotation);
flw_vertexColor = unpackUnorm4x8(i.color);
flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0;
}
#endif

View file

@ -1,13 +1,19 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/types.glsl"
layout(location = FLW_INSTANCE_BASE_INDEX + 0) in ivec2 transformed_light;
layout(location = FLW_INSTANCE_BASE_INDEX + 1) in vec4 transformed_color;
layout(location = FLW_INSTANCE_BASE_INDEX + 2) in mat4 transformed_pose;
layout(location = FLW_INSTANCE_BASE_INDEX + 6) in mat3 transformed_normal;
void flw_transformBoundingSphere(in FlwInstance i, inout vec3 center, inout float radius) {
mat4 pose = i.pose;
center = (pose * vec4(center, 1.0)).xyz;
void flw_instanceVertex() {
flw_vertexPos = transformed_pose * flw_vertexPos;
flw_vertexNormal = transformed_normal * flw_vertexNormal;
flw_vertexColor = transformed_color;
flw_vertexLight = transformed_light / 15.0;
float scale = max(length(pose[0].xyz), max(length(pose[1].xyz), length(pose[2].xyz)));
radius *= scale;
}
#ifdef VERTEX_SHADER
void flw_instanceVertex(in FlwInstance i) {
flw_vertexPos = i.pose * flw_vertexPos;
flw_vertexNormal = i.normal * flw_vertexNormal;
flw_vertexColor = i.color;
flw_vertexLight = i.light;
}
#endif

View file

@ -1,27 +0,0 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/types.glsl"
#define FLW_INSTANCE_STRUCT Instance
struct Instance {
Mat4F pose;
Mat3F normal;
uint color;
uint light;
};
void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) {
mat4 pose = unpackMat4F(i.pose);
center = (pose * vec4(center, 1.0)).xyz;
float scale = max(length(pose[0].xyz), max(length(pose[1].xyz), length(pose[2].xyz)));
radius *= scale;
}
#ifdef VERTEX_SHADER
void flw_instanceVertex(Instance i) {
flw_vertexPos = unpackMat4F(i.pose) * flw_vertexPos;
flw_vertexNormal = unpackMat3F(i.normal) * flw_vertexNormal;
flw_vertexColor = unpackUnorm4x8(i.color);
flw_vertexLight = vec2(float((i.light >> 16) & 0xFFFFu), float(i.light & 0xFFFFu)) / 15.0;
}
#endif

View file

@ -16,7 +16,7 @@ struct MeshDrawCommand {
// populated by instancers
layout(std430, binding = 0) restrict readonly buffer ObjectBuffer {
FLW_INSTANCE_STRUCT objects[];
FlwPackedInstance objects[];
};
layout(std430, binding = 1) restrict writeonly buffer TargetBuffer {
@ -45,7 +45,9 @@ bool isVisible() {
vec3 center;
float radius;
unpackBoundingSphere(sphere, center, radius);
flw_transformBoundingSphere(objects[flw_objectID], center, radius);
FlwInstance object = flw_unpackInstance(objects[flw_objectID]);
flw_transformBoundingSphere(object, center, radius);
return testSphere(center, radius);
}

View file

@ -1,7 +1,7 @@
#use "flywheel:api/vertex.glsl"
layout(std430, binding = 0) restrict readonly buffer ObjectBuffer {
FLW_INSTANCE_STRUCT objects[];
FlwPackedInstance objects[];
};
layout(std430, binding = 1) restrict readonly buffer TargetBuffer {
@ -10,8 +10,8 @@ layout(std430, binding = 1) restrict readonly buffer TargetBuffer {
void main() {
uint instanceIndex = objectIDs[gl_BaseInstance + gl_InstanceID];
FlwInstance i = flw_unpackInstance(objects[instanceIndex]);
flw_layoutVertex();
FLW_INSTANCE_STRUCT i = objects[instanceIndex];
flw_instanceVertex(i);
flw_materialVertex();
flw_contextVertex();

View file

@ -2,7 +2,8 @@
void main() {
flw_layoutVertex();
flw_instanceVertex();
FlwInstance i = flw_unpackInstance();
flw_instanceVertex(i);
flw_materialVertex();
flw_contextVertex();
}

View file

@ -32,6 +32,10 @@ struct BoundingSphere {
float radius;
};
struct LightCoord {
uint p;
};
vec3 unpackVec3F(in Vec3F v) {
return vec3(v.x, v.y, v.z);
}
@ -61,3 +65,7 @@ void unpackBoundingSphere(in BoundingSphere sphere, out vec3 center, out float r
center = unpackVec3F(sphere.center);
radius = sphere.radius;
}
vec2 unpackLightCoord(in LightCoord light) {
return vec2(float((light.p >> 16) & 0xFFFFu), float(light.p & 0xFFFFu)) / 15.0;
}