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 0a4fcf27d8
commit a9ee85ff76
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> {
void write(final long ptr, final T instance, final int batchID);
void write(final long ptr, final T instance);
int getAlignment();
}

View file

@ -52,8 +52,7 @@ public class Loader implements ResourceManagerReloadListener {
FileResolution.run(errorReporter, sources);
if (errorReporter.hasErrored()) {
errorReporter.dump();
throw new ShaderLoadingException("Failed to resolve all source files, see log for details");
throw errorReporter.dump();
}
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.GlShader;
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.ProgramAssembler;
import com.jozufozu.flywheel.core.compile.ShaderCompilationException;
@ -24,7 +25,7 @@ public class ComputeCullerCompiler extends Memoizer<FileResolution, GlProgram> {
CompilationContext context = new CompilationContext();
var header = GLSLVersion.V460.getVersionLine();
var header = CompileUtil.generateHeader(GLSLVersion.V460, ShaderType.COMPUTE);
String source = file.getFile()
.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 RenderLists renderLists = new RenderLists();
private FrustumUBO frustumUBO;
/**
* The set of instance managers that are attached to this engine.
*/
@ -77,7 +75,7 @@ public class IndirectEngine implements Engine {
setup();
for (var group : groups) {
group.submit(frustumUBO);
group.submit();
}
}
@ -146,26 +144,10 @@ public class IndirectEngine implements Engine {
@Override
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
if (frustumUBO == null) {
frustumUBO = new FrustumUBO();
}
for (var model : uninitializedModels) {
model.init(renderLists);
}
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) {

View file

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

View file

@ -89,11 +89,14 @@ public class IndirectMeshPool {
public class BufferedMesh {
public final Mesh mesh;
private final int vertexCount;
private long byteIndex;
private int baseVertex;
public BufferedMesh(Mesh mesh) {
this.mesh = mesh;
vertexCount = mesh.getVertexCount();
}
private void buffer(ByteBuffer buffer) {
@ -103,7 +106,7 @@ public class IndirectMeshPool {
}
public int getByteSize() {
return IndirectMeshPool.this.vertexType.getLayout().getStride() * this.mesh.getVertexCount();
return IndirectMeshPool.this.vertexType.getLayout().getStride() * this.vertexCount;
}
public int getBaseVertex() {
@ -111,7 +114,11 @@ public class IndirectMeshPool {
}
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();
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX));
finalSource.append("#extension GL_ARB_explicit_attrib_location : enable\n");
var index = new CompilationContext();
@ -197,6 +198,7 @@ public class InstancedArraysCompiler extends Memoizer<InstancedArraysCompiler.Co
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();

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.structs.StructTypes;
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.vertex.Formats;
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 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 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 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 POS_TEX_NORMAL_LAYOUT = layoutVertex(ResourceUtil.subPath(Names.POS_TEX_NORMAL, ".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 String generateHeader(GLSLVersion version, ShaderType type) {
return "#version " + version + '\n'
+ "#extension GL_ARB_explicit_attrib_location : enable\n"
+ "#extension GL_ARB_conservative_depth : enable\n"
return version.getVersionLine()
+ type.getDefineStatement()
+ '\n';
}

View file

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

View file

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

View file

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

View file

@ -13,7 +13,7 @@ public class OrientedStorageWriter implements StorageBufferWriter<OrientedPart>
}
@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 + 4, d.qY);
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 + 40, d.pivotZ);
MemoryUtil.memPutShort(ptr + 44, d.blockLight);
MemoryUtil.memPutShort(ptr + 46, d.skyLight);
MemoryUtil.memPutShort(ptr + 44, d.skyLight);
MemoryUtil.memPutShort(ptr + 46, d.blockLight);
MemoryUtil.memPutByte(ptr + 48, d.r);
MemoryUtil.memPutByte(ptr + 49, d.g);
MemoryUtil.memPutByte(ptr + 50, d.b);
MemoryUtil.memPutByte(ptr + 51, d.a);
MemoryUtil.memPutInt(ptr + 52, batchID);
}
@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_vertexColor;
in vec2 flw_vertexTexCoord;
@ -32,3 +33,4 @@ vec4 flw_fogFilter(vec4 color);
* Guard calls with FLW_DISCARD
*/
bool flw_discardPredicate(vec4 finalColor);
#endif

View file

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

View file

@ -1,67 +1,60 @@
#define FLW_SUBGROUP_SIZE 32
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:uniform/frustum.glsl"
#use "flywheel:instance/oriented_indirect.glsl"
uint flw_objectID;
uint flw_batchID;
struct MeshDrawCommand {
uint indexCount;
uint instanceCount;
uint firstIndex;
uint vertexOffset;
uint baseInstance;
layout(std140, binding = 0) uniform FrameData {
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[];
vec4 boundingSphere;
};
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[];
};
layout(std430, binding = 2) readonly buffer BoundingSpheres {
vec4 boundingSpheres[];
layout(std430, binding = 2) restrict readonly buffer BatchBuffer {
uint batchIDs[];
};
layout(std430, binding = 3) buffer DrawCommands {
layout(std430, binding = 3) restrict buffer DrawCommands {
MeshDrawCommand drawCommands[];
};
layout(std430, binding = 4) writeonly buffer DebugVisibility {
layout(std430, binding = 4) restrict writeonly buffer DebugVisibility {
uint objectVisibilityBits[];
};
// 83 - 27 = 56 spirv instruction results
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);
bvec2 resultB = greaterThanEqual(fma(frustum.b1, center.xx, fma(frustum.b2, center.yy, fma(frustum.b3, center.zz, frustum.b4))), -radius.xx);
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 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);
debug |= uint(resultA.y) << 1;
debug |= uint(resultA.z) << 2;
debug |= uint(resultA.w) << 3;
debug |= uint(resultB.x) << 4;
debug |= uint(resultB.y) << 5;
uint debug = uint(xyInside.x);
debug |= uint(xyInside.y) << 1;
debug |= uint(xyInside.z) << 2;
debug |= uint(xyInside.w) << 3;
debug |= uint(zInside.x) << 4;
debug |= uint(zInside.y) << 5;
objectVisibilityBits[flw_objectID] = debug;
return all(resultA) && all(resultB);
}
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;
return all(xyInside) && all(zInside);
}
bool isVisible() {
vec4 sphere = boundingSpheres[flw_batchID];
vec4 sphere = drawCommands[flw_batchID].boundingSphere;
vec3 center = sphere.xyz;
float radius = sphere.r;
@ -77,7 +70,7 @@ void main() {
return;
}
flw_batchID = objects[objectID].batchID;
flw_batchID = batchIDs[flw_objectID];
if (isVisible()) {
uint batchIndex = atomicAdd(drawCommands[flw_batchID].instanceCount, 1);

View file

@ -1,25 +1,18 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:compute/objects.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) readonly buffer ObjectBuffer {
Instance objects[];
layout(std430, binding = 0) restrict readonly buffer ObjectBuffer {
FLW_INSTANCE_STRUCT objects[];
};
layout(std430, binding = 1) readonly buffer TargetBuffer {
layout(std430, binding = 1) restrict readonly buffer TargetBuffer {
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() {
uint instanceIndex = objectIDs[gl_BaseInstance + gl_InstanceID];
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;
};