Gone, reduced to batches

- Remove BatchingEngine.
- Remove InstanceVertexTransformer and BoundingSphereTransformer.
- Remove fallback RenderType and MaterialVertexTransformer.
This commit is contained in:
Jozufozu 2023-12-20 12:05:27 -08:00
parent b7a1eba59a
commit 2dbdf83ec2
20 changed files with 0 additions and 1093 deletions

View file

@ -1,13 +0,0 @@
package com.jozufozu.flywheel.api.instance;
import org.joml.Vector4f;
public interface InstanceBoundingSphereTransformer<I extends Instance> {
/**
* Transform the bounding sphere of a mesh to match the location of the instance.
*
* @param boundingSphere The bounding sphere of the mesh formatted as < x, y, z, radius >
* @param instance The instance to transform the bounding sphere for.
*/
void transform(Vector4f boundingSphere, I instance);
}

View file

@ -35,8 +35,4 @@ public interface InstanceType<I extends Instance> {
ResourceLocation vertexShader();
ResourceLocation cullShader();
InstanceVertexTransformer<I> getVertexTransformer();
InstanceBoundingSphereTransformer<I> getBoundingSphereTransformer();
}

View file

@ -1,7 +0,0 @@
package com.jozufozu.flywheel.api.instance;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
public interface InstanceVertexTransformer<I extends Instance> {
void transform(MutableVertexList vertexList, I instance);
}

View file

@ -1,13 +1,8 @@
package com.jozufozu.flywheel.api.material;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
public interface Material {
RenderType getFallbackRenderType();
MaterialVertexTransformer getVertexTransformer();
MaterialShaders shaders();
FogShader fog();

View file

@ -1,9 +0,0 @@
package com.jozufozu.flywheel.api.material;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import net.minecraft.client.multiplayer.ClientLevel;
public interface MaterialVertexTransformer {
void transform(MutableVertexList vertexList, ClientLevel level);
}

View file

@ -5,7 +5,6 @@ import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.backend.Backend;
import com.jozufozu.flywheel.backend.compile.IndirectPrograms;
import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
import com.jozufozu.flywheel.backend.engine.batching.BatchingEngine;
import com.jozufozu.flywheel.backend.engine.indirect.IndirectEngine;
import com.jozufozu.flywheel.backend.engine.instancing.InstancingEngine;
import com.jozufozu.flywheel.gl.GlCompat;
@ -16,16 +15,6 @@ import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
public class Backends {
/**
* Use a thread pool to buffer instances in parallel on the CPU.
*/
public static final Backend BATCHING = SimpleBackend.builder()
.engineMessage(Component.literal("Using Batching Engine")
.withStyle(ChatFormatting.GREEN))
.engineFactory(level -> new BatchingEngine(256))
.supported(() -> !ShadersModHandler.isShaderPackInUse())
.register(Flywheel.rl("batching"));
/**
* Use GPU instancing to render everything.
*/
@ -33,7 +22,6 @@ public class Backends {
.engineMessage(Component.literal("Using Instancing Engine")
.withStyle(ChatFormatting.GREEN))
.engineFactory(level -> new InstancingEngine(256))
.fallback(() -> Backends.BATCHING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.supportsInstancing() && InstancingPrograms.allLoaded())
.register(Flywheel.rl("instancing"));

View file

@ -1,28 +0,0 @@
package com.jozufozu.flywheel.backend.engine.batching;
import org.jetbrains.annotations.NotNull;
import org.joml.FrustumIntersection;
import org.joml.Matrix4f;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.lib.math.MatrixMath;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
public record BatchContext(FrustumIntersection frustum, ClientLevel level, PoseStack.Pose matrices) {
@NotNull
static BatchContext create(RenderContext context, BlockPos origin) {
Vec3 cameraPos = context.camera()
.getPosition();
var stack = MatrixMath.copyPoseStack(context.stack());
stack.translate(origin.getX() - cameraPos.x, origin.getY() - cameraPos.y, origin.getZ() - cameraPos.z);
Matrix4f viewProjection = new Matrix4f(context.viewProjection());
viewProjection.translate((float) (origin.getX() - cameraPos.x), (float) (origin.getY() - cameraPos.y), (float) (origin.getZ() - cameraPos.z));
return new BatchContext(new FrustumIntersection(viewProjection), context.level(), stack.last());
}
}

View file

@ -1,108 +0,0 @@
package com.jozufozu.flywheel.backend.engine.batching;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.model.Mesh;
import com.jozufozu.flywheel.api.vertex.VertexView;
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
import com.jozufozu.flywheel.extension.RenderTypeExtension;
import it.unimi.dsi.fastutil.Pair;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.model.ModelBakery;
public class BatchedCrumbling {
public static void render(List<Engine.CrumblingBlock> crumblingBlocks, BatchContext batchContext, BatchedDrawTracker drawTracker) {
var instancesPerType = doCrumblingSort(crumblingBlocks);
for (var entry : instancesPerType.entrySet()) {
// TODO: separate concept of renderstage from drawbuffer
var drawBuffer = RenderTypeExtension.getDrawBufferSet(entry.getKey())
.getBuffer(RenderStage.AFTER_BLOCK_ENTITIES);
var bucket = entry.getValue();
drawBuffer.prepare(bucket.vertexCount);
VertexView vertexView = drawBuffer.slice(0, 0);
long basePtr = vertexView.ptr();
int totalVertices = 0;
for (var pair : bucket.instances) {
var instance = pair.first();
var instancer = pair.second();
totalVertices += bufferOne(instancer, totalVertices, vertexView, drawBuffer, instance);
}
vertexView.ptr(basePtr);
vertexView.vertexCount(totalVertices);
// apply these in bulk
BatchingTransforms.applyDecalUVs(vertexView);
BatchingTransforms.applyMatrices(vertexView, batchContext.matrices());
drawTracker._draw(drawBuffer);
}
}
@NotNull
private static Map<RenderType, CrumblingBucket> doCrumblingSort(List<Engine.CrumblingBlock> crumblingBlocks) {
Map<RenderType, CrumblingBucket> out = new HashMap<>();
for (Engine.CrumblingBlock crumblingBlock : crumblingBlocks) {
var crumblingType = ModelBakery.DESTROY_TYPES.get(crumblingBlock.progress());
for (Instance instance : crumblingBlock.instances()) {
if (!(instance.handle() instanceof InstanceHandleImpl impl)) {
continue;
}
if (!(impl.instancer instanceof BatchedInstancer<?> instancer)) {
continue;
}
var bucket = out.computeIfAbsent(crumblingType, $ -> new CrumblingBucket());
bucket.instances.add(Pair.of(instance, instancer));
for (TransformCall<?> transformCall : instancer.getTransformCalls()) {
var mesh = transformCall.mesh;
bucket.vertexCount += mesh.vertexCount();
}
}
}
return out;
}
private static <I extends Instance> int bufferOne(BatchedInstancer<I> batchedInstancer, int baseVertex, VertexView vertexView, DrawBuffer drawBuffer, Instance instance) {
int totalVertices = 0;
for (TransformCall<I> transformCall : batchedInstancer.getTransformCalls()) {
Mesh mesh = transformCall.mesh.mesh;
vertexView.ptr(drawBuffer.ptrForVertex(baseVertex + totalVertices));
vertexView.vertexCount(mesh.vertexCount());
mesh.write(vertexView);
batchedInstancer.type.getVertexTransformer()
.transform(vertexView, (I) instance);
totalVertices += mesh.vertexCount();
}
return totalVertices;
}
private static class CrumblingBucket {
private int vertexCount;
private final List<Pair<Instance, BatchedInstancer<?>>> instances = new ArrayList<>();
}
}

View file

@ -1,84 +0,0 @@
package com.jozufozu.flywheel.backend.engine.batching;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.api.model.Mesh;
import com.jozufozu.flywheel.api.model.Model;
import com.jozufozu.flywheel.api.task.TaskExecutor;
import com.jozufozu.flywheel.backend.engine.InstancerKey;
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
class BatchedDrawManager extends InstancerStorage<BatchedInstancer<?>> {
public final BatchedDrawTracker drawTracker = new BatchedDrawTracker();
private final Map<RenderStage, BatchedStagePlan> stagePlans = new EnumMap<>(RenderStage.class);
private final Map<VertexFormat, BatchedMeshPool> meshPools = new HashMap<>();
public Collection<BatchedStagePlan> getStagePlans() {
return stagePlans.values();
}
public void renderStage(TaskExecutor executor, RenderStage stage) {
var stagePlan = stagePlans.get(stage);
if (stagePlan == null) {
return;
}
executor.syncUntil(stagePlan.flag::isRaised);
stagePlan.flag.lower();
drawTracker.draw(stage);
}
@Override
protected <I extends Instance> BatchedInstancer<?> create(InstanceType<I> type) {
return new BatchedInstancer<>(type);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected <I extends Instance> void add(InstancerKey<I> key, BatchedInstancer<?> instancer, Model model, RenderStage stage) {
var stagePlan = stagePlans.computeIfAbsent(stage, renderStage -> new BatchedStagePlan(renderStage, drawTracker));
var meshes = model.meshes();
for (var entry : meshes.entrySet()) {
var material = entry.getKey();
RenderType renderType = material.getFallbackRenderType();
var mesh = alloc(entry.getValue(), renderType.format());
var transformCall = new TransformCall<>(instancer, material, mesh);
stagePlan.put(renderType, transformCall);
instancer.addTransformCall((TransformCall) transformCall);
}
}
@Override
public void flush() {
super.flush();
for (var pool : meshPools.values()) {
pool.flush();
}
}
@Override
public void delete() {
super.delete();
meshPools.values()
.forEach(BatchedMeshPool::delete);
meshPools.clear();
}
private BatchedMeshPool.BufferedMesh alloc(Mesh mesh, VertexFormat format) {
return meshPools.computeIfAbsent(format, BatchedMeshPool::new)
.alloc(mesh);
}
}

View file

@ -1,93 +0,0 @@
package com.jozufozu.flywheel.backend.engine.batching;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.extension.BufferBuilderExtension;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
public class BatchedDrawTracker {
private final Map<RenderStage, Set<DrawBuffer>> activeBuffers = new EnumMap<>(RenderStage.class);
{
for (RenderStage stage : RenderStage.values()) {
activeBuffers.put(stage, new HashSet<>());
}
}
private final BufferBuilder scratch;
public BatchedDrawTracker() {
scratch = new BufferBuilder(8);
((BufferBuilderExtension) scratch).flywheel$freeBuffer();
}
public void markActive(RenderStage stage, DrawBuffer buffer) {
synchronized (activeBuffers) {
activeBuffers.get(stage)
.add(buffer);
}
}
// TODO: remove?
public void markInactive(RenderStage stage, DrawBuffer buffer) {
synchronized (activeBuffers) {
activeBuffers.get(stage)
.remove(buffer);
}
}
public boolean hasStage(RenderStage stage) {
synchronized (activeBuffers) {
return !activeBuffers.get(stage)
.isEmpty();
}
}
/**
* Draw and reset all DrawBuffers for the given RenderStage.
*
* @param stage The RenderStage to draw.
*/
public void draw(RenderStage stage) {
// This may appear jank, but flag synchronization in BatchingEngine guarantees that
// the mapped-to Set will not be modified here. We don't have the same guarantee for
// activeBuffers itself, so we need to synchronize to fetch the Set.
Set<DrawBuffer> buffers;
synchronized (activeBuffers) {
buffers = activeBuffers.get(stage);
}
for (DrawBuffer buffer : buffers) {
_draw(buffer);
}
buffers.clear();
}
public void _draw(DrawBuffer buffer) {
if (buffer.hasVertices()) {
BufferBuilderExtension scratch = (BufferBuilderExtension) this.scratch;
buffer.inject(scratch);
buffer.getRenderType()
.end(this.scratch, RenderSystem.getVertexSorting());
}
buffer.reset();
}
/**
* Reset all active DrawBuffers.
*/
public void reset() {
for (Set<DrawBuffer> buffers : activeBuffers.values()) {
for (DrawBuffer buffer : buffers) {
buffer.reset();
}
buffers.clear();
}
}
}

View file

@ -1,40 +0,0 @@
package com.jozufozu.flywheel.backend.engine.batching;
import java.util.ArrayList;
import java.util.List;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
public class BatchedInstancer<I extends Instance> extends AbstractInstancer<I> {
private final List<TransformCall<I>> transformCalls = new ArrayList<>();
public BatchedInstancer(InstanceType<I> type) {
super(type);
}
public List<I> getRange(int start, int end) {
return instances.subList(start, end);
}
public List<I> getAll() {
return instances;
}
public I get(int index) {
return instances.get(index);
}
public void update() {
removeDeletedInstances();
}
public void addTransformCall(TransformCall<I> transformCall) {
transformCalls.add(transformCall);
}
public List<TransformCall<I>> getTransformCalls() {
return transformCalls;
}
}

View file

@ -1,221 +0,0 @@
package com.jozufozu.flywheel.backend.engine.batching;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector4fc;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.model.Mesh;
import com.jozufozu.flywheel.api.vertex.VertexView;
import com.jozufozu.flywheel.api.vertex.VertexViewProviderRegistry;
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
import com.mojang.blaze3d.vertex.VertexFormat;
public class BatchedMeshPool {
private final VertexFormat vertexFormat;
private final VertexView vertexView;
private final int growthMargin;
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
private final List<BufferedMesh> allBuffered = new ArrayList<>();
private final List<BufferedMesh> pendingBuffer = new ArrayList<>();
private MemoryBlock data;
private long byteSize;
private boolean dirty;
private boolean anyToRemove;
/**
* Create a new mesh pool.
*/
public BatchedMeshPool(VertexFormat vertexFormat) {
this.vertexFormat = vertexFormat;
vertexView = VertexViewProviderRegistry.getProvider(vertexFormat).createVertexView();
growthMargin = vertexFormat.getVertexSize() * 128;
}
public VertexFormat getVertexFormat() {
return vertexFormat;
}
/**
* Allocate a mesh in the arena.
*
* @param mesh The mesh to allocate.
* @return A handle to the allocated mesh.
*/
public BufferedMesh alloc(Mesh mesh) {
return meshes.computeIfAbsent(mesh, m -> {
BufferedMesh bufferedMesh = new BufferedMesh(m, byteSize);
byteSize += bufferedMesh.size();
allBuffered.add(bufferedMesh);
pendingBuffer.add(bufferedMesh);
dirty = true;
return bufferedMesh;
});
}
@Nullable
public BufferedMesh get(Mesh mesh) {
return meshes.get(mesh);
}
public void flush() {
if (dirty) {
if (anyToRemove) {
processDeletions();
}
realloc();
bufferPending();
dirty = false;
pendingBuffer.clear();
}
}
private void processDeletions() {
// remove deleted meshes
allBuffered.removeIf(bufferedMesh -> {
boolean deleted = bufferedMesh.isDeleted();
if (deleted) {
meshes.remove(bufferedMesh.mesh);
}
return deleted;
});
// re-evaluate first vertex for each mesh
int byteIndex = 0;
for (BufferedMesh mesh : allBuffered) {
if (mesh.byteIndex != byteIndex) {
pendingBuffer.add(mesh);
}
mesh.byteIndex = byteIndex;
byteIndex += mesh.size();
}
this.byteSize = byteIndex;
this.anyToRemove = false;
}
private void realloc() {
if (byteSize < 0) {
throw new IllegalArgumentException("Size " + byteSize + " < 0");
}
if (byteSize == 0) {
return;
}
if (data == null) {
data = MemoryBlock.malloc(byteSize);
} else if (byteSize > data.size()) {
data = data.realloc(byteSize + growthMargin);
}
}
private void bufferPending() {
try {
for (BufferedMesh mesh : pendingBuffer) {
mesh.write(vertexView);
}
pendingBuffer.clear();
} catch (Exception e) {
Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
}
}
public void delete() {
if (data != null) {
data.free();
}
meshes.clear();
allBuffered.clear();
pendingBuffer.clear();
}
@Override
public String toString() {
return "BatchedMeshPool{" + "vertexFormat=" + vertexFormat + ", byteSize=" + byteSize + ", meshCount=" + meshes.size() + '}';
}
public class BufferedMesh {
public final Mesh mesh;
private final int vertexCount;
private final int byteSize;
private final Vector4fc boundingSphere;
private long byteIndex;
private boolean deleted;
private BufferedMesh(Mesh mesh, long byteIndex) {
this.mesh = mesh;
vertexCount = mesh.vertexCount();
byteSize = vertexCount * vertexFormat.getVertexSize();
boundingSphere = mesh.boundingSphere();
this.byteIndex = byteIndex;
}
public VertexFormat vertexFormat() {
return vertexFormat;
}
public int vertexCount() {
return vertexCount;
}
public int size() {
return byteSize;
}
public Vector4fc boundingSphere() {
return boundingSphere;
}
public boolean isDeleted() {
return deleted;
}
public boolean isEmpty() {
return mesh.isEmpty() || isDeleted();
}
private long ptr() {
return BatchedMeshPool.this.data.ptr() + byteIndex;
}
private void write(VertexView vertexView) {
if (isEmpty()) {
return;
}
vertexView.ptr(ptr());
vertexView.vertexCount(vertexCount);
mesh.write(vertexView);
}
public void copyTo(long ptr) {
if (isEmpty()) {
return;
}
MemoryUtil.memCopy(ptr(), ptr, byteSize);
}
public void delete() {
deleted = true;
BatchedMeshPool.this.dirty = true;
BatchedMeshPool.this.anyToRemove = true;
}
}
}

View file

@ -1,113 +0,0 @@
package com.jozufozu.flywheel.backend.engine.batching;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.task.TaskExecutor;
import com.jozufozu.flywheel.lib.task.Flag;
import com.jozufozu.flywheel.lib.task.SimplyComposedPlan;
import com.jozufozu.flywheel.lib.task.StageFlag;
import com.jozufozu.flywheel.lib.task.Synchronizer;
import net.minecraft.client.renderer.RenderType;
/**
* All the rendering that happens within a render stage.
*/
public class BatchedStagePlan implements SimplyComposedPlan<BatchContext> {
/**
* This flag will be raised when this stage completes execution.
*/
public final Flag flag;
private final RenderStage stage;
private final BatchedDrawTracker tracker;
private final Map<RenderType, BufferPlan> bufferPlans = new HashMap<>();
public BatchedStagePlan(RenderStage stage, BatchedDrawTracker tracker) {
this.flag = new StageFlag(stage);
this.stage = stage;
this.tracker = tracker;
}
@Override
public void execute(TaskExecutor taskExecutor, BatchContext context, Runnable onCompletion) {
if (isEmpty()) {
flag.raise();
onCompletion.run();
return;
}
taskExecutor.execute(() -> {
var sync = new Synchronizer(bufferPlans.size(), () -> {
flag.raise();
onCompletion.run();
});
for (var plan : bufferPlans.values()) {
plan.execute(taskExecutor, context, sync);
}
});
}
public void put(RenderType renderType, TransformCall<?> transformCall) {
bufferPlans.computeIfAbsent(renderType, type -> new BufferPlan(DrawBuffer.get(type, stage)))
.add(transformCall);
}
public boolean isEmpty() {
return bufferPlans.isEmpty();
}
private class BufferPlan implements SimplyComposedPlan<BatchContext> {
private final DrawBuffer buffer;
private final List<TransformCall<?>> transformCalls = new ArrayList<>();
public BufferPlan(DrawBuffer drawBuffer) {
buffer = drawBuffer;
}
public void add(TransformCall<?> transformCall) {
transformCalls.add(transformCall);
}
@Override
public void execute(TaskExecutor taskExecutor, BatchContext ctx, Runnable onCompletion) {
var vertexCount = setupAndCountVertices();
if (vertexCount <= 0) {
onCompletion.run();
return;
}
tracker.markActive(stage, buffer);
buffer.prepare(vertexCount);
var vertexCounter = new AtomicInteger(0);
var planContext = new TransformCall.PlanContext(ctx.frustum(), vertexCounter, buffer, ctx.level(), ctx.matrices());
var synchronizer = new Synchronizer(transformCalls.size(), () -> {
buffer.verticesToDraw(vertexCounter.get());
onCompletion.run();
});
for (var transformCall : transformCalls) {
transformCall.plan()
.execute(taskExecutor, planContext, synchronizer);
}
}
private int setupAndCountVertices() {
int vertices = 0;
for (var transformCall : transformCalls) {
transformCall.setup();
vertices += transformCall.getTotalVertexCount();
}
return vertices;
}
}
}

View file

@ -1,63 +0,0 @@
package com.jozufozu.flywheel.backend.engine.batching;
import java.util.List;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.task.Plan;
import com.jozufozu.flywheel.api.task.TaskExecutor;
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
import com.jozufozu.flywheel.lib.task.DynamicNestedPlan;
import com.jozufozu.flywheel.lib.task.Flag;
import com.jozufozu.flywheel.lib.task.MapContextPlan;
import com.jozufozu.flywheel.lib.task.NamedFlag;
import com.jozufozu.flywheel.lib.task.SimplePlan;
public class BatchingEngine extends AbstractEngine {
private final BatchedDrawManager drawManager = new BatchedDrawManager();
private final Flag flushFlag = new NamedFlag("flushed");
public BatchingEngine(int maxOriginDistance) {
super(maxOriginDistance);
}
@Override
public Plan<RenderContext> createFramePlan() {
return SimplePlan.<RenderContext>of(() -> {
drawManager.flush();
flushFlag.raise();
}).then(MapContextPlan.map((RenderContext ctx) -> BatchContext.create(ctx, renderOrigin))
.to(DynamicNestedPlan.of(drawManager::getStagePlans)));
}
@Override
public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) {
executor.syncUntil(flushFlag::isRaised);
if (stage.isLast()) {
flushFlag.lower();
}
drawManager.renderStage(executor, stage);
}
@Override
public void renderCrumbling(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) {
executor.syncUntil(flushFlag::isRaised);
var batchContext = BatchContext.create(context, this.renderOrigin);
BatchedCrumbling.render(crumblingBlocks, batchContext, this.drawManager.drawTracker);
}
@Override
protected InstancerStorage<? extends AbstractInstancer<?>> getStorage() {
return drawManager;
}
@Override
public void delete() {
drawManager.delete();
}
}

View file

@ -1,52 +0,0 @@
package com.jozufozu.flywheel.backend.engine.batching;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.joml.Vector4f;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.lib.vertex.VertexTransformations;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.SheetedDecalTextureGenerator;
import net.minecraft.core.Direction;
public class BatchingTransforms {
public static void applyMatrices(MutableVertexList vertexList, PoseStack.Pose matrices) {
Matrix4f modelMatrix = matrices.pose();
Matrix3f normalMatrix = matrices.normal();
for (int i = 0; i < vertexList.vertexCount(); i++) {
VertexTransformations.transformPos(vertexList, i, modelMatrix);
VertexTransformations.transformNormal(vertexList, i, normalMatrix);
}
}
/**
* Performs the same operation as {@link SheetedDecalTextureGenerator} in-place.
* <br>
* Call this in world space.
*
* @param vertexList The vertex list to apply the transformations to.
*/
public static void applyDecalUVs(MutableVertexList vertexList) {
Vector3f normal = new Vector3f();
Vector4f pos = new Vector4f();
for (int i = 0; i < vertexList.vertexCount(); i++) {
vertexList.getPos(i, pos);
vertexList.getNormal(i, normal);
Direction direction = Direction.getNearest(normal.x(), normal.y(), normal.z());
pos.rotateY((float)Math.PI);
pos.rotateX((-(float)Math.PI / 2F));
pos.rotate(direction.getRotation());
float u = -pos.x();
float v = -pos.y();
vertexList.u(i, u);
vertexList.v(i, v);
}
}
}

View file

@ -1,78 +0,0 @@
package com.jozufozu.flywheel.backend.engine.batching;
import java.util.concurrent.atomic.AtomicInteger;
import org.joml.FrustumIntersection;
import org.joml.Vector4f;
import org.joml.Vector4fc;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.InstanceBoundingSphereTransformer;
import com.jozufozu.flywheel.api.instance.InstanceVertexTransformer;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.material.MaterialVertexTransformer;
import com.jozufozu.flywheel.api.task.Plan;
import com.jozufozu.flywheel.api.vertex.VertexView;
import com.jozufozu.flywheel.lib.task.ForEachSlicePlan;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.multiplayer.ClientLevel;
public class TransformCall<I extends Instance> {
public final BatchedInstancer<I> instancer;
public final Material material;
public final BatchedMeshPool.BufferedMesh mesh;
private final int meshVertexCount;
private final Plan<PlanContext> drawPlan;
public TransformCall(BatchedInstancer<I> instancer, Material material, BatchedMeshPool.BufferedMesh mesh) {
this.instancer = instancer;
this.material = material;
this.mesh = mesh;
InstanceVertexTransformer<I> instanceVertexTransformer = instancer.type.getVertexTransformer();
InstanceBoundingSphereTransformer<I> boundingSphereTransformer = instancer.type.getBoundingSphereTransformer();
MaterialVertexTransformer materialVertexTransformer = material.getVertexTransformer();
meshVertexCount = mesh.vertexCount();
Vector4fc meshBoundingSphere = mesh.boundingSphere();
drawPlan = ForEachSlicePlan.of(instancer::getAll, (subList, ctx) -> {
VertexView vertexView = ctx.buffer.slice(0, meshVertexCount);
Vector4f boundingSphere = new Vector4f();
for (I instance : subList) {
boundingSphere.set(meshBoundingSphere);
boundingSphereTransformer.transform(boundingSphere, instance);
if (!ctx.frustum.testSphere(boundingSphere.x, boundingSphere.y, boundingSphere.z, boundingSphere.w)) {
continue;
}
final int baseVertex = ctx.vertexCounter.getAndAdd(meshVertexCount);
vertexView.ptr(ctx.buffer.ptrForVertex(baseVertex));
mesh.copyTo(vertexView.ptr());
instanceVertexTransformer.transform(vertexView, instance);
materialVertexTransformer.transform(vertexView, ctx.level);
BatchingTransforms.applyMatrices(vertexView, ctx.matrices);
}
});
}
public void setup() {
instancer.update();
}
public int getTotalVertexCount() {
return meshVertexCount * instancer.getInstanceCount();
}
public Plan<PlanContext> plan() {
return drawPlan;
}
public record PlanContext(FrustumIntersection frustum, AtomicInteger vertexCounter, DrawBuffer buffer, ClientLevel level, PoseStack.Pose matrices) {
}
}

View file

@ -1,13 +1,8 @@
package com.jozufozu.flywheel.lib.instance;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.instance.InstanceBoundingSphereTransformer;
import com.jozufozu.flywheel.api.instance.InstanceHandle;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.api.instance.InstanceVertexTransformer;
import com.jozufozu.flywheel.api.instance.InstanceWriter;
import com.jozufozu.flywheel.api.layout.FloatType;
import com.jozufozu.flywheel.api.layout.IntegerType;
@ -16,8 +11,6 @@ import com.jozufozu.flywheel.api.layout.VectorSize;
import com.jozufozu.flywheel.lib.layout.BufferLayout;
import com.jozufozu.flywheel.lib.layout.CommonItems;
import com.jozufozu.flywheel.lib.layout.LayoutBuilder;
import com.jozufozu.flywheel.lib.math.RenderMath;
import com.jozufozu.flywheel.lib.vertex.VertexTransformations;
import net.minecraft.resources.ResourceLocation;
@ -71,42 +64,4 @@ public class OrientedType implements InstanceType<OrientedInstance> {
return CULL_SHADER;
}
@Override
public InstanceVertexTransformer<OrientedInstance> getVertexTransformer() {
return (vertexList, instance) -> {
Matrix4f modelMatrix = new Matrix4f();
modelMatrix.translate(instance.posX + instance.pivotX, instance.posY + instance.pivotY, instance.posZ + instance.pivotZ);
modelMatrix.rotate(instance.rotation);
modelMatrix.translate(-instance.pivotX, -instance.pivotY, -instance.pivotZ);
Matrix3f normalMatrix = new Matrix3f();
normalMatrix.set(instance.rotation);
float r = RenderMath.uf(instance.r);
float g = RenderMath.uf(instance.g);
float b = RenderMath.uf(instance.b);
float a = RenderMath.uf(instance.a);
int light = instance.getPackedLight();
for (int i = 0; i < vertexList.vertexCount(); i++) {
VertexTransformations.transformPos(vertexList, i, modelMatrix);
VertexTransformations.transformNormal(vertexList, i, normalMatrix);
vertexList.r(i, r);
vertexList.g(i, g);
vertexList.b(i, b);
vertexList.a(i, a);
vertexList.light(i, light);
}
};
}
@Override
public InstanceBoundingSphereTransformer<OrientedInstance> getBoundingSphereTransformer() {
return (boundingSphere, instance) -> {
boundingSphere.sub(instance.pivotX, instance.pivotY, instance.pivotZ, 0);
boundingSphere.rotate(instance.rotation);
boundingSphere.add(instance.posX + instance.pivotX, instance.posY + instance.pivotY, instance.posZ + instance.pivotZ, 0);
};
}
}

View file

@ -1,10 +1,8 @@
package com.jozufozu.flywheel.lib.instance;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.instance.InstanceBoundingSphereTransformer;
import com.jozufozu.flywheel.api.instance.InstanceHandle;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.api.instance.InstanceVertexTransformer;
import com.jozufozu.flywheel.api.instance.InstanceWriter;
import com.jozufozu.flywheel.api.layout.IntegerType;
import com.jozufozu.flywheel.api.layout.Layout;
@ -13,9 +11,6 @@ import com.jozufozu.flywheel.api.layout.VectorSize;
import com.jozufozu.flywheel.lib.layout.BufferLayout;
import com.jozufozu.flywheel.lib.layout.CommonItems;
import com.jozufozu.flywheel.lib.layout.LayoutBuilder;
import com.jozufozu.flywheel.lib.math.MatrixMath;
import com.jozufozu.flywheel.lib.math.RenderMath;
import com.jozufozu.flywheel.lib.vertex.VertexTransformations;
import net.minecraft.resources.ResourceLocation;
@ -67,35 +62,4 @@ public class TransformedType implements InstanceType<TransformedInstance> {
return CULL_SHADER;
}
@Override
public InstanceVertexTransformer<TransformedInstance> getVertexTransformer() {
return (vertexList, instance) -> {
float r = RenderMath.uf(instance.r);
float g = RenderMath.uf(instance.g);
float b = RenderMath.uf(instance.b);
float a = RenderMath.uf(instance.a);
int light = instance.getPackedLight();
for (int i = 0; i < vertexList.vertexCount(); i++) {
VertexTransformations.transformPos(vertexList, i, instance.model);
VertexTransformations.transformNormal(vertexList, i, instance.normal);
vertexList.r(i, r);
vertexList.g(i, g);
vertexList.b(i, b);
vertexList.a(i, a);
vertexList.light(i, light);
}
};
}
@Override
public InstanceBoundingSphereTransformer<TransformedInstance> getBoundingSphereTransformer() {
return (boundingSphere, instance) -> {
var radius = boundingSphere.w;
boundingSphere.w = 1;
boundingSphere.mul(instance.model);
boundingSphere.w = radius * MatrixMath.extractScale(instance.model);
};
}
}

View file

@ -1,120 +1,83 @@
package com.jozufozu.flywheel.lib.material;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.material.MaterialVertexTransformer;
import com.jozufozu.flywheel.api.material.Transparency;
import com.jozufozu.flywheel.lib.math.DiffuseLightCalculator;
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.resources.ResourceLocation;
public final class Materials {
public static final MaterialVertexTransformer SHADING_TRANSFORMER = (vertexList, level) -> {
if (ShadersModHandler.isShaderPackInUse()) {
return;
}
DiffuseLightCalculator diffuseCalc = DiffuseLightCalculator.forLevel(level);
for (int i = 0; i < vertexList.vertexCount(); i++) {
float diffuse = diffuseCalc.getDiffuse(vertexList.normalX(i), vertexList.normalY(i), vertexList.normalZ(i), true);
vertexList.r(i, vertexList.r(i) * diffuse);
vertexList.g(i, vertexList.g(i) * diffuse);
vertexList.b(i, vertexList.b(i) * diffuse);
}
};
private static final ResourceLocation MINECART_LOCATION = new ResourceLocation("textures/entity/minecart.png");
public static final Material CHUNK_SOLID_SHADED = SimpleMaterial.builder()
.useOverlay(false)
.fallbackRenderType(RenderType.solid())
.vertexTransformer(SHADING_TRANSFORMER)
.build();
public static final Material CHUNK_SOLID_UNSHADED = SimpleMaterial.builder()
.useOverlay(false)
.diffuse(false)
.fallbackRenderType(RenderType.solid())
.build();
public static final Material CHUNK_CUTOUT_MIPPED_SHADED = SimpleMaterial.builder()
.cutout(CutoutShaders.HALF)
.useOverlay(false)
.fallbackRenderType(RenderType.cutoutMipped())
.vertexTransformer(SHADING_TRANSFORMER)
.build();
public static final Material CHUNK_CUTOUT_MIPPED_UNSHADED = SimpleMaterial.builder()
.cutout(CutoutShaders.HALF)
.useOverlay(false)
.diffuse(false)
.fallbackRenderType(RenderType.cutoutMipped())
.build();
public static final Material CHUNK_CUTOUT_SHADED = SimpleMaterial.builder()
.cutout(CutoutShaders.ONE_TENTH)
.mipmap(false)
.useOverlay(false)
.fallbackRenderType(RenderType.cutout())
.vertexTransformer(SHADING_TRANSFORMER)
.build();
public static final Material CHUNK_CUTOUT_UNSHADED = SimpleMaterial.builder()
.cutout(CutoutShaders.ONE_TENTH)
.mipmap(false)
.useOverlay(false)
.diffuse(false)
.fallbackRenderType(RenderType.cutout())
.build();
public static final Material CHUNK_TRANSLUCENT_SHADED = SimpleMaterial.builder()
.transparency(Transparency.TRANSLUCENT)
.useOverlay(false)
.fallbackRenderType(RenderType.translucent())
.vertexTransformer(SHADING_TRANSFORMER)
.build();
public static final Material CHUNK_TRANSLUCENT_UNSHADED = SimpleMaterial.builder()
.transparency(Transparency.TRANSLUCENT)
.useOverlay(false)
.diffuse(false)
.fallbackRenderType(RenderType.translucent())
.build();
public static final Material CHUNK_TRIPWIRE_SHADED = SimpleMaterial.builder()
.cutout(CutoutShaders.ONE_TENTH)
.transparency(Transparency.TRANSLUCENT)
.useOverlay(false)
.fallbackRenderType(RenderType.tripwire())
.vertexTransformer(SHADING_TRANSFORMER)
.build();
public static final Material CHUNK_TRIPWIRE_UNSHADED = SimpleMaterial.builder()
.cutout(CutoutShaders.ONE_TENTH)
.transparency(Transparency.TRANSLUCENT)
.useOverlay(false)
.diffuse(false)
.fallbackRenderType(RenderType.tripwire())
.build();
public static final Material CHEST = SimpleMaterial.builder()
.cutout(CutoutShaders.ONE_TENTH)
.texture(Sheets.CHEST_SHEET)
.mipmap(false)
.fallbackRenderType(Sheets.chestSheet())
.build();
public static final Material SHULKER = SimpleMaterial.builder()
.cutout(CutoutShaders.ONE_TENTH)
.texture(Sheets.SHULKER_SHEET)
.mipmap(false)
.backfaceCulling(false)
.fallbackRenderType(Sheets.shulkerBoxSheet())
.build();
public static final Material BELL = SimpleMaterial.builder()
.mipmap(false)
.fallbackRenderType(Sheets.solidBlockSheet())
.build();
public static final Material MINECART = SimpleMaterial.builder()
.texture(MINECART_LOCATION)
.mipmap(false)
.fallbackRenderType(RenderType.entitySolid(MINECART_LOCATION))
.build();
private Materials() {

View file

@ -5,18 +5,13 @@ import com.jozufozu.flywheel.api.material.DepthTest;
import com.jozufozu.flywheel.api.material.FogShader;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.material.MaterialShaders;
import com.jozufozu.flywheel.api.material.MaterialVertexTransformer;
import com.jozufozu.flywheel.api.material.Transparency;
import com.jozufozu.flywheel.api.material.WriteMask;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.InventoryMenu;
public class SimpleMaterial implements Material {
protected final RenderType fallbackRenderType;
protected final MaterialVertexTransformer vertexTransformer;
protected final MaterialShaders shaders;
protected final FogShader fog;
protected final CutoutShader cutout;
@ -36,8 +31,6 @@ public class SimpleMaterial implements Material {
protected final boolean diffuse;
protected SimpleMaterial(Builder builder) {
fallbackRenderType = builder.getFallbackRenderType();
vertexTransformer = builder.getVertexTransformer();
shaders = builder.shaders();
fog = builder.fog();
cutout = builder.cutout();
@ -62,16 +55,6 @@ public class SimpleMaterial implements Material {
return new Builder(material);
}
@Override
public RenderType getFallbackRenderType() {
return fallbackRenderType;
}
@Override
public MaterialVertexTransformer getVertexTransformer() {
return vertexTransformer;
}
@Override
public MaterialShaders shaders() {
return shaders;
@ -143,9 +126,6 @@ public class SimpleMaterial implements Material {
}
public static class Builder implements Material {
protected RenderType fallbackRenderType;
protected MaterialVertexTransformer vertexTransformer;
protected MaterialShaders shaders;
protected FogShader fog;
protected CutoutShader cutout;
@ -165,9 +145,6 @@ public class SimpleMaterial implements Material {
protected boolean diffuse;
public Builder() {
fallbackRenderType = RenderType.solid();
vertexTransformer = (vertexList, level) -> {
};
shaders = StandardMaterialShaders.DEFAULT;
fog = FogShaders.LINEAR;
cutout = CutoutShaders.OFF;
@ -189,8 +166,6 @@ public class SimpleMaterial implements Material {
}
public Builder copyFrom(Material material) {
fallbackRenderType = material.getFallbackRenderType();
vertexTransformer = material.getVertexTransformer();
shaders = material.shaders();
fog = material.fog();
cutout = material.cutout();
@ -208,16 +183,6 @@ public class SimpleMaterial implements Material {
return this;
}
public Builder fallbackRenderType(RenderType type) {
this.fallbackRenderType = type;
return this;
}
public Builder vertexTransformer(MaterialVertexTransformer vertexTransformer) {
this.vertexTransformer = vertexTransformer;
return this;
}
public Builder shaders(MaterialShaders value) {
this.shaders = value;
return this;
@ -288,16 +253,6 @@ public class SimpleMaterial implements Material {
return this;
}
@Override
public RenderType getFallbackRenderType() {
return fallbackRenderType;
}
@Override
public MaterialVertexTransformer getVertexTransformer() {
return vertexTransformer;
}
@Override
public MaterialShaders shaders() {
return shaders;