Separate shader logic and fix rendering errors

- Batch IDs are stored in a separate buffer
 - Bounding spheres in draw command buffer, subject to change
 - Guard shader api files
 - Generate complete header for compute culler compiler
 - Move Frustum UBO to uniform shader/provider
This commit is contained in:
Jozufozu 2022-08-06 17:17:46 -07:00
parent 63dc8ee923
commit 03f6125c68
24 changed files with 230 additions and 174 deletions

View file

@ -4,7 +4,7 @@ import com.jozufozu.flywheel.api.instancer.InstancedPart;
public interface StorageBufferWriter<T extends InstancedPart> { public interface StorageBufferWriter<T extends InstancedPart> {
void write(final long ptr, final T instance, final int batchID); void write(final long ptr, final T instance);
int getAlignment(); int getAlignment();
} }

View file

@ -52,8 +52,7 @@ public class Loader implements ResourceManagerReloadListener {
FileResolution.run(errorReporter, sources); FileResolution.run(errorReporter, sources);
if (errorReporter.hasErrored()) { if (errorReporter.hasErrored()) {
errorReporter.dump(); throw errorReporter.dump();
throw new ShaderLoadingException("Failed to resolve all source files, see log for details");
} }
sources.postResolve(); sources.postResolve();

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.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.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;
@ -24,7 +25,7 @@ public class ComputeCullerCompiler extends Memoizer<FileResolution, GlProgram> {
CompilationContext context = new CompilationContext(); CompilationContext context = new CompilationContext();
var header = GLSLVersion.V460.getVersionLine(); var header = CompileUtil.generateHeader(GLSLVersion.V460, ShaderType.COMPUTE);
String source = file.getFile() String source = file.getFile()
.generateFinalSource(context); .generateFinalSource(context);

View file

@ -1,31 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.indirect;
import static org.lwjgl.opengl.GL46.*;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.util.joml.FrustumIntersection;
// This should be a push constant :whywheel:
public class FrustumUBO {
public static final int BUFFER_SIZE = 96;
private final int ubo;
private final long clientStorage;
FrustumUBO() {
ubo = glCreateBuffers();
glNamedBufferStorage(ubo, BUFFER_SIZE, GL_DYNAMIC_STORAGE_BIT);
clientStorage = MemoryUtil.nmemAlloc(BUFFER_SIZE);
}
public void update(FrustumIntersection frustum) {
frustum.getJozuPackedPlanes(clientStorage);
nglNamedBufferSubData(ubo, 0, BUFFER_SIZE, clientStorage);
}
public void bind() {
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo);
}
}

View file

@ -45,8 +45,6 @@ public class IndirectEngine implements Engine {
protected final List<InstancedModel<?>> uninitializedModels = new ArrayList<>(); protected final List<InstancedModel<?>> uninitializedModels = new ArrayList<>();
protected final RenderLists renderLists = new RenderLists(); protected final RenderLists renderLists = new RenderLists();
private FrustumUBO frustumUBO;
/** /**
* The set of instance managers that are attached to this engine. * The set of instance managers that are attached to this engine.
*/ */
@ -77,7 +75,7 @@ public class IndirectEngine implements Engine {
setup(); setup();
for (var group : groups) { for (var group : groups) {
group.submit(frustumUBO); group.submit();
} }
} }
@ -146,26 +144,10 @@ public class IndirectEngine implements Engine {
@Override @Override
public void beginFrame(TaskEngine taskEngine, RenderContext context) { public void beginFrame(TaskEngine taskEngine, RenderContext context) {
if (frustumUBO == null) {
frustumUBO = new FrustumUBO();
}
for (var model : uninitializedModels) { for (var model : uninitializedModels) {
model.init(renderLists); model.init(renderLists);
} }
uninitializedModels.clear(); uninitializedModels.clear();
Vec3 camera = context.camera()
.getPosition();
var camX = (float) (camera.x - originCoordinate.getX());
var camY = (float) (camera.y - originCoordinate.getY());
var camZ = (float) (camera.z - originCoordinate.getZ());
var culler = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ);
frustumUBO.update(culler);
} }
private void shiftListeners(int cX, int cY, int cZ) { private void shiftListeners(int cX, int cY, int cZ) {

View file

@ -21,7 +21,8 @@ import com.jozufozu.flywheel.core.vertex.Formats;
public class IndirectList<T extends InstancedPart> { public class IndirectList<T extends InstancedPart> {
private static final long DRAW_COMMAND_STRIDE = 20; private static final long DRAW_COMMAND_STRIDE = 48;
private static final long DRAW_COMMAND_OFFSET = 0;
final StorageBufferWriter<T> storageBufferWriter; final StorageBufferWriter<T> storageBufferWriter;
final GlProgram compute; final GlProgram compute;
@ -31,18 +32,15 @@ public class IndirectList<T extends InstancedPart> {
private final long objectStride; private final long objectStride;
private final int maxBatchCount; private final int maxBatchCount;
private final long objectClientStorage; private final long objectClientStorage;
private final long batchIDClientStorage;
private final int elementBuffer; private final int elementBuffer;
/** /**
* Stores raw instance data per-object. * Stores raw instance data per-object.
*/ */
int objectBuffer; int objectBuffer;
int targetBuffer; int targetBuffer;
/** int batchBuffer;
* Stores bounding spheres
*/
int boundingSphereBuffer;
/** /**
* Stores drawIndirect structs. * Stores drawIndirect structs.
@ -69,7 +67,7 @@ public class IndirectList<T extends InstancedPart> {
glCreateBuffers(shaderStorageBuffers); glCreateBuffers(shaderStorageBuffers);
objectBuffer = shaderStorageBuffers[0]; objectBuffer = shaderStorageBuffers[0];
targetBuffer = shaderStorageBuffers[1]; targetBuffer = shaderStorageBuffers[1];
boundingSphereBuffer = shaderStorageBuffers[2]; batchBuffer = shaderStorageBuffers[2];
drawBuffer = shaderStorageBuffers[3]; drawBuffer = shaderStorageBuffers[3];
debugBuffer = shaderStorageBuffers[4]; debugBuffer = shaderStorageBuffers[4];
meshPool = new IndirectMeshPool(Formats.BLOCK, 1024); meshPool = new IndirectMeshPool(Formats.BLOCK, 1024);
@ -78,15 +76,15 @@ public class IndirectList<T extends InstancedPart> {
maxObjectCount = 1024L; maxObjectCount = 1024L;
maxBatchCount = 64; maxBatchCount = 64;
// +4 for the batch id
objectStride = storageBufferWriter.getAlignment(); objectStride = storageBufferWriter.getAlignment();
glNamedBufferStorage(objectBuffer, objectStride * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); glNamedBufferStorage(objectBuffer, objectStride * maxObjectCount, GL_DYNAMIC_STORAGE_BIT);
glNamedBufferStorage(targetBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); glNamedBufferStorage(targetBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT);
glNamedBufferStorage(boundingSphereBuffer, 16 * maxBatchCount, GL_DYNAMIC_STORAGE_BIT); glNamedBufferStorage(batchBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT);
glNamedBufferStorage(drawBuffer, DRAW_COMMAND_STRIDE * maxBatchCount, GL_DYNAMIC_STORAGE_BIT); glNamedBufferStorage(drawBuffer, DRAW_COMMAND_STRIDE * maxBatchCount, GL_DYNAMIC_STORAGE_BIT);
glNamedBufferStorage(debugBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT); glNamedBufferStorage(debugBuffer, 4 * maxObjectCount, GL_DYNAMIC_STORAGE_BIT);
objectClientStorage = MemoryUtil.nmemAlloc(objectStride * maxObjectCount); objectClientStorage = MemoryUtil.nmemAlloc(objectStride * maxObjectCount);
batchIDClientStorage = MemoryUtil.nmemAlloc(4 * maxObjectCount);
vertexArray = glCreateVertexArrays(); vertexArray = glCreateVertexArrays();
@ -121,7 +119,7 @@ public class IndirectList<T extends InstancedPart> {
batches.add(new Batch<>(instancer, meshPool.alloc(mesh))); batches.add(new Batch<>(instancer, meshPool.alloc(mesh)));
} }
void submit(FrustumUBO frustumUBO) { void submit() {
int instanceCountThisFrame = calculateTotalInstanceCount(); int instanceCountThisFrame = calculateTotalInstanceCount();
if (instanceCountThisFrame == 0) { if (instanceCountThisFrame == 0) {
@ -130,24 +128,22 @@ public class IndirectList<T extends InstancedPart> {
meshPool.uploadAll(); meshPool.uploadAll();
uploadInstanceData(); uploadInstanceData();
uploadBoundingSpheres();
uploadIndirectCommands(); uploadIndirectCommands();
frustumUBO.bind(); UniformBuffer.getInstance().sync();
dispatchCompute(instanceCountThisFrame); dispatchCompute(instanceCountThisFrame);
issueMemoryBarrier(); issueMemoryBarrier();
dispatchDraw(); dispatchDraw();
} }
private void dispatchDraw() { private void dispatchDraw() {
UniformBuffer.getInstance().sync();
draw.bind(); draw.bind();
Materials.BELL.setup(); Materials.BELL.setup();
glVertexArrayElementBuffer(vertexArray, elementBuffer); glVertexArrayElementBuffer(vertexArray, elementBuffer);
glBindVertexArray(vertexArray); glBindVertexArray(vertexArray);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, drawBuffer); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, drawBuffer);
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, 0, batches.size(), 0); glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, DRAW_COMMAND_OFFSET, batches.size(), (int) DRAW_COMMAND_STRIDE);
Materials.BELL.clear(); Materials.BELL.clear();
} }
@ -155,27 +151,11 @@ public class IndirectList<T extends InstancedPart> {
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
} }
private void uploadBoundingSpheres() {
try (var stack = MemoryStack.stackPush()) {
final int size = batches.size() * 16;
final long basePtr = stack.nmalloc(size);
long ptr = basePtr;
for (Batch<T> batch : batches) {
var boundingSphere = batch.mesh.mesh.getBoundingSphere();
boundingSphere.getToAddress(ptr);
ptr += 16;
}
nglNamedBufferSubData(boundingSphereBuffer, 0, size, basePtr);
}
}
private void dispatchCompute(int instanceCount) { private void dispatchCompute(int instanceCount) {
compute.bind(); compute.bind();
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, objectBuffer, 0, instanceCount * objectStride); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, objectBuffer, 0, instanceCount * objectStride);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, targetBuffer, 0, instanceCount * 4L); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, targetBuffer, 0, instanceCount * 4L);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, boundingSphereBuffer, 0, batches.size() * 16L); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, batchBuffer, 0, instanceCount * 4L);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, drawBuffer, 0, batches.size() * DRAW_COMMAND_STRIDE); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, drawBuffer, 0, batches.size() * DRAW_COMMAND_STRIDE);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 4, debugBuffer, 0, instanceCount * 4L); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 4, debugBuffer, 0, instanceCount * 4L);
@ -184,22 +164,28 @@ public class IndirectList<T extends InstancedPart> {
} }
private void uploadInstanceData() { private void uploadInstanceData() {
long ptr = objectClientStorage; long objectPtr = objectClientStorage;
long batchIDPtr = batchIDClientStorage;
int baseInstance = 0; int baseInstance = 0;
int batchID = 0; int batchID = 0;
for (var batch : batches) { for (var batch : batches) {
batch.baseInstance = baseInstance; batch.baseInstance = baseInstance;
var instancer = batch.instancer; var instancer = batch.instancer;
for (T t : instancer.getAll()) { for (T t : instancer.getAll()) {
storageBufferWriter.write(ptr, t, batchID); // write object
ptr += objectStride; storageBufferWriter.write(objectPtr, t);
objectPtr += objectStride;
// write batchID
MemoryUtil.memPutInt(batchIDPtr, batchID);
batchIDPtr += 4;
} }
baseInstance += batch.instancer.instanceCount; baseInstance += batch.instancer.instanceCount;
batchID++; batchID++;
} }
nglNamedBufferSubData(objectBuffer, 0, ptr - objectClientStorage, objectClientStorage); nglNamedBufferSubData(objectBuffer, 0, objectPtr - objectClientStorage, objectClientStorage);
nglNamedBufferSubData(batchBuffer, 0, batchIDPtr - batchIDClientStorage, batchIDClientStorage);
} }
private void uploadIndirectCommands() { private void uploadIndirectCommands() {
@ -235,11 +221,15 @@ public class IndirectList<T extends InstancedPart> {
} }
public void writeIndirectCommand(long ptr) { public void writeIndirectCommand(long ptr) {
MemoryUtil.memPutInt(ptr, mesh.getVertexCount()); // count var boundingSphere = mesh.mesh.getBoundingSphere();
MemoryUtil.memPutInt(ptr, mesh.getIndexCount()); // count
MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be incremented by the compute shader MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be incremented by the compute shader
MemoryUtil.memPutInt(ptr + 8, 0); // firstIndex - all models share the same index buffer MemoryUtil.memPutInt(ptr + 8, 0); // firstIndex - all models share the same index buffer
MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex
MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance
boundingSphere.getToAddress(ptr + 32); // boundingSphere
} }
} }
} }

View file

@ -89,11 +89,14 @@ public class IndirectMeshPool {
public class BufferedMesh { public class BufferedMesh {
public final Mesh mesh; public final Mesh mesh;
private final int vertexCount;
private long byteIndex; private long byteIndex;
private int baseVertex; private int baseVertex;
public BufferedMesh(Mesh mesh) { public BufferedMesh(Mesh mesh) {
this.mesh = mesh; this.mesh = mesh;
vertexCount = mesh.getVertexCount();
} }
private void buffer(ByteBuffer buffer) { private void buffer(ByteBuffer buffer) {
@ -103,7 +106,7 @@ public class IndirectMeshPool {
} }
public int getByteSize() { public int getByteSize() {
return IndirectMeshPool.this.vertexType.getLayout().getStride() * this.mesh.getVertexCount(); return IndirectMeshPool.this.vertexType.getLayout().getStride() * this.vertexCount;
} }
public int getBaseVertex() { public int getBaseVertex() {
@ -111,7 +114,11 @@ public class IndirectMeshPool {
} }
public int getVertexCount() { public int getVertexCount() {
return this.mesh.getVertexCount(); return this.vertexCount;
}
public int getIndexCount() {
return this.vertexCount * 6 / 4;
} }
} }

View file

@ -112,6 +112,7 @@ public class InstancedArraysCompiler extends Memoizer<InstancedArraysCompiler.Co
StringBuilder finalSource = new StringBuilder(); StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX)); finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX));
finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n");
var index = new CompilationContext(); var index = new CompilationContext();
@ -197,6 +198,7 @@ public class InstancedArraysCompiler extends Memoizer<InstancedArraysCompiler.Co
StringBuilder finalSource = new StringBuilder(); StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.FRAGMENT)); finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.FRAGMENT));
finalSource.append("#extension GL_ARB_conservative_depth : enable\n");
var ctx = new CompilationContext(); var ctx = new CompilationContext();

View file

@ -11,6 +11,7 @@ import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.error.ErrorReporter; import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.uniform.FogProvider; import com.jozufozu.flywheel.core.uniform.FogProvider;
import com.jozufozu.flywheel.core.uniform.FrustumProvider;
import com.jozufozu.flywheel.core.uniform.ViewProvider; import com.jozufozu.flywheel.core.uniform.ViewProvider;
import com.jozufozu.flywheel.core.vertex.Formats; import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.util.ResourceUtil; import com.jozufozu.flywheel.util.ResourceUtil;
@ -22,6 +23,7 @@ public class Components {
public static final ViewProvider VIEW_PROVIDER = ComponentRegistry.register(new ViewProvider()); public static final ViewProvider VIEW_PROVIDER = ComponentRegistry.register(new ViewProvider());
public static final FogProvider FOG_PROVIDER = ComponentRegistry.register(new FogProvider()); public static final FogProvider FOG_PROVIDER = ComponentRegistry.register(new FogProvider());
public static final FrustumProvider FRUSTUM_PROVIDER = ComponentRegistry.register(new FrustumProvider());
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));
@ -36,6 +38,7 @@ public class Components {
public static final FileResolution VIEW_UNIFORMS = uniform(Flywheel.rl("uniform/view.glsl")); public static final FileResolution VIEW_UNIFORMS = uniform(Flywheel.rl("uniform/view.glsl"));
public static final FileResolution FOG_UNIFORMS = uniform(Flywheel.rl("uniform/fog.glsl")); public static final FileResolution FOG_UNIFORMS = uniform(Flywheel.rl("uniform/fog.glsl"));
public static final FileResolution FRUSTUM_UNIFORMS = uniform(Flywheel.rl("uniform/frustum.glsl"));
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"));

View file

@ -17,9 +17,7 @@ public class CompileUtil {
public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$"); public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$");
public static String generateHeader(GLSLVersion version, ShaderType type) { public static String generateHeader(GLSLVersion version, ShaderType type) {
return "#version " + version + '\n' return version.getVersionLine()
+ "#extension GL_ARB_explicit_attrib_location : enable\n"
+ "#extension GL_ARB_conservative_depth : enable\n"
+ type.getDefineStatement() + type.getDefineStatement()
+ '\n'; + '\n';
} }

View file

@ -114,7 +114,7 @@ public class FileResolution {
ErrorBuilder builder = errorReporter.error(String.format("could not find source for file %s", fileLoc)); ErrorBuilder builder = errorReporter.error(String.format("could not find source for file %s", fileLoc));
for (Span location : neededAt) { for (Span location : neededAt) {
builder.pointAtFile(location.getSourceFile()) builder.pointAtFile(location.getSourceFile())
.pointAt(location, 1); .pointAt(location);
} }
} }

View file

@ -88,6 +88,10 @@ public class ErrorBuilder {
.pointAt(span, 0); .pointAt(span, 0);
} }
public ErrorBuilder pointAt(Span span) {
return pointAt(span, 0);
}
public ErrorBuilder pointAt(Span span, int ctxLines) { public ErrorBuilder pointAt(Span span, int ctxLines) {
if (span.lines() == 1) { if (span.lines() == 1) {
@ -123,7 +127,9 @@ public class ErrorBuilder {
for (ErrorLine line : lines) { for (ErrorLine line : lines) {
int length = line.neededMargin(); int length = line.neededMargin();
if (length > maxLength) maxLength = length; if (length > maxLength) {
maxLength = length;
}
} }
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();

View file

@ -3,9 +3,11 @@ package com.jozufozu.flywheel.core.source.error;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
import com.jozufozu.flywheel.core.source.SourceFile; import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.parse.ShaderFunction; import com.jozufozu.flywheel.core.source.parse.ShaderFunction;
import com.jozufozu.flywheel.core.source.parse.ShaderStruct; import com.jozufozu.flywheel.core.source.parse.ShaderStruct;
@ -82,10 +84,12 @@ public class ErrorReporter {
return !reportedErrors.isEmpty(); return !reportedErrors.isEmpty();
} }
public void dump() { public ShaderLoadingException dump() {
for (var error : reportedErrors) { var allErrors = reportedErrors.stream()
Backend.LOGGER.error(error.build()); .map(ErrorBuilder::build)
} .collect(Collectors.joining());
return new ShaderLoadingException(allErrors);
} }
public static void printLines(CharSequence source) { public static void printLines(CharSequence source) {

View file

@ -13,7 +13,7 @@ public class OrientedStorageWriter implements StorageBufferWriter<OrientedPart>
} }
@Override @Override
public void write(final long ptr, OrientedPart d, final int batchID) { public void write(final long ptr, OrientedPart d) {
MemoryUtil.memPutFloat(ptr, d.qX); MemoryUtil.memPutFloat(ptr, d.qX);
MemoryUtil.memPutFloat(ptr + 4, d.qY); MemoryUtil.memPutFloat(ptr + 4, d.qY);
MemoryUtil.memPutFloat(ptr + 8, d.qZ); MemoryUtil.memPutFloat(ptr + 8, d.qZ);
@ -27,15 +27,13 @@ public class OrientedStorageWriter implements StorageBufferWriter<OrientedPart>
MemoryUtil.memPutFloat(ptr + 36, d.pivotY); MemoryUtil.memPutFloat(ptr + 36, d.pivotY);
MemoryUtil.memPutFloat(ptr + 40, d.pivotZ); MemoryUtil.memPutFloat(ptr + 40, d.pivotZ);
MemoryUtil.memPutShort(ptr + 44, d.blockLight); MemoryUtil.memPutShort(ptr + 44, d.skyLight);
MemoryUtil.memPutShort(ptr + 46, d.skyLight); MemoryUtil.memPutShort(ptr + 46, d.blockLight);
MemoryUtil.memPutByte(ptr + 48, d.r); MemoryUtil.memPutByte(ptr + 48, d.r);
MemoryUtil.memPutByte(ptr + 49, d.g); MemoryUtil.memPutByte(ptr + 49, d.g);
MemoryUtil.memPutByte(ptr + 50, d.b); MemoryUtil.memPutByte(ptr + 50, d.b);
MemoryUtil.memPutByte(ptr + 51, d.a); MemoryUtil.memPutByte(ptr + 51, d.a);
MemoryUtil.memPutInt(ptr + 52, batchID);
} }
@Override @Override

View file

@ -0,0 +1,60 @@
package com.jozufozu.flywheel.core.uniform;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.uniform.UniformProvider;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.util.extension.MatrixExtension;
import com.jozufozu.flywheel.util.joml.FrustumIntersection;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.MinecraftForge;
public class FrustumProvider extends UniformProvider {
public FrustumProvider() {
MinecraftForge.EVENT_BUS.addListener(this::beginFrame);
}
@Override
public int getActualByteSize() {
return 96;
}
@Override
public FileResolution getUniformShader() {
return Components.Files.FRUSTUM_UNIFORMS;
}
public void beginFrame(BeginFrameEvent event) {
update(event.getContext());
}
public void update(RenderContext context) {
if (buffer == null) {
return;
}
Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(context.level());
Vec3 camera = context.camera()
.getPosition();
var camX = (float) (camera.x - originCoordinate.getX());
var camY = (float) (camera.y - originCoordinate.getY());
var camZ = (float) (camera.z - originCoordinate.getZ());
var shiftedCuller = RenderContext.createCuller(context.viewProjection(), -camX, -camY, -camZ);
long ptr = MemoryUtil.memAddress(buffer);
shiftedCuller.getJozuPackedPlanes(ptr);
notifier.signalChanged();
}
}

View file

@ -0,0 +1,4 @@
#ifdef COMPUTE_SHADER
uint flw_objectID;
uint flw_batchID;
#endif

View file

@ -1,3 +1,4 @@
#ifdef FRAGMENT_SHADER
in vec4 flw_vertexPos; in vec4 flw_vertexPos;
in vec4 flw_vertexColor; in vec4 flw_vertexColor;
in vec2 flw_vertexTexCoord; in vec2 flw_vertexTexCoord;
@ -32,3 +33,4 @@ vec4 flw_fogFilter(vec4 color);
* Guard calls with FLW_DISCARD * Guard calls with FLW_DISCARD
*/ */
bool flw_discardPredicate(vec4 finalColor); bool flw_discardPredicate(vec4 finalColor);
#endif

View file

@ -1,3 +1,4 @@
#ifdef VERTEX_SHADER
out vec4 flw_vertexPos; out vec4 flw_vertexPos;
out vec4 flw_vertexColor; out vec4 flw_vertexColor;
out vec2 flw_vertexTexCoord; out vec2 flw_vertexTexCoord;
@ -11,3 +12,4 @@ out vec4 flw_var0;
out vec4 flw_var1; out vec4 flw_var1;
out vec4 flw_var2; out vec4 flw_var2;
out vec4 flw_var3; out vec4 flw_var3;
#endif

View file

@ -1,67 +1,60 @@
#define FLW_SUBGROUP_SIZE 32 #define FLW_SUBGROUP_SIZE 32
layout(local_size_x = FLW_SUBGROUP_SIZE) in; layout(local_size_x = FLW_SUBGROUP_SIZE) in;
#use "flywheel:compute/objects.glsl" #use "flywheel:api/cull.glsl"
#use "flywheel:util/quaternion.glsl" #use "flywheel:util/quaternion.glsl"
#use "flywheel:uniform/frustum.glsl"
#use "flywheel:instance/oriented_indirect.glsl"
uint flw_objectID; struct MeshDrawCommand {
uint flw_batchID; uint indexCount;
uint instanceCount;
uint firstIndex;
uint vertexOffset;
uint baseInstance;
layout(std140, binding = 0) uniform FrameData { vec4 boundingSphere;
vec4 a1; // vec4(nx.x, px.x, ny.x, py.x)
vec4 a2; // vec4(nx.y, px.y, ny.y, py.y)
vec4 a3; // vec4(nx.z, px.z, ny.z, py.z)
vec4 a4; // vec4(nx.w, px.w, ny.w, py.w)
vec2 b1; // vec2(nz.x, pz.x)
vec2 b2; // vec2(nz.y, pz.y)
vec2 b3; // vec2(nz.z, pz.z)
vec2 b4; // vec2(nz.w, pz.w)
} frustum;
// populated by instancers
layout(std430, binding = 0) readonly buffer ObjectBuffer {
Instance objects[];
}; };
layout(std430, binding = 1) writeonly buffer TargetBuffer { // populated by instancers
layout(std430, binding = 0) restrict readonly buffer ObjectBuffer {
FLW_INSTANCE_STRUCT objects[];
};
layout(std430, binding = 1) restrict writeonly buffer TargetBuffer {
uint objectIDs[]; uint objectIDs[];
}; };
layout(std430, binding = 2) readonly buffer BoundingSpheres { layout(std430, binding = 2) restrict readonly buffer BatchBuffer {
vec4 boundingSpheres[]; uint batchIDs[];
}; };
layout(std430, binding = 3) buffer DrawCommands { layout(std430, binding = 3) restrict buffer DrawCommands {
MeshDrawCommand drawCommands[]; MeshDrawCommand drawCommands[];
}; };
layout(std430, binding = 4) writeonly buffer DebugVisibility { layout(std430, binding = 4) restrict writeonly buffer DebugVisibility {
uint objectVisibilityBits[]; uint objectVisibilityBits[];
}; };
// 83 - 27 = 56 spirv instruction results // 83 - 27 = 56 spirv instruction results
bool testSphere(vec3 center, float radius) { bool testSphere(vec3 center, float radius) {
bvec4 resultA = greaterThanEqual(fma(frustum.a1, center.xxxx, fma(frustum.a2, center.yyyy, fma(frustum.a3, center.zzzz, frustum.a4))), -radius.xxxx); bvec4 xyInside = greaterThanEqual(fma(flw_planes.xyX, center.xxxx, fma(flw_planes.xyY, center.yyyy, fma(flw_planes.xyZ, center.zzzz, flw_planes.xyW))), -radius.xxxx);
bvec2 resultB = greaterThanEqual(fma(frustum.b1, center.xx, fma(frustum.b2, center.yy, fma(frustum.b3, center.zz, frustum.b4))), -radius.xx); bvec2 zInside = greaterThanEqual(fma(flw_planes.zX, center.xx, fma(flw_planes.zY, center.yy, fma(flw_planes.zZ, center.zz, flw_planes.zW))), -radius.xx);
uint debug = uint(resultA.x); uint debug = uint(xyInside.x);
debug |= uint(resultA.y) << 1; debug |= uint(xyInside.y) << 1;
debug |= uint(resultA.z) << 2; debug |= uint(xyInside.z) << 2;
debug |= uint(resultA.w) << 3; debug |= uint(xyInside.w) << 3;
debug |= uint(resultB.x) << 4; debug |= uint(zInside.x) << 4;
debug |= uint(resultB.y) << 5; debug |= uint(zInside.y) << 5;
objectVisibilityBits[flw_objectID] = debug; objectVisibilityBits[flw_objectID] = debug;
return all(resultA) && all(resultB); return all(xyInside) && all(zInside);
}
void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) {
center = rotateVertexByQuat(center - i.pivot, i.rotation) + i.pivot + i.pos;
radius = radius;
} }
bool isVisible() { bool isVisible() {
vec4 sphere = boundingSpheres[flw_batchID]; vec4 sphere = drawCommands[flw_batchID].boundingSphere;
vec3 center = sphere.xyz; vec3 center = sphere.xyz;
float radius = sphere.r; float radius = sphere.r;
@ -77,7 +70,7 @@ void main() {
return; return;
} }
flw_batchID = objects[objectID].batchID; flw_batchID = batchIDs[flw_objectID];
if (isVisible()) { if (isVisible()) {
uint batchIndex = atomicAdd(drawCommands[flw_batchID].instanceCount, 1); uint batchIndex = atomicAdd(drawCommands[flw_batchID].instanceCount, 1);

View file

@ -1,25 +1,18 @@
#use "flywheel:api/vertex.glsl" #use "flywheel:api/vertex.glsl"
#use "flywheel:compute/objects.glsl"
#use "flywheel:layout/block.vert" #use "flywheel:layout/block.vert"
#use "flywheel:context/world.vert" #use "flywheel:context/world.vert"
#use "flywheel:util/quaternion.glsl" #use "flywheel:util/quaternion.glsl"
#use "flywheel:instance/oriented_indirect.glsl"
// populated by instancers // populated by instancers
layout(std430, binding = 0) readonly buffer ObjectBuffer { layout(std430, binding = 0) restrict readonly buffer ObjectBuffer {
Instance objects[]; FLW_INSTANCE_STRUCT objects[];
}; };
layout(std430, binding = 1) readonly buffer TargetBuffer { layout(std430, binding = 1) restrict readonly buffer TargetBuffer {
uint objectIDs[]; uint objectIDs[];
}; };
void flw_instanceVertex(Instance i) {
flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - i.pivot, i.rotation) + i.pivot + i.pos, 1.0);
flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, i.rotation);
flw_vertexColor = unpackUnorm4x8(i.color);
flw_vertexLight = unpackUnorm2x16(i.light) / 15.0;
}
void main() { void main() {
uint instanceIndex = objectIDs[gl_BaseInstance + gl_InstanceID]; uint instanceIndex = objectIDs[gl_BaseInstance + gl_InstanceID];
flw_layoutVertex(); flw_layoutVertex();

View file

@ -1,17 +0,0 @@
struct Instance {
vec4 rotation;
vec3 pos;
vec3 pivot;
uint light;
uint color;
uint batchID;
};
struct MeshDrawCommand {
uint indexCount;
uint instanceCount;
uint firstIndex;
uint vertexOffset;
uint baseInstance;
};

View file

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

View file

@ -0,0 +1,22 @@
#use "flywheel:api/vertex.glsl"
#define FLW_INSTANCE_STRUCT Instance
struct Instance {
mat4 pose;
mat3 normal;
uint color;
uint light;
};
void flw_transformBoundingSphere(in Instance i, inout vec3 center, inout float radius) {
center = (i.pose * vec4(center, 1.0)).xyz;
}
#ifdef VERTEX_SHADER
void flw_instanceVertex(Instance i) {
flw_vertexPos = i.pose * flw_vertexPos;
flw_vertexNormal = 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

@ -0,0 +1,14 @@
struct FLWPackedPlanes {
vec4 xyX; // <nx.x, px.x, ny.x, py.x>
vec4 xyY; // <nx.y, px.y, ny.y, py.y>
vec4 xyZ; // <nx.z, px.z, ny.z, py.z>
vec4 xyW; // <nx.w, px.w, ny.w, py.w>
vec2 zX; // <nz.x, pz.x>
vec2 zY; // <nz.y, pz.y>
vec2 zZ; // <nz.z, pz.z>
vec2 zW; // <nz.w, pz.w>
};
layout(std140, binding = 2) uniform FLWFrustum {
FLWPackedPlanes flw_planes;
};