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; 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.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.source.FileResolution; 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(); StorageBufferWriter<S> getStorageBufferWriter();
FileResolution getIndirectShader();
interface VertexTransformer<S extends InstancedPart> { interface VertexTransformer<S extends InstancedPart> {
void transform(MutableVertexList vertexList, S struct, ClientLevel level); void transform(MutableVertexList vertexList, S struct, ClientLevel level);
} }

View file

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

View file

@ -1,14 +1,20 @@
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.material.Material;
import com.jozufozu.flywheel.api.pipeline.PipelineShader;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType; 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.core.ComponentRegistry;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler; 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.Components;
import com.jozufozu.flywheel.core.compile.ShaderCompilationException;
import com.jozufozu.flywheel.core.source.FileResolution; 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 com.jozufozu.flywheel.util.StringUtil;
@ -63,21 +69,45 @@ public class Loader implements ResourceManagerReloadListener {
Backend.LOGGER.info("All shaders passed checks."); Backend.LOGGER.info("All shaders passed checks.");
long compileStart = System.nanoTime(); long compileStart = System.nanoTime();
int programCounter = 0;
boolean crash = false;
for (Material material : ComponentRegistry.materials) { for (Material material : ComponentRegistry.materials) {
for (StructType<?> structType : ComponentRegistry.structTypes) { for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) { for (VertexType vertexType : ComponentRegistry.vertexTypes) {
for (ContextShader contextShader : ComponentRegistry.contextShaders) { for (ContextShader contextShader : ComponentRegistry.contextShaders) {
var ctx = new PipelineCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader, Components.INSTANCED_ARRAYS); for (PipelineShader pipelineShader : List.of(Components.INSTANCED_ARRAYS, Components.INDIRECT)) {
PipelineCompiler.INSTANCE.getProgram(ctx); 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(); 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; ClientLevel world = Minecraft.getInstance().level;
if (Backend.canUseInstancing(world)) { if (Backend.canUseInstancing(world)) {

View file

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

View file

@ -1,23 +1,24 @@
package com.jozufozu.flywheel.backend.instancing; package com.jozufozu.flywheel.backend.instancing;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.pipeline.PipelineShader; 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.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GLSLVersion; import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType; 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.CompileUtil;
import com.jozufozu.flywheel.core.compile.Memoizer; import com.jozufozu.flywheel.core.compile.Memoizer;
import com.jozufozu.flywheel.core.compile.ProgramAssembler; import com.jozufozu.flywheel.core.compile.ProgramAssembler;
import com.jozufozu.flywheel.core.compile.ShaderCompilationException; import com.jozufozu.flywheel.core.compile.ShaderCompilationException;
import com.jozufozu.flywheel.core.source.CompilationContext; 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 com.jozufozu.flywheel.event.ReloadRenderersEvent;
import net.minecraft.resources.ResourceLocation; 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 vertex = new ShaderCompiler.Context(glslVersion, ShaderType.VERTEX, ctx.getVertexComponents());
var fragment = new ShaderCompiler.Context(glslVersion, ShaderType.FRAGMENT, ctx.getFragmentComponents()); var fragment = new ShaderCompiler.Context(glslVersion, ShaderType.FRAGMENT, ctx.getFragmentComponents());
return new ProgramAssembler(ctx.instanceShader.getFileLoc()) return new ProgramAssembler(ctx.structType.getInstanceShader()
.attachShader(shaderCompiler.get(vertex)) .getFileLoc()).attachShader(shaderCompiler.get(vertex))
.attachShader(shaderCompiler.get(fragment)) .attachShader(shaderCompiler.get(fragment))
.link() .link()
.build(ctx.contextShader.factory()); .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. * Represents the entire context of a program's usage.
* *
* @param vertexType The vertexType the program should be adapted for. * @param vertexType The vertexType the program should be adapted for.
* @param material The material shader to use. TODO: Flatten materials * @param material The material shader to use. TODO: Flatten materials
* @param instanceShader The instance shader to use. * @param structType The instance shader to use.
* @param contextShader The context 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) { ContextShader contextShader, PipelineShader pipelineShader) {
ImmutableList<SourceFile> getVertexComponents() { ImmutableList<SourceComponent> getVertexComponents() {
return ImmutableList.of(vertexType.getLayoutShader().getFile(), instanceShader.getFile(), material.getVertexShader().getFile(), var layout = vertexType.getLayoutShader()
contextShader.getVertexShader(), pipelineShader.vertex().getFile()); .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() { ImmutableList<SourceComponent> getFragmentComponents() {
return ImmutableList.of(material.getFragmentShader().getFile(), contextShader.getFragmentShader(), var material = this.material.getFragmentShader()
pipelineShader.fragment().getFile()); .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 @Override
protected GlShader _create(Context key) { protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder(); StringBuilder finalSource = new StringBuilder(key.generateHeader());
finalSource.append(key.generateHeader());
finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n"); finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n");
finalSource.append("#extension GL_ARB_conservative_depth : enable\n"); finalSource.append("#extension GL_ARB_conservative_depth : enable\n");
finalSource.append("#extension GL_ARB_enhanced_layouts : 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 ctx = new CompilationContext();
var names = ImmutableList.<ResourceLocation>builder(); var names = ImmutableList.<ResourceLocation>builder();
for (var file : key.components) { var included = new LinkedHashSet<SourceComponent>(); // linked to preserve order
finalSource.append(file.generateFinalSource(ctx)); for (var component : key.sourceComponents) {
names.add(file.name); 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 { try {
@ -144,14 +165,15 @@ public class PipelineCompiler extends Memoizer<PipelineCompiler.Context, GlProgr
} }
/** /**
* @param glslVersion The GLSL version to use. * @param glslVersion The GLSL version to use.
* @param components A list of shader components to stitch together, in order. * @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() { public String generateHeader() {
return CompileUtil.generateHeader(glslVersion, shaderType); return CompileUtil.generateHeader(glslVersion, shaderType);
} }
} }
} }
} }

View file

@ -1,20 +1,26 @@
package com.jozufozu.flywheel.backend.instancing.indirect; package com.jozufozu.flywheel.backend.instancing.indirect;
import java.util.LinkedHashSet;
import java.util.List;
import com.google.common.collect.ImmutableList; 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.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.Components; 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.CompileUtil;
import com.jozufozu.flywheel.core.compile.Memoizer; import com.jozufozu.flywheel.core.compile.Memoizer;
import com.jozufozu.flywheel.core.compile.ProgramAssembler; import com.jozufozu.flywheel.core.compile.ProgramAssembler;
import com.jozufozu.flywheel.core.compile.ShaderCompilationException; import com.jozufozu.flywheel.core.compile.ShaderCompilationException;
import com.jozufozu.flywheel.core.source.CompilationContext; import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; 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(); public static final ComputeCullerCompiler INSTANCE = new ComputeCullerCompiler();
@ -22,22 +28,35 @@ public class ComputeCullerCompiler extends Memoizer<FileResolution, GlProgram> {
} }
@Override @Override
protected GlProgram _create(FileResolution file) { protected GlProgram _create(StructType<?> structType) {
var location = structType.getInstanceShader();
var finalSource = new StringBuilder(); var finalSource = new StringBuilder();
CompilationContext context = new CompilationContext(); 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(CompileUtil.generateHeader(GLSLVersion.V460, ShaderType.COMPUTE));
finalSource.append(file.getFile() for (var include : included) {
.generateFinalSource(context)); finalSource.append(include.source(context));
}
finalSource.append(Components.Pipeline.INDIRECT_CULL.getFile() for (var component : components) {
.generateFinalSource(context)); finalSource.append(component.source(context));
}
try { 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() .link()
.build(GlProgram::new); .build(GlProgram::new);
} catch (ShaderCompilationException e) { } catch (ShaderCompilationException e) {

View file

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

View file

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

View file

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

View file

@ -2,6 +2,7 @@ package com.jozufozu.flywheel.core;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -55,7 +56,7 @@ public class BackendTypes {
@Nullable @Nullable
public static BackendType getBackendType(String name) { 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 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 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 INSTANCED_ARRAYS = new PipelineShader(GLSLVersion.V420, Pipeline.INSTANCED_ARRAYS_DRAW, Pipeline.DRAW_FRAGMENT, (structType) -> structType.getLayout()
public static final PipelineShader INDIRECT = new PipelineShader(GLSLVersion.V460, Pipeline.INDIRECT_DRAW, Pipeline.DRAW_FRAGMENT); .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() { public static void init() {
Files.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 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 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 = 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 = 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 DEFAULT_VERTEX = materialVertex(ResourceUtil.subPath(Names.DEFAULT, ".vert"));
public static final FileResolution SHADED_VERTEX = materialVertex(ResourceUtil.subPath(Names.SHADED, ".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")); 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); public static final Lazy<FullscreenQuad> INSTANCE = Lazy.of(FullscreenQuad::new);
private static final BufferLayout LAYOUT = BufferLayout.builder() private static final BufferLayout LAYOUT = BufferLayout.builder()
.addItems(CommonItems.VEC4) .addItem(CommonItems.VEC4, "posTex")
.build(); .build();
private static final float[] vertices = { 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) { public static int getElementCount(String type) {
Matcher vec = vecType.matcher(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); Matcher mat = matType.matcher(type);
if (mat.find()) { if (mat.find()) {
@ -32,7 +34,9 @@ public class CompileUtil {
String m = mat.group(2); String m = mat.group(2);
if (m != null) return Integer.parseInt(m) * n; if (m != null) {
return Integer.parseInt(m) * n;
}
return n; return n;
} }

View file

@ -68,11 +68,17 @@ public class DebugCompiler extends Memoizer<DebugCompiler.Context, GlProgram> {
protected GlShader _create(Context ctx) { protected GlShader _create(Context ctx) {
var index = new CompilationContext(); var index = new CompilationContext();
String source = CompileUtil.generateHeader(GLSLVersion.V420, ctx.type) + ctx.source.getFile() StringBuilder source = new StringBuilder(CompileUtil.generateHeader(GLSLVersion.V420, ctx.type));
.generateFinalSource(index);
var file = ctx.source.getFile();
for (var include : file.flattenedImports) {
source.append(include.source(index));
}
source.append(file.source(index));
try { 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) { } catch (ShaderCompilationException e) {
throw e.withErrorLog(index); throw e.withErrorLog(index);
} }

View file

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

View file

@ -1,11 +1,21 @@
package com.jozufozu.flywheel.core.layout; package com.jozufozu.flywheel.core.layout;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute; 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. * Classic Vertex Format struct with a clever name.
@ -19,23 +29,25 @@ import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
*/ */
public class BufferLayout { public class BufferLayout {
private final List<VertexAttribute> attributes; public final List<LayoutItem> layoutItems;
public final List<VertexAttribute> attributes;
private final int stride; private final int stride;
public BufferLayout(List<LayoutItem> layoutItems, int padding) { public BufferLayout(List<LayoutItem> layoutItems, int padding) {
this.layoutItems = ImmutableList.copyOf(layoutItems);
ImmutableList.Builder<VertexAttribute> attributes = ImmutableList.builder(); ImmutableList.Builder<VertexAttribute> attributes = ImmutableList.builder();
for (LayoutItem item : layoutItems) { for (var item : layoutItems) {
item.provideAttributes(attributes::add); item.type.provideAttributes(attributes::add);
} }
this.attributes = attributes.build(); this.attributes = attributes.build();
this.stride = calculateStride(this.attributes) + padding; this.stride = calculateStride(this.attributes) + padding;
} }
public List<VertexAttribute> getAttributes() { public List<VertexAttribute> attributes() {
return attributes; return attributes;
} }
@ -47,6 +59,14 @@ public class BufferLayout {
return stride; return stride;
} }
public InstancedArraysComponent getInstancedArraysComponent() {
return new InstancedArraysComponent();
}
public IndirectComponent getIndirectComponent() {
return new IndirectComponent();
}
public static Builder builder() { public static Builder builder() {
return new Builder(); return new Builder();
} }
@ -67,8 +87,8 @@ public class BufferLayout {
allItems = ImmutableList.builder(); allItems = ImmutableList.builder();
} }
public Builder addItems(LayoutItem... attributes) { public Builder addItem(InputType type, String name) {
allItems.add(attributes); allItems.add(new LayoutItem(type, name));
return this; 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 class CommonItems {
public static final PrimitiveItem VEC4 = primitiveF(GlNumericType.FLOAT, 4); private static final String VEC3_TYPE = "vec3";
public static final PrimitiveItem VEC3 = primitiveF(GlNumericType.FLOAT, 3); private static final String VEC4_TYPE = "vec4";
public static final PrimitiveItem VEC2 = primitiveF(GlNumericType.FLOAT, 2); private static final String VEC2_TYPE = "vec2";
public static final PrimitiveItem FLOAT = primitiveF(GlNumericType.FLOAT, 1); 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 MatrixItem MAT3 = new MatrixItem(3, 3, "mat3", "Mat3F", "unpackMat3F");
public static final PrimitiveItem RGB = primitiveF(GlNumericType.UBYTE, 3, true); public static final MatrixItem MAT4 = new MatrixItem(4, 4, "mat4", "Mat4F", "unpackMat4F");
public static final PrimitiveItem LIGHT = primitiveI(GlNumericType.UBYTE, 2);
public static final PrimitiveItem LIGHT_SHORT = primitiveI(GlNumericType.USHORT, 2);
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 java.util.function.Consumer;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute; 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); 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.GlNumericType;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute; import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.backend.gl.array.VertexAttributeF; 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 @Override
public void provideAttributes(Consumer<VertexAttribute> consumer) { 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; package com.jozufozu.flywheel.core.layout;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute; 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 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.attribute = attribute;
this.typeName = typeName;
this.packedTypeName = packedTypeName;
this.unpackingFunction = unpackingFunction;
} }
@Override @Override
@ -17,4 +25,58 @@ public class PrimitiveItem implements LayoutItem {
consumer.accept(attribute); 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 class CompilationContext {
public final List<SourceFile> files = new ArrayList<>(); public final List<SourceFile> files = new ArrayList<>();
/** private String generatedSource = "";
* Returns an arbitrary file ID for use this compilation context, or generates one if missing. private int generatedLines = 0;
* @param sourceFile The file to retrieve the ID for.
* @return A file ID unique to the given sourceFile. String sourceHeader(SourceFile sourceFile) {
*/ return "#line " + 0 + ' ' + getOrCreateFileID(sourceFile) + " // " + sourceFile.name + '\n';
public int getFileID(SourceFile sourceFile) { }
int i = files.indexOf(sourceFile);
if (i != -1) { public String generatedHeader(String generatedCode, @Nullable String comment) {
return i; generatedSource += generatedCode;
int lines = generatedCode.split("\n").length;
var out = "#line " + generatedLines + ' ' + 0;
generatedLines += lines;
if (comment != null) {
out += " // " + comment;
} }
int size = files.size(); return out + '\n';
files.add(sourceFile);
return size;
} }
public boolean contains(SourceFile sourceFile) { public boolean contains(SourceFile sourceFile) {
return files.contains(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) { public String parseErrors(String log) {
@ -61,8 +91,4 @@ public class CompilationContext {
return null; 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.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.source.error.ErrorReporter; import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.core.source.parse.ShaderFunction; 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 { public class SourceChecks {
@ -46,7 +46,7 @@ public class SourceChecks {
} }
ShaderFunction func = maybeFunc.get(); ShaderFunction func = maybeFunc.get();
ImmutableList<Variable> params = func.getParameters(); ImmutableList<ShaderVariable> params = func.getParameters();
if (params.size() != arity) { if (params.size() != arity) {
errorReporter.generateFunctionArgumentCountError(name, arity, func.getArgs()); errorReporter.generateFunctionArgumentCountError(name, arity, func.getArgs());
return null; return null;

View file

@ -1,8 +1,8 @@
package com.jozufozu.flywheel.core.source; package com.jozufozu.flywheel.core.source;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -13,6 +13,7 @@ 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.source.error.ErrorReporter; 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;
@ -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.ErrorSpan;
import com.jozufozu.flywheel.core.source.span.Span; import com.jozufozu.flywheel.core.source.span.Span;
import com.jozufozu.flywheel.core.source.span.StringSpan; import com.jozufozu.flywheel.core.source.span.StringSpan;
import com.jozufozu.flywheel.util.Pair;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@ -29,10 +29,10 @@ import net.minecraft.resources.ResourceLocation;
* Immutable class representing a shader file. * Immutable class representing a shader file.
* *
* <p> * <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> * </p>
*/ */
public class SourceFile { public class SourceFile implements SourceComponent {
public final ResourceLocation name; public final ResourceLocation name;
@ -56,10 +56,9 @@ public class SourceFile {
*/ */
public final ImmutableList<Import> imports; public final ImmutableList<Import> imports;
public final ImmutableMap<String, ShaderField> fields; public final ImmutableMap<String, ShaderField> fields;
private final List<Span> elisions;
// POST-RESOLUTION // POST-RESOLUTION
private List<Import> flattenedImports; public List<SourceFile> flattenedImports;
public SourceFile(ErrorReporter errorReporter, ShaderSources parent, ResourceLocation name, String source) { public SourceFile(ErrorReporter errorReporter, ShaderSources parent, ResourceLocation name, String source) {
this.parent = parent; this.parent = parent;
@ -68,14 +67,27 @@ public class SourceFile {
this.lines = new SourceLines(source); this.lines = new SourceLines(source);
this.elisions = new ArrayList<>();
this.imports = parseImports(errorReporter); this.imports = parseImports(errorReporter);
this.functions = parseFunctions(); this.functions = parseFunctions();
this.structs = parseStructs(); this.structs = parseStructs();
this.fields = parseFields(); 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() { public void postResolve() {
this.flattenImports(); this.flattenImports();
} }
@ -91,7 +103,7 @@ public class SourceFile {
return; return;
} }
List<Import> flat = new ArrayList<>(this.imports.size()); ArrayList<SourceFile> flat = new ArrayList<>(this.imports.size());
for (Import include : this.imports) { for (Import include : this.imports) {
SourceFile file = include.resolution.getFile(); SourceFile file = include.resolution.getFile();
@ -99,7 +111,7 @@ public class SourceFile {
file.flattenImports(); file.flattenImports();
flat.addAll(file.flattenedImports); flat.addAll(file.flattenedImports);
flat.add(include); flat.add(file);
} }
this.flattenedImports = flat.stream() this.flattenedImports = flat.stream()
@ -135,14 +147,8 @@ public class SourceFile {
if (struct != null) return Optional.of(struct); if (struct != null) return Optional.of(struct);
for (Import include : flattenedImports) { for (var include : flattenedImports) {
var file = include.getFile(); var external = include.structs.get(name);
if (file == null) {
continue;
}
var external = file.structs.get(name);
if (external != null) { if (external != null) {
return Optional.of(external); return Optional.of(external);
@ -163,14 +169,8 @@ public class SourceFile {
if (local != null) return Optional.of(local); if (local != null) return Optional.of(local);
for (Import include : imports) { for (var include : flattenedImports) {
var file = include.getFile(); var external = include.functions.get(name);
if (file == null) {
continue;
}
var external = file.functions.get(name);
if (external != null) { if (external != null) {
return Optional.of(external); return Optional.of(external);
@ -184,59 +184,19 @@ public class SourceFile {
return "#use " + '"' + name + '"'; 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() { public String printSource() {
return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers(); return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers();
} }
private CharSequence replaceAndElide(List<Pair<Span, String>> replacements) { private CharSequence elideImports() {
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) {
StringBuilder out = new StringBuilder(); StringBuilder out = new StringBuilder();
int lastEnd = 0; int lastEnd = 0;
replacements.sort(Comparator.comparing(Pair::first)); for (var include : imports) {
var loc = include.self;
for (var replacement : replacements) {
var loc = replacement.first();
if (loc.getSourceFile() != this) {
continue;
}
out.append(this.source, lastEnd, loc.getStartPos()); out.append(this.source, lastEnd, loc.getStartPos());
out.append(replacement.second());
lastEnd = loc.getEndPos(); lastEnd = loc.getEndPos();
} }

View file

@ -53,6 +53,11 @@ public class ErrorBuilder {
String lineNo = matcher.group(2); String lineNo = matcher.group(2);
String msg = matcher.group(3); String msg = matcher.group(3);
Span span = env.getLineSpan(Integer.parseInt(fileId), Integer.parseInt(lineNo)); Span span = env.getLineSpan(Integer.parseInt(fileId), Integer.parseInt(lineNo));
if (span == null) {
return ErrorBuilder.compError("Error in generated code");
}
return ErrorBuilder.compError(msg) return ErrorBuilder.compError(msg)
.pointAtFile(span.getSourceFile()) .pointAtFile(span.getSourceFile())
.pointAt(span, 1); .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 args;
private final Span body; 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) { public ShaderFunction(Span self, Span type, Span name, Span args, Span body) {
super(self); super(self);
@ -56,7 +56,7 @@ public class ShaderFunction extends AbstractShaderElement {
return parameters.get(index).type; return parameters.get(index).type;
} }
public ImmutableList<Variable> getParameters() { public ImmutableList<ShaderVariable> getParameters() {
return parameters; return parameters;
} }
@ -64,12 +64,12 @@ public class ShaderFunction extends AbstractShaderElement {
return type.get(); return type.get();
} }
protected ImmutableList<Variable> parseArguments() { protected ImmutableList<ShaderVariable> parseArguments() {
if (args.isErr() || args.isEmpty()) return ImmutableList.of(); if (args.isErr() || args.isEmpty()) return ImmutableList.of();
Matcher arguments = argument.matcher(args.get()); Matcher arguments = argument.matcher(args.get());
ImmutableList.Builder<Variable> builder = ImmutableList.builder(); ImmutableList.Builder<ShaderVariable> builder = ImmutableList.builder();
while (arguments.find()) { while (arguments.find()) {
Span self = Span.fromMatcher(args, arguments); Span self = Span.fromMatcher(args, arguments);
@ -77,7 +77,7 @@ public class ShaderFunction extends AbstractShaderElement {
Span type = Span.fromMatcher(args, arguments, 2); Span type = Span.fromMatcher(args, arguments, 2);
Span name = Span.fromMatcher(args, arguments, 3); 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(); return builder.build();

View file

@ -2,14 +2,14 @@ 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 Variable extends AbstractShaderElement { public class ShaderVariable extends AbstractShaderElement {
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 Variable(Span self, Span qualifier, Span type, Span name) { public ShaderVariable(Span self, Span qualifier, Span type, Span name) {
super(self); super(self);
this.qualifierSpan = qualifier; this.qualifierSpan = qualifier;
this.type = type; this.type = type;

View file

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

View file

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

View file

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

View file

@ -8,7 +8,9 @@ import com.jozufozu.flywheel.core.source.FileResolution;
public class PosTexNormalVertex implements VertexType { public class PosTexNormalVertex implements VertexType {
public static final BufferLayout FORMAT = BufferLayout.builder() 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(); .build();
@Override @Override

View file

@ -1,15 +1,20 @@
#use "flywheel:api/vertex.glsl" #use "flywheel:api/vertex.glsl"
#use "flywheel:util/quaternion.glsl" #use "flywheel:util/quaternion.glsl"
#use "flywheel:util/types.glsl"
layout(location = FLW_INSTANCE_BASE_INDEX + 0) in ivec2 oriented_light; void flw_transformBoundingSphere(in FlwInstance i, inout vec3 center, inout float radius) {
layout(location = FLW_INSTANCE_BASE_INDEX + 1) in vec4 oriented_color; vec4 rotation = i.rotation;
layout(location = FLW_INSTANCE_BASE_INDEX + 2) in vec3 oriented_pos; vec3 pivot = i.pivot;
layout(location = FLW_INSTANCE_BASE_INDEX + 3) in vec3 oriented_pivot; vec3 pos = i.position;
layout(location = FLW_INSTANCE_BASE_INDEX + 4) in vec4 oriented_rotation;
void flw_instanceVertex() { center = rotateVertexByQuat(center - pivot, rotation) + pivot + pos;
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;
} }
#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:api/vertex.glsl"
#use "flywheel:util/types.glsl"
layout(location = FLW_INSTANCE_BASE_INDEX + 0) in ivec2 transformed_light; void flw_transformBoundingSphere(in FlwInstance i, inout vec3 center, inout float radius) {
layout(location = FLW_INSTANCE_BASE_INDEX + 1) in vec4 transformed_color; mat4 pose = i.pose;
layout(location = FLW_INSTANCE_BASE_INDEX + 2) in mat4 transformed_pose; center = (pose * vec4(center, 1.0)).xyz;
layout(location = FLW_INSTANCE_BASE_INDEX + 6) in mat3 transformed_normal;
void flw_instanceVertex() { float scale = max(length(pose[0].xyz), max(length(pose[1].xyz), length(pose[2].xyz)));
flw_vertexPos = transformed_pose * flw_vertexPos; radius *= scale;
flw_vertexNormal = transformed_normal * flw_vertexNormal;
flw_vertexColor = transformed_color;
flw_vertexLight = transformed_light / 15.0;
} }
#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 // populated by instancers
layout(std430, binding = 0) restrict readonly buffer ObjectBuffer { layout(std430, binding = 0) restrict readonly buffer ObjectBuffer {
FLW_INSTANCE_STRUCT objects[]; FlwPackedInstance objects[];
}; };
layout(std430, binding = 1) restrict writeonly buffer TargetBuffer { layout(std430, binding = 1) restrict writeonly buffer TargetBuffer {
@ -45,7 +45,9 @@ bool isVisible() {
vec3 center; vec3 center;
float radius; float radius;
unpackBoundingSphere(sphere, center, 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); return testSphere(center, radius);
} }

View file

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

View file

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

View file

@ -32,6 +32,10 @@ struct BoundingSphere {
float radius; float radius;
}; };
struct LightCoord {
uint p;
};
vec3 unpackVec3F(in Vec3F v) { vec3 unpackVec3F(in Vec3F v) {
return vec3(v.x, v.y, v.z); 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); center = unpackVec3F(sphere.center);
radius = sphere.radius; radius = sphere.radius;
} }
vec2 unpackLightCoord(in LightCoord light) {
return vec2(float((light.p >> 16) & 0xFFFFu), float(light.p & 0xFFFFu)) / 15.0;
}