Condensing culling

- Adapt compilers to work with arbitrary instance types
 - Use single compiler for all draw shaders
 - Temp solution for instanced array attributes
 - Introduce pipeline shaders
This commit is contained in:
Jozufozu 2022-08-09 15:38:56 -07:00
parent a9ee85ff76
commit e7339dc7ef
24 changed files with 299 additions and 392 deletions

View file

@ -16,7 +16,7 @@ import com.jozufozu.flywheel.core.DebugRender;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.core.StitchedSprite;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysCompiler;
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.core.model.Models;
import com.jozufozu.flywheel.event.EntityWorldHandler;
@ -80,7 +80,7 @@ public class Flywheel {
forgeEventBus.addListener(FlwCommands::registerClientCommands);
forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onRendererReload);
forgeEventBus.addListener(InstancedArraysCompiler::invalidateAll);
forgeEventBus.addListener(PipelineCompiler::invalidateAll);
forgeEventBus.addListener(Models::onReload);
forgeEventBus.addListener(MeshPool::reset);
forgeEventBus.addListener(CrumblingRenderer::onReloadRenderers);

View file

@ -0,0 +1,8 @@
package com.jozufozu.flywheel.api.pipeline;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.core.source.FileResolution;
public record PipelineShader(GLSLVersion glslVersion, FileResolution vertex, FileResolution fragment) {
}

View file

@ -6,10 +6,10 @@ 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.instancing.InstancedArraysCompiler;
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
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;
@ -69,8 +69,8 @@ public class Loader implements ResourceManagerReloadListener {
for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) {
for (ContextShader contextShader : ComponentRegistry.contextShaders) {
var ctx = new InstancedArraysCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader);
InstancedArraysCompiler.INSTANCE.getProgram(ctx);
var ctx = new PipelineCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader, Components.INSTANCED_ARRAYS);
PipelineCompiler.INSTANCE.getProgram(ctx);
}
}
}

View file

@ -0,0 +1,154 @@
package com.jozufozu.flywheel.backend.instancing;
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.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.compile.*;
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;
/**
* A caching compiler.
*
* <p>
* This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of
* compiled programs, and will only compile a program if it is not already in the cache.
* </p>
* <p>
* A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload.
* </p>
*/
public class PipelineCompiler extends Memoizer<PipelineCompiler.Context, GlProgram> {
public static final PipelineCompiler INSTANCE = new PipelineCompiler();
private final ShaderCompiler shaderCompiler;
private PipelineCompiler() {
this.shaderCompiler = new ShaderCompiler();
}
/**
* Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec.
*
* @param ctx The context of compilation.
* @return A compiled GlProgram.
*/
public GlProgram getProgram(PipelineCompiler.Context ctx) {
return super.get(ctx);
}
@Override
public void invalidate() {
super.invalidate();
shaderCompiler.invalidate();
}
@Override
protected GlProgram _create(PipelineCompiler.Context ctx) {
var glslVersion = ctx.pipelineShader()
.glslVersion();
var vertex = new ShaderCompiler.Context(glslVersion, ShaderType.VERTEX, ctx.getVertexComponents());
var fragment = new ShaderCompiler.Context(glslVersion, ShaderType.FRAGMENT, ctx.getFragmentComponents());
return new ProgramAssembler(ctx.instanceShader.getFileLoc())
.attachShader(shaderCompiler.get(vertex))
.attachShader(shaderCompiler.get(fragment))
.link()
.build(ctx.contextShader.factory());
}
@Override
protected void _destroy(GlProgram value) {
value.delete();
}
public static void invalidateAll(ReloadRenderersEvent ignored) {
INSTANCE.invalidate();
}
/**
* Represents the entire context of a program's usage.
*
* @param vertexType The vertexType the program should be adapted for.
* @param material The material shader to use.
* @param instanceShader The instance shader to use.
* @param contextShader The context shader to use.
*/
public record Context(VertexType vertexType, Material material, FileResolution instanceShader,
ContextShader contextShader, PipelineShader pipelineShader) {
ImmutableList<SourceFile> getVertexComponents() {
return ImmutableList.of(vertexType.getLayoutShader().getFile(), instanceShader.getFile(), material.getVertexShader().getFile(),
contextShader.getVertexShader(), pipelineShader.vertex().getFile());
}
ImmutableList<SourceFile> getFragmentComponents() {
return ImmutableList.of(material.getFragmentShader().getFile(), contextShader.getFragmentShader(),
pipelineShader.fragment().getFile());
}
}
/**
* Handles compilation and deletion of vertex shaders.
*/
public static class ShaderCompiler extends Memoizer<ShaderCompiler.Context, GlShader> {
public ShaderCompiler() {
}
@Override
protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(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");
var ctx = new CompilationContext();
var names = ImmutableList.<ResourceLocation>builder();
for (var file : key.components) {
finalSource.append(file.generateFinalSource(ctx));
names.add(file.name);
}
try {
return new GlShader(finalSource.toString(), key.shaderType, names.build());
} catch (ShaderCompilationException e) {
throw e.withErrorLog(ctx);
}
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
/**
* @param glslVersion The GLSL version to use.
* @param components A list of shader components to stitch together, in order.
*/
public record Context(GLSLVersion glslVersion, ShaderType shaderType, List<SourceFile> components) {
public String generateHeader() {
return CompileUtil.generateHeader(glslVersion, shaderType);
}
}
}
}

View file

@ -5,6 +5,7 @@ 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.compile.CompileUtil;
import com.jozufozu.flywheel.core.compile.Memoizer;
import com.jozufozu.flywheel.core.compile.ProgramAssembler;
@ -23,14 +24,18 @@ public class ComputeCullerCompiler extends Memoizer<FileResolution, GlProgram> {
@Override
protected GlProgram _create(FileResolution file) {
var finalSource = new StringBuilder();
CompilationContext context = new CompilationContext();
var header = CompileUtil.generateHeader(GLSLVersion.V460, ShaderType.COMPUTE);
String source = file.getFile()
.generateFinalSource(context);
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V460, ShaderType.COMPUTE));
finalSource.append(file.getFile()
.generateFinalSource(context));
finalSource.append(Components.Pipeline.INDIRECT_CULL.getFile()
.generateFinalSource(context));
try {
var shader = new GlShader(header + source, ShaderType.COMPUTE, ImmutableList.of(file.getFileLoc()));
var shader = new GlShader(finalSource.toString(), ShaderType.COMPUTE, ImmutableList.of(file.getFileLoc()));
return new ProgramAssembler(file.getFileLoc()).attachShader(shader)
.link()

View file

@ -1,65 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.indirect;
import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.WorldProgram;
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.ShaderLoadingException;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
public class IndirectDrawCompiler extends Memoizer<IndirectDrawCompiler.Program, GlProgram> {
public static final IndirectDrawCompiler INSTANCE = new IndirectDrawCompiler();
private IndirectDrawCompiler() {
}
@Override
protected GlProgram _create(IndirectDrawCompiler.Program program) {
GlShader vertexShader = compile(program.vertex.getFile(), ShaderType.VERTEX);
GlShader fragmentShader = compile(program.fragment.getFile(), ShaderType.FRAGMENT);
return new ProgramAssembler(program.vertex.getFileLoc())
.attachShader(vertexShader)
.attachShader(fragmentShader)
.link()
.build(WorldProgram::new);
}
@NotNull
private static GlShader compile(SourceFile file, ShaderType type) {
var context = new CompilationContext();
try {
var header = CompileUtil.generateHeader(GLSLVersion.V460, type);
var source = file.generateFinalSource(context);
return new GlShader(header + source, type, ImmutableList.of(file.name));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(context);
}
}
@Override
protected void _destroy(GlProgram value) {
value.delete();
}
public static void invalidateAll(ReloadRenderersEvent ignored) {
INSTANCE.invalidate();
}
public record Program(FileResolution vertex, FileResolution fragment) {
}
}

View file

@ -19,7 +19,7 @@ import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancedArraysCompiler;
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
import com.jozufozu.flywheel.util.WeakHashSet;
@ -91,20 +91,6 @@ public class IndirectEngine implements Engine {
RenderSystem.enableCull();
}
protected void setup(ShaderState desc) {
VertexType vertexType = desc.vertex();
FileResolution instanceShader = desc.instance()
.getInstanceShader();
Material material = desc.material();
var ctx = new InstancedArraysCompiler.Context(vertexType, material, instanceShader, context);
InstancedArraysCompiler.INSTANCE.getProgram(ctx)
.bind();
UniformBuffer.getInstance().sync();
}
public void clearAll() {
factories.values().forEach(IndirectFactory::clear);
}

View file

@ -12,6 +12,7 @@ import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StorageBufferWriter;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.QuadConverter;
@ -21,7 +22,7 @@ import com.jozufozu.flywheel.core.vertex.Formats;
public class IndirectList<T extends InstancedPart> {
private static final long DRAW_COMMAND_STRIDE = 48;
private static final long DRAW_COMMAND_STRIDE = 36;
private static final long DRAW_COMMAND_OFFSET = 0;
final StorageBufferWriter<T> storageBufferWriter;
@ -73,18 +74,20 @@ public class IndirectList<T extends InstancedPart> {
meshPool = new IndirectMeshPool(Formats.BLOCK, 1024);
// FIXME: Resizable buffers
maxObjectCount = 1024L;
maxObjectCount = 64 * 64 * 64;
maxBatchCount = 64;
objectStride = storageBufferWriter.getAlignment();
glNamedBufferStorage(objectBuffer, objectStride * maxObjectCount, GL_DYNAMIC_STORAGE_BIT);
glNamedBufferStorage(targetBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT);
glNamedBufferStorage(batchBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT);
int persistentBits = GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT;
glNamedBufferStorage(objectBuffer, objectStride * maxObjectCount, persistentBits);
glNamedBufferStorage(targetBuffer, 4 * maxObjectCount, 0);
glNamedBufferStorage(batchBuffer, 4 * maxObjectCount, persistentBits);
glNamedBufferStorage(drawBuffer, DRAW_COMMAND_STRIDE * maxBatchCount, GL_DYNAMIC_STORAGE_BIT);
glNamedBufferStorage(debugBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT);
glNamedBufferStorage(debugBuffer, 4 * maxObjectCount, 0);
objectClientStorage = MemoryUtil.nmemAlloc(objectStride * maxObjectCount);
batchIDClientStorage = MemoryUtil.nmemAlloc(4 * maxObjectCount);
int mapBits = persistentBits | GL_MAP_FLUSH_EXPLICIT_BIT;
objectClientStorage = nglMapNamedBufferRange(objectBuffer, 0, objectStride * maxObjectCount, mapBits);
batchIDClientStorage = nglMapNamedBufferRange(batchBuffer, 0, 4 * maxObjectCount, mapBits);
vertexArray = glCreateVertexArrays();
@ -92,8 +95,8 @@ public class IndirectList<T extends InstancedPart> {
.quads2Tris(2048).buffer.handle();
setupVertexArray();
compute = ComputeCullerCompiler.INSTANCE.get(Components.Files.CULL_INSTANCES);
draw = IndirectDrawCompiler.INSTANCE.get(new IndirectDrawCompiler.Program(Components.Files.DRAW_INDIRECT_VERTEX, Components.Files.DRAW_INDIRECT_FRAGMENT));
compute = ComputeCullerCompiler.INSTANCE.get(Components.Files.ORIENTED_INDIRECT);
draw = PipelineCompiler.INSTANCE.get(new PipelineCompiler.Context(Formats.BLOCK, Materials.BELL, Components.Files.ORIENTED_INDIRECT, Components.WORLD, Components.INDIRECT));
}
private void setupVertexArray() {
@ -184,8 +187,8 @@ public class IndirectList<T extends InstancedPart> {
batchID++;
}
nglNamedBufferSubData(objectBuffer, 0, objectPtr - objectClientStorage, objectClientStorage);
nglNamedBufferSubData(batchBuffer, 0, batchIDPtr - batchIDClientStorage, batchIDClientStorage);
glFlushMappedNamedBufferRange(objectBuffer, 0, objectPtr - objectClientStorage);
glFlushMappedNamedBufferRange(batchBuffer, 0, batchIDPtr - batchIDClientStorage);
}
private void uploadIndirectCommands() {
@ -229,7 +232,7 @@ public class IndirectList<T extends InstancedPart> {
MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex
MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance
boundingSphere.getToAddress(ptr + 32); // boundingSphere
boundingSphere.getToAddress(ptr + 20); // boundingSphere
}
}
}

View file

@ -1,252 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.ArrayList;
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.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.compile.*;
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.core.source.parse.ShaderField;
import com.jozufozu.flywheel.core.source.span.Span;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.util.Pair;
/**
* A caching compiler.
*
* <p>
* This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of
* compiled programs, and will only compile a program if it is not already in the cache.
* </p>
* <p>
* A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload.
* </p>
*/
public class InstancedArraysCompiler extends Memoizer<InstancedArraysCompiler.Context, GlProgram> {
public static final InstancedArraysCompiler INSTANCE = new InstancedArraysCompiler();
private final VertexCompiler vertexCompiler;
private final FragmentCompiler fragmentCompiler;
private InstancedArraysCompiler() {
this.vertexCompiler = new VertexCompiler();
this.fragmentCompiler = new FragmentCompiler();
}
/**
* Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec.
*
* @param ctx The context of compilation.
* @return A compiled GlProgram.
*/
public GlProgram getProgram(InstancedArraysCompiler.Context ctx) {
return super.get(ctx);
}
@Override
public void invalidate() {
super.invalidate();
vertexCompiler.invalidate();
fragmentCompiler.invalidate();
}
@Override
protected GlProgram _create(InstancedArraysCompiler.Context ctx) {
// TODO: try-catch here to prevent crashing if shaders failed to compile
Material material = ctx.material;
FileResolution instanceShader = ctx.instanceShader();
ContextShader contextShader = ctx.contextShader;
var vertex = new VertexCompiler.Context(ctx.vertexType(), instanceShader.getFile(), material.getVertexShader().getFile(),
contextShader.getVertexShader());
var fragment = new FragmentCompiler.Context(material.getFragmentShader().getFile(), contextShader.getFragmentShader());
return new ProgramAssembler(instanceShader.getFileLoc())
.attachShader(vertexCompiler.get(vertex))
.attachShader(fragmentCompiler.get(fragment))
.link()
.build(contextShader.factory());
}
@Override
protected void _destroy(GlProgram value) {
value.delete();
}
public static void invalidateAll(ReloadRenderersEvent ignored) {
INSTANCE.invalidate();
}
/**
* Represents the entire context of a program's usage.
*
* @param vertexType The vertexType the program should be adapted for.
* @param material The material shader to use.
* @param instanceShader The instance shader to use.
* @param contextShader The context shader to use.
*/
public record Context(VertexType vertexType, Material material, FileResolution instanceShader,
ContextShader contextShader) {
}
/**
* Handles compilation and deletion of vertex shaders.
*/
public static class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
public VertexCompiler() {
}
@Override
protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX));
finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n");
var index = new CompilationContext();
// LAYOUT
var layoutShader = key.vertexType.getLayoutShader().getFile();
finalSource.append(layoutShader.generateFinalSource(index));
// INSTANCE
int attributeBaseIndex = key.vertexType.getLayout()
.getAttributeCount();
var instanceShader = key.instanceShader;
var replacements = new ArrayList<Pair<Span, String>>();
for (ShaderField field : instanceShader.fields.values()) {
if (field.decoration != ShaderField.Decoration.IN) {
continue;
}
int location = Integer.parseInt(field.location.get());
int newLocation = location + attributeBaseIndex;
replacements.add(Pair.of(field.location, Integer.toString(newLocation)));
}
finalSource.append(instanceShader.generateFinalSource(index, replacements));
// MATERIAL
var materialShader = key.materialShader;
finalSource.append(materialShader.generateFinalSource(index));
// CONTEXT
var contextShaderSource = key.contextShader;
finalSource.append(contextShaderSource.generateFinalSource(index));
// MAIN
finalSource.append("""
void main() {
flw_layoutVertex();
flw_instanceVertex();
flw_materialVertex();
flw_contextVertex();
}
""");
try {
return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(index);
}
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
/**
* @param vertexType The vertex type to use.
* @param instanceShader The instance shader source.
* @param materialShader The vertex material shader source.
* @param contextShader The context shader source.
*/
public record Context(VertexType vertexType, SourceFile instanceShader, SourceFile materialShader, SourceFile contextShader) {
}
}
/**
* Handles compilation and deletion of fragment shaders.
*/
public static class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShader> {
public FragmentCompiler() {
}
@Override
protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.FRAGMENT));
finalSource.append("#extension GL_ARB_conservative_depth : enable\n");
var ctx = new CompilationContext();
// MATERIAL
SourceFile materialShader = key.materialShader;
finalSource.append(materialShader.generateFinalSource(ctx));
// CONTEXT
SourceFile contextShaderSource = key.contextShader;
finalSource.append(contextShaderSource.generateFinalSource(ctx));
// MAIN
finalSource.append(generateFooter());
try {
return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(ctx);
}
}
protected String generateFooter() {
return """
void main() {
flw_initFragment();
flw_materialFragment();
flw_contextFragment();
}
""";
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
/**
* Represents the conditions under which a shader is compiled.
*
* @param materialShader The fragment material shader source.
*/
public record Context(SourceFile materialShader, SourceFile contextShader) {
}
}
}

View file

@ -17,7 +17,9 @@ 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;
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;
@ -136,9 +138,9 @@ public class InstancingEngine implements Engine {
.getInstanceShader();
Material material = desc.material();
var ctx = new InstancedArraysCompiler.Context(vertexType, material, instanceShader, context);
var ctx = new PipelineCompiler.Context(vertexType, material, instanceShader, context, Components.INSTANCED_ARRAYS);
InstancedArraysCompiler.INSTANCE.getProgram(ctx)
PipelineCompiler.INSTANCE.getProgram(ctx)
.bind();
UniformBuffer.getInstance().sync();
}

View file

@ -4,6 +4,8 @@ import java.util.function.BiConsumer;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.pipeline.PipelineShader;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceChecks;
@ -27,6 +29,9 @@ 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 void init() {
Files.init();
Formats.init();
@ -34,6 +39,18 @@ public class Components {
Materials.init();
}
public static class Pipeline {
public static final FileResolution DRAW_FRAGMENT = pipeline("pipeline/draw.frag");
public static final FileResolution INSTANCED_ARRAYS_DRAW = pipeline("pipeline/instanced_arrays_draw.vert");
public static final FileResolution INDIRECT_DRAW = pipeline("pipeline/indirect_draw.vert");
public static final FileResolution INDIRECT_CULL = pipeline("pipeline/indirect_cull.glsl");
private static FileResolution pipeline(String name) {
return FileResolution.get(Flywheel.rl(name))
.validateWith(Checks.PIPELINE);
}
}
public static class Files {
public static final FileResolution VIEW_UNIFORMS = uniform(Flywheel.rl("uniform/view.glsl"));
@ -42,7 +59,9 @@ 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"));
@ -51,9 +70,6 @@ public class Components {
public static final FileResolution WORLD_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.WORLD, ".frag"));
public static final FileResolution CRUMBLING_VERTEX = contextVertex(ResourceUtil.subPath(Names.CRUMBLING, ".vert"));
public static final FileResolution CRUMBLING_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.CRUMBLING, ".frag"));
public static final FileResolution CULL_INSTANCES = compute(Flywheel.rl("compute/cull_instances.glsl"));
public static final FileResolution DRAW_INDIRECT_VERTEX = FileResolution.get(ResourceUtil.subPath(Names.DRAW_INDIRECT, ".vert"));
public static final FileResolution DRAW_INDIRECT_FRAGMENT = FileResolution.get(ResourceUtil.subPath(Names.DRAW_INDIRECT, ".frag"));
private static FileResolution compute(ResourceLocation rl) {
return FileResolution.get(rl);
@ -69,8 +85,7 @@ public class Components {
}
private static FileResolution instanceVertex(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.INSTANCE_VERTEX);
return FileResolution.get(location); // .validateWith(Checks.INSTANCE_VERTEX);
}
private static FileResolution materialVertex(ResourceLocation location) {
@ -100,12 +115,16 @@ public class Components {
public static class Checks {
public static final BiConsumer<ErrorReporter, SourceFile> LAYOUT_VERTEX = SourceChecks.checkFunctionArity("flw_layoutVertex", 0);
public static final BiConsumer<ErrorReporter, SourceFile> INSTANCE_VERTEX = SourceChecks.checkFunctionArity("flw_instanceVertex", 0);
public static final BiConsumer<ErrorReporter, SourceFile> LAYOUT_VERTEX = SourceChecks.checkFunctionArity("flw_layoutVertex", 0)
.andThen(SourceChecks.checkDefine("FLW_INSTANCE_BASE_INDEX"));
public static final BiConsumer<ErrorReporter, SourceFile> INSTANCE_VERTEX = SourceChecks.checkFunctionParameterTypeExists("flw_instanceVertex", 1, 0)
.andThen(SourceChecks.checkDefine("FLW_INSTANCE_STRUCT"));
public static final BiConsumer<ErrorReporter, SourceFile> MATERIAL_VERTEX = SourceChecks.checkFunctionArity("flw_materialVertex", 0);
public static final BiConsumer<ErrorReporter, SourceFile> MATERIAL_FRAGMENT = SourceChecks.checkFunctionArity("flw_materialFragment", 0);
public static final BiConsumer<ErrorReporter, SourceFile> CONTEXT_VERTEX = SourceChecks.checkFunctionArity("flw_contextVertex", 0);
public static final BiConsumer<ErrorReporter, SourceFile> CONTEXT_FRAGMENT = SourceChecks.checkFunctionArity("flw_contextFragment", 0).andThen(SourceChecks.checkFunctionArity("flw_initFragment", 0));
public static final BiConsumer<ErrorReporter, SourceFile> PIPELINE = SourceChecks.checkFunctionArity("main", 0);
}
public static class Names {

View file

@ -162,4 +162,25 @@ public class FileResolution {
public String toString() {
return "FileResolution[" + fileLoc + "]";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FileResolution that = (FileResolution) o;
return fileLoc.equals(that.fileLoc);
}
@Override
public int hashCode() {
// FileResolutions are interned and therefore can be hashed based on object identity.
// Overriding this to make it explicit.
return System.identityHashCode(this);
}
}

View file

@ -54,4 +54,12 @@ public class SourceChecks {
return func;
}
public static BiConsumer<? super ErrorReporter, ? super SourceFile> checkDefine(String define) {
return (errorReporter, file) -> {
// if (!file.hasDefine(define)) {
// errorReporter.generateMissingDefine(file, define, "\"" + define + "\" define not defined");
// }
};
}
}

View file

@ -185,10 +185,7 @@ public class SourceFile {
}
public String generateFinalSource(CompilationContext context) {
return generateFinalSource(context, Collections.emptyList());
}
public String generateFinalSource(CompilationContext context, List<Pair<Span, String>> replacements) {
List<Pair<Span, String>> replacements = Collections.emptyList();
var out = new StringBuilder();
for (Import include : flattenedImports) {
SourceFile file = include.getFile();

View file

@ -1,11 +1,11 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/quaternion.glsl"
layout(location = 0) in ivec2 oriented_light;
layout(location = 1) in vec4 oriented_color;
layout(location = 2) in vec3 oriented_pos;
layout(location = 3) in vec3 oriented_pivot;
layout(location = 4) in vec4 oriented_rotation;
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_instanceVertex() {
flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - oriented_pivot, oriented_rotation) + oriented_pivot + oriented_pos, 1.0);

View file

@ -1,7 +1,7 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/quaternion.glsl"
#define FLW_INSTANCE_STRUCT Instance
#define FLW_INSTANCE_STRUCT Instance
struct Instance {
vec4 rotation;
vec3 pos;

View file

@ -1,9 +1,9 @@
#use "flywheel:api/vertex.glsl"
layout(location = 0) in ivec2 transformed_light;
layout(location = 1) in vec4 transformed_color;
layout(location = 2) in mat4 transformed_pose;
layout(location = 6) in mat3 transformed_normal;
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_instanceVertex() {
flw_vertexPos = transformed_pose * flw_vertexPos;

View file

@ -5,6 +5,7 @@ layout(location = 1) in vec4 _flw_v_color;
layout(location = 2) in vec2 _flw_v_texCoord;
layout(location = 3) in ivec2 _flw_v_light;
layout(location = 4) in vec3 _flw_v_normal;
#define FLW_INSTANCE_BASE_INDEX 5
void flw_layoutVertex() {
flw_vertexPos = vec4(_flw_v_pos, 1.0);

View file

@ -3,6 +3,7 @@
layout(location = 0) in vec3 _flw_v_pos;
layout(location = 1) in vec2 _flw_v_texCoord;
layout(location = 2) in vec3 _flw_v_normal;
#define FLW_INSTANCE_BASE_INDEX 3
void flw_layoutVertex() {
flw_vertexPos = vec4(_flw_v_pos, 1.0);

View file

@ -1,6 +1,4 @@
#use "flywheel:api/fragment.glsl"
#use "flywheel:context/world.frag"
#use "flywheel:material/default.frag"
void main() {
flw_initFragment();

View file

@ -1,9 +1,8 @@
#define FLW_SUBGROUP_SIZE 32
layout(local_size_x = FLW_SUBGROUP_SIZE) in;
#use "flywheel:api/cull.glsl"
#use "flywheel:util/quaternion.glsl"
#use "flywheel:uniform/frustum.glsl"
#use "flywheel:instance/oriented_indirect.glsl"
#use "flywheel:util/types.glsl"
struct MeshDrawCommand {
uint indexCount;
@ -12,7 +11,7 @@ struct MeshDrawCommand {
uint vertexOffset;
uint baseInstance;
vec4 boundingSphere;
BoundingSphere boundingSphere;
};
// populated by instancers
@ -54,10 +53,11 @@ bool testSphere(vec3 center, float radius) {
}
bool isVisible() {
vec4 sphere = drawCommands[flw_batchID].boundingSphere;
BoundingSphere sphere = drawCommands[flw_batchID].boundingSphere;
vec3 center = sphere.xyz;
float radius = sphere.r;
vec3 center;
float radius;
unpackBoundingSphere(sphere, center, radius);
flw_transformBoundingSphere(objects[flw_objectID], center, radius);
return testSphere(center, radius);

View file

@ -1,10 +1,5 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:layout/block.vert"
#use "flywheel:context/world.vert"
#use "flywheel:util/quaternion.glsl"
#use "flywheel:instance/oriented_indirect.glsl"
// populated by instancers
layout(std430, binding = 0) restrict readonly buffer ObjectBuffer {
FLW_INSTANCE_STRUCT objects[];
};
@ -16,7 +11,8 @@ layout(std430, binding = 1) restrict readonly buffer TargetBuffer {
void main() {
uint instanceIndex = objectIDs[gl_BaseInstance + gl_InstanceID];
flw_layoutVertex();
Instance i = objects[instanceIndex];
FLW_INSTANCE_STRUCT i = objects[instanceIndex];
flw_instanceVertex(i);
flw_materialVertex();
flw_contextVertex();
}

View file

@ -0,0 +1,8 @@
#use "flywheel:api/vertex.glsl"
void main() {
flw_layoutVertex();
flw_instanceVertex();
flw_materialVertex();
flw_contextVertex();
}

View file

@ -0,0 +1,17 @@
struct Vec3F {
float x;
float y;
float z;
};
// 4-aligned instead of a 16-aligned vec4
struct BoundingSphere {
Vec3F center;
float radius;
};
void unpackBoundingSphere(in BoundingSphere sphere, out vec3 center, out float radius) {
center = vec3(sphere.center.x, sphere.center.y, sphere.center.z);
radius = sphere.radius;
}