Indirectly materialized

- Pass material IDs through the indirect draw buffer
- Turns out buffers can be bound to both storage and draw indirect
- Use indexOf in ComponentRegistry to determine IDs
- Remove World/Crumbling Program and move sampler binding to
  ContextShader setup
This commit is contained in:
Jozufozu 2023-03-25 17:54:34 -07:00
parent d197fe0a4f
commit 80001f0037
15 changed files with 76 additions and 128 deletions

View file

@ -4,7 +4,7 @@ import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
public interface Context {
void setup(GlProgram program);
void onProgramLink(GlProgram program);
FileResolution vertexShader();

View file

@ -3,11 +3,14 @@ package com.jozufozu.flywheel.api.context;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
public record ContextShader(GlProgram.Factory factory, FileResolution vertexShader,
FileResolution fragmentShader) implements Context {
public record ContextShader(FileResolution vertexShader, FileResolution fragmentShader) implements Context {
@Override
public void setup(GlProgram program) {
public void onProgramLink(GlProgram program) {
program.bind();
program.setSamplerBinding("flw_diffuseTex", 0);
program.setSamplerBinding("flw_overlayTex", 1);
program.setSamplerBinding("flw_lightTex", 2);
GlProgram.unbind();
}
@Override

View file

@ -2,8 +2,6 @@ package com.jozufozu.flywheel.backend.gl.shader;
import static org.lwjgl.opengl.GL20.*;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlObject;
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
@ -15,7 +13,6 @@ public class GlProgram extends GlObject {
setHandle(handle);
}
// TODO: Programs bind the uniform buffers they need, no more GlProgram inheritance
public void bind() {
// TODO: bind textures?
UniformBuffer.getInstance()
@ -48,17 +45,13 @@ public class GlProgram extends GlObject {
*
* @param name The name of the sampler uniform.
* @param binding The index of the texture unit.
* @return The sampler uniform's index.
* @throws NullPointerException If no uniform exists with the given name.
*/
public int setSamplerBinding(String name, int binding) {
public void setSamplerBinding(String name, int binding) {
int samplerUniform = getUniformLocation(name);
if (samplerUniform >= 0) {
glUniform1i(samplerUniform, binding);
}
return samplerUniform;
}
@Override
@ -66,11 +59,4 @@ public class GlProgram extends GlObject {
glDeleteProgram(handle);
}
/**
* A factory interface to create a {@link GlProgram}.
*/
public interface Factory {
@NotNull GlProgram create(int handle);
}
}

View file

@ -145,7 +145,7 @@ public class FlwCompiler {
var glProgram = link(vertex.handle(), fragment.handle());
ctx.contextShader()
.setup(glProgram);
.onProgramLink(glProgram);
pipelinePrograms.put(ctx, glProgram);
}

View file

@ -1,22 +1,7 @@
package com.jozufozu.flywheel.backend.instancing.indirect;
import static org.lwjgl.opengl.GL45.glCreateBuffers;
import static org.lwjgl.opengl.GL46.GL_DRAW_INDIRECT_BUFFER;
import static org.lwjgl.opengl.GL46.GL_DYNAMIC_STORAGE_BIT;
import static org.lwjgl.opengl.GL46.GL_MAP_FLUSH_EXPLICIT_BIT;
import static org.lwjgl.opengl.GL46.GL_MAP_PERSISTENT_BIT;
import static org.lwjgl.opengl.GL46.GL_MAP_WRITE_BIT;
import static org.lwjgl.opengl.GL46.GL_SHADER_STORAGE_BUFFER;
import static org.lwjgl.opengl.GL46.glBindBuffer;
import static org.lwjgl.opengl.GL46.glCopyNamedBufferSubData;
import static org.lwjgl.opengl.GL46.glDeleteBuffers;
import static org.lwjgl.opengl.GL46.glFlushMappedNamedBufferRange;
import static org.lwjgl.opengl.GL46.glNamedBufferStorage;
import static org.lwjgl.opengl.GL46.nglBindBuffersRange;
import static org.lwjgl.opengl.GL46.nglCreateBuffers;
import static org.lwjgl.opengl.GL46.nglDeleteBuffers;
import static org.lwjgl.opengl.GL46.nglMapNamedBufferRange;
import static org.lwjgl.opengl.GL46.nglNamedBufferSubData;
import static org.lwjgl.opengl.GL46.*;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.system.Pointer;
@ -30,7 +15,7 @@ public class IndirectBuffers {
public static final long PTR_SIZE = Pointer.POINTER_SIZE;
// DRAW COMMAND
public static final long DRAW_COMMAND_STRIDE = 36;
public static final long DRAW_COMMAND_STRIDE = 44;
public static final long DRAW_COMMAND_OFFSET = 0;
// BITS
@ -180,15 +165,16 @@ public class IndirectBuffers {
FlwMemoryTracker._freeGPUMemory(maxDrawCount * DRAW_COMMAND_STRIDE);
}
public void bindAll() {
bindN(BUFFER_COUNT);
public void bindForCompute() {
multiBind(BUFFER_COUNT);
}
public void bindObjectAndTarget() {
bindN(2);
public void bindForDraw() {
multiBind(BUFFER_COUNT);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, draw);
}
private void bindN(int bufferCount) {
private void multiBind(int bufferCount) {
if (bufferCount > BUFFER_COUNT) {
throw new IllegalArgumentException("Can't bind more than " + BUFFER_COUNT + " buffers");
}
@ -197,10 +183,6 @@ public class IndirectBuffers {
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, bufferCount, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
}
void bindIndirectBuffer() {
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, draw);
}
void flushBatchIDs(long length) {
glFlushMappedNamedBufferRange(batch, 0, length);
}

View file

@ -3,18 +3,11 @@ package com.jozufozu.flywheel.backend.instancing.indirect;
import static org.lwjgl.opengl.GL42.GL_COMMAND_BARRIER_BIT;
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT;
import static org.lwjgl.opengl.GL46.glBindVertexArray;
import static org.lwjgl.opengl.GL46.glCreateVertexArrays;
import static org.lwjgl.opengl.GL46.glDeleteVertexArrays;
import static org.lwjgl.opengl.GL46.glDispatchCompute;
import static org.lwjgl.opengl.GL46.glEnableVertexArrayAttrib;
import static org.lwjgl.opengl.GL46.glVertexArrayElementBuffer;
import static org.lwjgl.opengl.GL46.glVertexArrayVertexBuffer;
import static org.lwjgl.opengl.GL46.*;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.instancing.compile.FlwCompiler;
@ -26,7 +19,6 @@ public class IndirectCullingGroup<T extends InstancedPart> {
private static final int BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT;
final StructWriter<T> storageBufferWriter;
final GlProgram compute;
final GlProgram draw;
private final VertexType vertexType;
@ -47,7 +39,6 @@ public class IndirectCullingGroup<T extends InstancedPart> {
IndirectCullingGroup(StructType<T> structType, VertexType vertexType) {
this.vertexType = vertexType;
storageBufferWriter = structType.getWriter();
objectStride = structType.getLayout()
.getStride();
@ -116,7 +107,7 @@ public class IndirectCullingGroup<T extends InstancedPart> {
uploadIndirectCommands();
compute.bind();
buffers.bindAll();
buffers.bindForCompute();
var groupCount = (instanceCountThisFrame + 31) >> 5; // ceil(instanceCount / 32)
glDispatchCompute(groupCount, 1, 1);
@ -130,8 +121,7 @@ public class IndirectCullingGroup<T extends InstancedPart> {
draw.bind();
glBindVertexArray(vertexArray);
buffers.bindObjectAndTarget();
buffers.bindIndirectBuffer();
buffers.bindForDraw();
memoryBarrier();

View file

@ -4,6 +4,7 @@ import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.core.ComponentRegistry;
public final class IndirectDraw<T extends InstancedPart> {
final IndirectInstancer<T> instancer;
@ -11,12 +12,18 @@ public final class IndirectDraw<T extends InstancedPart> {
final Material material;
int baseInstance = -1;
final int vertexMaterialID;
final int fragmentMaterialID;
boolean needsFullWrite = true;
IndirectDraw(IndirectInstancer<T> instancer, Material material, IndirectMeshPool.BufferedMesh mesh) {
this.instancer = instancer;
this.material = material;
this.mesh = mesh;
this.vertexMaterialID = ComponentRegistry.materials.getVertexID(material);
this.fragmentMaterialID = ComponentRegistry.materials.getFragmentID(material);
}
public void prepare(int baseInstance) {
@ -48,5 +55,8 @@ public final class IndirectDraw<T extends InstancedPart> {
MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance
boundingSphere.getToAddress(ptr + 20); // boundingSphere
MemoryUtil.memPutInt(ptr + 36, vertexMaterialID); // vertexMaterialID
MemoryUtil.memPutInt(ptr + 40, fragmentMaterialID); // fragmentMaterialID
}
}

View file

@ -10,6 +10,7 @@ import java.util.List;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.util.Textures;
public class IndirectDrawSet<T extends InstancedPart> {
@ -37,6 +38,7 @@ public class IndirectDrawSet<T extends InstancedPart> {
continue;
}
material.setup();
Textures.bindActiveTextures();
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, i * stride, 1, stride);
material.clear();
}

View file

@ -1,13 +1,6 @@
package com.jozufozu.flywheel.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import org.jetbrains.annotations.Nullable;
@ -18,8 +11,6 @@ import com.jozufozu.flywheel.api.uniform.UniformProvider;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.source.FileResolution;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import net.minecraft.resources.ResourceLocation;
public class ComponentRegistry {
@ -93,8 +84,8 @@ public class ComponentRegistry {
public <T extends Material> T add(T material) {
materials.add(material);
vertexSources.register(material, material.getVertexShader());
fragmentSources.register(material, material.getFragmentShader());
vertexSources.register(material.getVertexShader());
fragmentSources.register(material.getFragmentShader());
return material;
}
@ -113,15 +104,21 @@ public class ComponentRegistry {
return fragmentSources.sourceView;
}
public int getVertexID(Material material) {
return vertexSources.orderedSources.indexOf(material.getVertexShader());
}
public int getFragmentID(Material material) {
return fragmentSources.orderedSources.indexOf(material.getFragmentShader());
}
private static class MaterialSources {
private final Set<FileResolution> registered = new HashSet<>();
private final List<FileResolution> orderedSources = new ArrayList<>();
private final Reference2IntMap<Material> material2ID = new Reference2IntOpenHashMap<>();
private final List<FileResolution> sourceView = Collections.unmodifiableList(orderedSources);
public void register(Material material, FileResolution vertexShader) {
public void register(FileResolution vertexShader) {
if (registered.add(vertexShader)) {
material2ID.put(material, orderedSources.size());
orderedSources.add(vertexShader);
}
}

View file

@ -4,7 +4,6 @@ import java.util.function.BiConsumer;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceChecks;
import com.jozufozu.flywheel.core.source.SourceFile;
@ -19,9 +18,9 @@ import net.minecraft.resources.ResourceLocation;
public class Components {
public static final FlwUniformProvider UNIFORM_PROVIDER = ComponentRegistry.register(new FlwUniformProvider());
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 FlwUniformProvider UNIFORM_PROVIDER = ComponentRegistry.register(new FlwUniformProvider());
public static final ContextShader WORLD = ComponentRegistry.register(new ContextShader(Files.WORLD_VERTEX, Files.WORLD_FRAGMENT));
public static final ContextShader CRUMBLING = ComponentRegistry.register(new ContextShader(Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT));
public static void init() {
Files.init();

View file

@ -1,25 +0,0 @@
package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
public class WorldProgram extends GlProgram {
// TODO: sampler registry?
protected int diffuseTex;
protected int overlayTex;
protected int lightTex;
public WorldProgram(int handle) {
super(handle);
bind();
registerSamplers();
unbind();
}
protected void registerSamplers() {
diffuseTex = setSamplerBinding("flw_diffuseTex", 0);
overlayTex = setSamplerBinding("flw_overlayTex", 1);
lightTex = setSamplerBinding("flw_lightTex", 2);
}
}

View file

@ -1,14 +0,0 @@
package com.jozufozu.flywheel.core.crumbling;
import com.jozufozu.flywheel.core.WorldProgram;
public class CrumblingProgram extends WorldProgram {
public CrumblingProgram(int handle) {
super(handle);
}
@Override
protected void registerSamplers() {
diffuseTex = setSamplerBinding("flw_diffuseTex", 0);
}
}

View file

@ -2,16 +2,7 @@
layout(local_size_x = FLW_SUBGROUP_SIZE) in;
#use "flywheel:api/cull.glsl"
#use "flywheel:util/types.glsl"
struct MeshDrawCommand {
uint indexCount;
uint instanceCount;
uint firstIndex;
uint vertexOffset;
uint baseInstance;
BoundingSphere boundingSphere;
};
#use "flywheel:pipeline/indirect_draw_command.glsl"
// populated by instancers
layout(std430, binding = 0) restrict readonly buffer ObjectBuffer {

View file

@ -1,4 +1,5 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:pipeline/indirect_draw_command.glsl"
layout(std430, binding = 0) restrict readonly buffer ObjectBuffer {
FlwPackedInstance objects[];
@ -8,9 +9,22 @@ layout(std430, binding = 1) restrict readonly buffer TargetBuffer {
uint objectIDs[];
};
layout(std430, binding = 2) restrict readonly buffer BatchBuffer {
uint batchIDs[];
};
layout(std430, binding = 3) restrict readonly buffer DrawCommands {
MeshDrawCommand drawCommands[];
};
void main() {
uint instanceIndex = objectIDs[gl_BaseInstance + gl_InstanceID];
uint batchID = batchIDs[instanceIndex];
FlwInstance i = flw_unpackInstance(objects[instanceIndex]);
flw_materialVertexID = drawCommands[batchID].vertexMaterialID;
flw_materialFragmentID = drawCommands[batchID].fragmentMaterialID;
flw_layoutVertex();
flw_instanceVertex(i);
flw_materialVertex();

View file

@ -0,0 +1,13 @@
#use "flywheel:util/types.glsl"
struct MeshDrawCommand {
uint indexCount;
uint instanceCount;
uint firstIndex;
uint vertexOffset;
uint baseInstance;
BoundingSphere boundingSphere;
uint vertexMaterialID;
uint fragmentMaterialID;
};