mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-08 13:26:39 +01:00
MeshP... InstancedMo... DrawC... RenderLists refactor
- RenderLists -> InstancingDrawManager, keeps track of: - Uninitialized models - All Instancers - All DrawCalls via DrawSet - All MeshPools - One MeshPool is now locked to a single VertexType - DrawCall binds instance attributes to avoid making assumptions about mesh attribute count - Yeet crumbling - Simplify GPUInstancerFactory
This commit is contained in:
parent
bb7d971ebe
commit
cfa79ec550
13 changed files with 207 additions and 554 deletions
|
@ -7,7 +7,6 @@ import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.RenderWork;
|
import com.jozufozu.flywheel.backend.RenderWork;
|
||||||
import com.jozufozu.flywheel.backend.ShadersModHandler;
|
import com.jozufozu.flywheel.backend.ShadersModHandler;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.backend.instancing.instancing.MeshPool;
|
|
||||||
import com.jozufozu.flywheel.config.BackendTypeArgument;
|
import com.jozufozu.flywheel.config.BackendTypeArgument;
|
||||||
import com.jozufozu.flywheel.config.FlwCommands;
|
import com.jozufozu.flywheel.config.FlwCommands;
|
||||||
import com.jozufozu.flywheel.config.FlwConfig;
|
import com.jozufozu.flywheel.config.FlwConfig;
|
||||||
|
@ -16,7 +15,6 @@ import com.jozufozu.flywheel.core.PartialModel;
|
||||||
import com.jozufozu.flywheel.core.QuadConverter;
|
import com.jozufozu.flywheel.core.QuadConverter;
|
||||||
import com.jozufozu.flywheel.core.StitchedSprite;
|
import com.jozufozu.flywheel.core.StitchedSprite;
|
||||||
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
|
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
|
||||||
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
|
|
||||||
import com.jozufozu.flywheel.core.model.Models;
|
import com.jozufozu.flywheel.core.model.Models;
|
||||||
import com.jozufozu.flywheel.event.EntityWorldHandler;
|
import com.jozufozu.flywheel.event.EntityWorldHandler;
|
||||||
import com.jozufozu.flywheel.event.ForgeEvents;
|
import com.jozufozu.flywheel.event.ForgeEvents;
|
||||||
|
@ -80,10 +78,8 @@ public class Flywheel {
|
||||||
forgeEventBus.addListener(FlwCommands::registerClientCommands);
|
forgeEventBus.addListener(FlwCommands::registerClientCommands);
|
||||||
|
|
||||||
forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onRendererReload);
|
forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onRendererReload);
|
||||||
forgeEventBus.<ReloadRenderersEvent>addListener(ProgramCompiler::invalidateAll);
|
forgeEventBus.addListener(ProgramCompiler::invalidateAll);
|
||||||
forgeEventBus.addListener(Models::onReload);
|
forgeEventBus.addListener(Models::onReload);
|
||||||
forgeEventBus.addListener(MeshPool::reset);
|
|
||||||
forgeEventBus.addListener(CrumblingRenderer::onReloadRenderers);
|
|
||||||
|
|
||||||
forgeEventBus.addListener(InstancedRenderDispatcher::onReloadRenderers);
|
forgeEventBus.addListener(InstancedRenderDispatcher::onReloadRenderers);
|
||||||
forgeEventBus.addListener(InstancedRenderDispatcher::onRenderStage);
|
forgeEventBus.addListener(InstancedRenderDispatcher::onRenderStage);
|
||||||
|
|
|
@ -7,7 +7,6 @@ import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.core.ComponentRegistry;
|
import com.jozufozu.flywheel.core.ComponentRegistry;
|
||||||
import com.jozufozu.flywheel.core.compile.ContextShader;
|
import com.jozufozu.flywheel.core.compile.ContextShader;
|
||||||
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
|
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
|
||||||
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
|
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
import com.jozufozu.flywheel.core.source.FileResolution;
|
||||||
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
|
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
|
||||||
import com.jozufozu.flywheel.core.source.ShaderSources;
|
import com.jozufozu.flywheel.core.source.ShaderSources;
|
||||||
|
@ -83,9 +82,7 @@ public class Loader implements ResourceManagerReloadListener {
|
||||||
|
|
||||||
ClientLevel world = Minecraft.getInstance().level;
|
ClientLevel world = Minecraft.getInstance().level;
|
||||||
if (Backend.canUseInstancing(world)) {
|
if (Backend.canUseInstancing(world)) {
|
||||||
// TODO: looks like it might be good to have another event here
|
|
||||||
InstancedRenderDispatcher.resetInstanceWorld(world);
|
InstancedRenderDispatcher.resetInstanceWorld(world);
|
||||||
CrumblingRenderer.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,155 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.SortedSet;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.instancing.GPUInstancer;
|
|
||||||
import com.jozufozu.flywheel.core.model.Model;
|
|
||||||
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
import net.minecraft.client.renderer.LevelRenderer;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
|
||||||
import net.minecraft.server.level.BlockDestructionProgress;
|
|
||||||
|
|
||||||
public class SadCrumbling {
|
|
||||||
// public void renderCrumbling(LevelRenderer levelRenderer, ClientLevel level, PoseStack stack, Camera camera, Matrix4f projectionMatrix) {
|
|
||||||
// var dataByStage = getDataByStage(levelRenderer, level);
|
|
||||||
// if (dataByStage.isEmpty()) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var map = modelsToParts(dataByStage);
|
|
||||||
// var stateSnapshot = GameStateRegistry.takeSnapshot();
|
|
||||||
//
|
|
||||||
// Vec3 cameraPosition = camera.getPosition();
|
|
||||||
// var camX = cameraPosition.x - originCoordinate.getX();
|
|
||||||
// var camY = cameraPosition.y - originCoordinate.getY();
|
|
||||||
// var camZ = cameraPosition.z - originCoordinate.getZ();
|
|
||||||
//
|
|
||||||
// // don't want to mutate viewProjection
|
|
||||||
// var vp = projectionMatrix.copy();
|
|
||||||
// vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ);
|
|
||||||
//
|
|
||||||
// GlBuffer instanceBuffer = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER);
|
|
||||||
//
|
|
||||||
// GlVertexArray crumblingVAO = new GlVertexArray();
|
|
||||||
//
|
|
||||||
// crumblingVAO.bind();
|
|
||||||
//
|
|
||||||
// // crumblingVAO.bindAttributes();
|
|
||||||
//
|
|
||||||
// for (var entry : map.entrySet()) {
|
|
||||||
// var model = entry.getKey();
|
|
||||||
// var parts = entry.getValue();
|
|
||||||
//
|
|
||||||
// if (parts.isEmpty()) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// StructType<?> structType = parts.get(0).type;
|
|
||||||
//
|
|
||||||
// for (var meshEntry : model.get()
|
|
||||||
// .entrySet()) {
|
|
||||||
// Material material = meshEntry.getKey();
|
|
||||||
// Mesh mesh = meshEntry.getValue();
|
|
||||||
//
|
|
||||||
// MeshPool.BufferedMesh bufferedMesh = MeshPool.getInstance()
|
|
||||||
// .get(mesh);
|
|
||||||
//
|
|
||||||
// if (bufferedMesh == null || !bufferedMesh.isGpuResident()) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// material.getRenderType().setupRenderState();
|
|
||||||
//
|
|
||||||
// CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// CrumblingProgram program = Contexts.CRUMBLING.getProgram(new ProgramCompiler.Context(Formats.POS_TEX_NORMAL,
|
|
||||||
// structType.getInstanceShader(), material.getVertexShader(), material.getFragmentShader(),
|
|
||||||
// coreShaderInfo.getAdjustedAlphaDiscard(), coreShaderInfo.fogType(),
|
|
||||||
// GameStateRegistry.takeSnapshot()));
|
|
||||||
//
|
|
||||||
// program.bind();
|
|
||||||
// program.uploadUniforms(camX, camY, camZ, vp, level);
|
|
||||||
//
|
|
||||||
// // bufferedMesh.drawInstances();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Map<Model, List<InstancedPart>> modelsToParts(Int2ObjectMap<List<BlockEntityInstance<?>>> dataByStage) {
|
|
||||||
var map = new HashMap<Model, List<InstancedPart>>();
|
|
||||||
|
|
||||||
for (var entry : dataByStage.int2ObjectEntrySet()) {
|
|
||||||
RenderType currentLayer = ModelBakery.DESTROY_TYPES.get(entry.getIntKey());
|
|
||||||
|
|
||||||
// something about when we call this means that the textures are not ready for use on the first frame they should appear
|
|
||||||
if (currentLayer == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var blockEntityInstance : entry.getValue()) {
|
|
||||||
|
|
||||||
for (var part : blockEntityInstance.getCrumblingParts()) {
|
|
||||||
if (part.getOwner() instanceof GPUInstancer<?> instancer) {
|
|
||||||
|
|
||||||
// queue the instances for copying to the crumbling instance buffer
|
|
||||||
map.computeIfAbsent(instancer.parent.getModel(), k -> new ArrayList<>()).add(part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private Int2ObjectMap<List<BlockEntityInstance<?>>> getDataByStage(LevelRenderer levelRenderer, ClientLevel level) {
|
|
||||||
var destructionProgress = ((LevelRendererAccessor) levelRenderer).flywheel$getDestructionProgress();
|
|
||||||
if (destructionProgress.isEmpty()) {
|
|
||||||
return Int2ObjectMaps.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(InstancedRenderDispatcher.getInstanceWorld(level)
|
|
||||||
.getBlockEntities() instanceof BlockEntityInstanceManager beim)) {
|
|
||||||
return Int2ObjectMaps.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
var dataByStage = new Int2ObjectArrayMap<List<BlockEntityInstance<?>>>();
|
|
||||||
|
|
||||||
for (var entry : destructionProgress.long2ObjectEntrySet()) {
|
|
||||||
SortedSet<BlockDestructionProgress> progresses = entry.getValue();
|
|
||||||
|
|
||||||
if (progresses == null || progresses.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int progress = progresses.last()
|
|
||||||
.getProgress();
|
|
||||||
|
|
||||||
var data = dataByStage.computeIfAbsent(progress, $ -> new ArrayList<>());
|
|
||||||
|
|
||||||
long pos = entry.getLongKey();
|
|
||||||
|
|
||||||
beim.getCrumblingInstances(pos, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataByStage;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,23 +4,23 @@ import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
||||||
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
||||||
|
|
||||||
public class DrawCall {
|
public class DrawCall {
|
||||||
|
|
||||||
private final GPUInstancer<?> instancer;
|
final GPUInstancer<?> instancer;
|
||||||
private final Material material;
|
final Material material;
|
||||||
|
private final int meshAttributes;
|
||||||
MeshPool.BufferedMesh bufferedMesh;
|
MeshPool.BufferedMesh bufferedMesh;
|
||||||
GlVertexArray vao;
|
GlVertexArray vao;
|
||||||
|
|
||||||
DrawCall(GPUInstancer<?> instancer, Material material, Mesh mesh) {
|
DrawCall(GPUInstancer<?> instancer, Material material, MeshPool.BufferedMesh mesh) {
|
||||||
this.instancer = instancer;
|
this.instancer = instancer;
|
||||||
this.material = material;
|
this.material = material;
|
||||||
this.vao = new GlVertexArray();
|
this.vao = new GlVertexArray();
|
||||||
this.bufferedMesh = MeshPool.getInstance()
|
this.bufferedMesh = mesh;
|
||||||
.alloc(mesh);
|
this.meshAttributes = this.bufferedMesh.getAttributeCount();
|
||||||
this.instancer.attributeBaseIndex = this.bufferedMesh.getAttributeCount();
|
this.vao.enableArrays(this.meshAttributes + instancer.instanceFormat.getAttributeCount());
|
||||||
this.vao.enableArrays(this.bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Material getMaterial() {
|
public Material getMaterial() {
|
||||||
|
@ -32,11 +32,15 @@ public class DrawCall {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render() {
|
public void render() {
|
||||||
if (invalid()) return;
|
if (invalid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try (var ignored = GlStateTracker.getRestoreState()) {
|
try (var ignored = GlStateTracker.getRestoreState()) {
|
||||||
|
|
||||||
this.instancer.renderSetup(vao);
|
this.instancer.update();
|
||||||
|
|
||||||
|
bindInstancerToVAO();
|
||||||
|
|
||||||
if (this.instancer.glInstanceCount > 0) {
|
if (this.instancer.glInstanceCount > 0) {
|
||||||
bufferedMesh.drawInstances(vao, this.instancer.glInstanceCount);
|
bufferedMesh.drawInstances(vao, this.instancer.glInstanceCount);
|
||||||
|
@ -55,8 +59,24 @@ public class DrawCall {
|
||||||
return this.instancer.vbo == null || bufferedMesh == null || vao == null;
|
return this.instancer.vbo == null || bufferedMesh == null || vao == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void bindInstancerToVAO() {
|
||||||
|
if (!this.instancer.boundTo.add(vao)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var instanceFormat = this.instancer.instanceFormat;
|
||||||
|
|
||||||
|
vao.bindAttributes(this.instancer.vbo, this.meshAttributes, instanceFormat, 0L);
|
||||||
|
|
||||||
|
for (int i = 0; i < instanceFormat.getAttributeCount(); i++) {
|
||||||
|
vao.setAttributeDivisor(this.meshAttributes + i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
if (invalid()) return;
|
if (invalid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
vao.delete();
|
vao.delete();
|
||||||
bufferedMesh.delete();
|
bufferedMesh.delete();
|
||||||
|
|
|
@ -17,19 +17,17 @@ import com.jozufozu.flywheel.core.layout.BufferLayout;
|
||||||
|
|
||||||
public class GPUInstancer<D extends InstancedPart> extends AbstractInstancer<D> {
|
public class GPUInstancer<D extends InstancedPart> extends AbstractInstancer<D> {
|
||||||
|
|
||||||
public final BufferLayout instanceFormat;
|
final BufferLayout instanceFormat;
|
||||||
public final StructType<D> structType;
|
final StructType<D> structType;
|
||||||
public final InstancedModel<D> parent;
|
final Set<GlVertexArray> boundTo = new HashSet<>();
|
||||||
|
|
||||||
GlBuffer vbo;
|
GlBuffer vbo;
|
||||||
int attributeBaseIndex;
|
|
||||||
int glInstanceCount = 0;
|
int glInstanceCount = 0;
|
||||||
|
|
||||||
boolean anyToUpdate;
|
boolean anyToUpdate;
|
||||||
|
|
||||||
public GPUInstancer(InstancedModel<D> parent, StructType<D> type) {
|
|
||||||
|
public GPUInstancer(StructType<D> type) {
|
||||||
super(type);
|
super(type);
|
||||||
this.parent = parent;
|
|
||||||
this.instanceFormat = type.getLayout();
|
this.instanceFormat = type.getLayout();
|
||||||
this.structType = type;
|
this.structType = type;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +38,9 @@ public class GPUInstancer<D extends InstancedPart> extends AbstractInstancer<D>
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
if (vbo != null) return;
|
if (vbo != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER, GlBufferUsage.DYNAMIC_DRAW);
|
vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER, GlBufferUsage.DYNAMIC_DRAW);
|
||||||
vbo.setGrowthMargin(instanceFormat.getStride() * 16);
|
vbo.setGrowthMargin(instanceFormat.getStride() * 16);
|
||||||
|
@ -50,17 +50,7 @@ public class GPUInstancer<D extends InstancedPart> extends AbstractInstancer<D>
|
||||||
return !anyToUpdate && !anyToRemove && glInstanceCount == 0;
|
return !anyToUpdate && !anyToRemove && glInstanceCount == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
void update() {
|
||||||
|
|
||||||
void renderSetup(GlVertexArray vao) {
|
|
||||||
update();
|
|
||||||
|
|
||||||
if (boundTo.add(vao)) {
|
|
||||||
bindInstanceAttributes(vao);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void update() {
|
|
||||||
if (anyToRemove) {
|
if (anyToRemove) {
|
||||||
removeDeletedInstances();
|
removeDeletedInstances();
|
||||||
}
|
}
|
||||||
|
@ -115,14 +105,6 @@ public class GPUInstancer<D extends InstancedPart> extends AbstractInstancer<D>
|
||||||
return vbo.ensureCapacity(requiredSize);
|
return vbo.ensureCapacity(requiredSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindInstanceAttributes(GlVertexArray vao) {
|
|
||||||
vao.bindAttributes(this.vbo, this.attributeBaseIndex, this.instanceFormat, 0L);
|
|
||||||
|
|
||||||
for (int i = 0; i < this.instanceFormat.getAttributeCount(); i++) {
|
|
||||||
vao.setAttributeDivisor(this.attributeBaseIndex + i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void delete() {
|
||||||
vbo.delete();
|
vbo.delete();
|
||||||
|
|
|
@ -2,13 +2,13 @@ package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.instancer.Instancer;
|
import com.jozufozu.flywheel.api.instancer.Instancer;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancerFactory;
|
import com.jozufozu.flywheel.api.instancer.InstancerFactory;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
|
|
||||||
import com.jozufozu.flywheel.core.model.Model;
|
import com.jozufozu.flywheel.core.model.Model;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,53 +17,23 @@ import com.jozufozu.flywheel.core.model.Model;
|
||||||
*/
|
*/
|
||||||
public class GPUInstancerFactory<D extends InstancedPart> implements InstancerFactory<D> {
|
public class GPUInstancerFactory<D extends InstancedPart> implements InstancerFactory<D> {
|
||||||
|
|
||||||
protected final Map<Model, InstancedModel<D>> models = new HashMap<>();
|
protected final Map<Model, GPUInstancer<D>> models = new HashMap<>();
|
||||||
protected final StructType<D> type;
|
protected final StructType<D> type;
|
||||||
private final Consumer<InstancedModel<D>> creationListener;
|
private final BiConsumer<GPUInstancer<?>, Model> creationListener;
|
||||||
|
|
||||||
public GPUInstancerFactory(StructType<D> type, Consumer<InstancedModel<D>> creationListener) {
|
public GPUInstancerFactory(StructType<D> type, BiConsumer<GPUInstancer<?>, Model> creationListener) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.creationListener = creationListener;
|
this.creationListener = creationListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Instancer<D> model(Model modelKey) {
|
public Instancer<D> model(Model modelKey) {
|
||||||
return models.computeIfAbsent(modelKey, this::createInstancer).getInstancer();
|
return models.computeIfAbsent(modelKey, this::createInstancer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getInstanceCount() {
|
private GPUInstancer<D> createInstancer(Model model) {
|
||||||
return models.values()
|
var instancer = new GPUInstancer<>(type);
|
||||||
.stream()
|
this.creationListener.accept(instancer, model);
|
||||||
.map(InstancedModel::getInstancer)
|
|
||||||
.mapToInt(AbstractInstancer::getInstanceCount)
|
|
||||||
.sum();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getVertexCount() {
|
|
||||||
return models.values()
|
|
||||||
.stream()
|
|
||||||
.mapToInt(InstancedModel::getVertexCount)
|
|
||||||
.sum();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
models.values().forEach(InstancedModel::delete);
|
|
||||||
models.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear all instance data without freeing resources.
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
models.values()
|
|
||||||
.stream()
|
|
||||||
.map(InstancedModel::getInstancer)
|
|
||||||
.forEach(AbstractInstancer::clear);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InstancedModel<D> createInstancer(Model model) {
|
|
||||||
var instancer = new InstancedModel<>(type, model);
|
|
||||||
this.creationListener.accept(instancer);
|
|
||||||
return instancer;
|
return instancer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.instancing;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
import com.jozufozu.flywheel.core.model.Model;
|
|
||||||
|
|
||||||
public class InstancedModel<D extends InstancedPart> {
|
|
||||||
|
|
||||||
private final StructType<D> type;
|
|
||||||
private final Model model;
|
|
||||||
private final GPUInstancer<D> instancer;
|
|
||||||
private List<DrawCall> layers;
|
|
||||||
|
|
||||||
public InstancedModel(StructType<D> type, Model model) {
|
|
||||||
this.type = type;
|
|
||||||
this.model = model;
|
|
||||||
this.instancer = new GPUInstancer<>(this, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(RenderLists renderLists) {
|
|
||||||
instancer.init();
|
|
||||||
|
|
||||||
layers = model.getMeshes()
|
|
||||||
.entrySet()
|
|
||||||
.stream()
|
|
||||||
.map(entry -> new DrawCall(instancer, entry.getKey(), entry.getValue()))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
for (DrawCall layer : layers) {
|
|
||||||
renderLists.add(new ShaderState(layer.getMaterial(), layer.getVertexType(), type), layer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Model getModel() {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GPUInstancer<D> getInstancer() {
|
|
||||||
return instancer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getVertexCount() {
|
|
||||||
return model.getVertexCount() * instancer.glInstanceCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
if (instancer.vbo == null) return;
|
|
||||||
|
|
||||||
instancer.delete();
|
|
||||||
|
|
||||||
for (var layer : layers) {
|
|
||||||
layer.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
|
import com.google.common.collect.ListMultimap;
|
||||||
|
import com.jozufozu.flywheel.api.RenderStage;
|
||||||
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
import com.jozufozu.flywheel.core.model.Mesh;
|
||||||
|
import com.jozufozu.flywheel.core.model.Model;
|
||||||
|
|
||||||
|
public class InstancingDrawManager {
|
||||||
|
|
||||||
|
private final List<UninitializedModel> uninitializedModels = new ArrayList<>();
|
||||||
|
private final List<GPUInstancer<?>> allInstancers = new ArrayList<>();
|
||||||
|
private final Map<RenderStage, DrawSet> renderLists = new EnumMap<>(RenderStage.class);
|
||||||
|
private final Map<VertexType, MeshPool> meshPools = new HashMap<>();
|
||||||
|
|
||||||
|
public DrawSet get(RenderStage stage) {
|
||||||
|
return renderLists.getOrDefault(stage, DrawSet.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void create(GPUInstancer<?> gpuInstancer, Model model) {
|
||||||
|
uninitializedModels.add(new UninitializedModel(gpuInstancer, model));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flush() {
|
||||||
|
for (var model : uninitializedModels) {
|
||||||
|
model.instancer()
|
||||||
|
.init();
|
||||||
|
|
||||||
|
add(model.instancer(), model.model());
|
||||||
|
}
|
||||||
|
uninitializedModels.clear();
|
||||||
|
|
||||||
|
for (var pool : meshPools.values()) {
|
||||||
|
pool.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
meshPools.values()
|
||||||
|
.forEach(MeshPool::delete);
|
||||||
|
meshPools.clear();
|
||||||
|
|
||||||
|
renderLists.values()
|
||||||
|
.forEach(DrawSet::delete);
|
||||||
|
renderLists.clear();
|
||||||
|
|
||||||
|
allInstancers.forEach(GPUInstancer::delete);
|
||||||
|
allInstancers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearInstancers() {
|
||||||
|
allInstancers.forEach(GPUInstancer::clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(GPUInstancer<?> instancer, Model model) {
|
||||||
|
var meshes = model.getMeshes();
|
||||||
|
for (var entry : meshes.entrySet()) {
|
||||||
|
DrawCall layer = new DrawCall(instancer, entry.getKey(), alloc(entry.getValue()));
|
||||||
|
var material = layer.getMaterial();
|
||||||
|
var shaderState = new ShaderState(material, layer.getVertexType(), layer.instancer.type);
|
||||||
|
|
||||||
|
renderLists.computeIfAbsent(material.getRenderStage(), DrawSet::new)
|
||||||
|
.put(shaderState, layer);
|
||||||
|
}
|
||||||
|
allInstancers.add(instancer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MeshPool.BufferedMesh alloc(Mesh mesh) {
|
||||||
|
return meshPools.computeIfAbsent(mesh.getVertexType(), MeshPool::new)
|
||||||
|
.alloc(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DrawSet implements Iterable<Map.Entry<ShaderState, Collection<DrawCall>>> {
|
||||||
|
|
||||||
|
public static final DrawSet EMPTY = new DrawSet(ImmutableListMultimap.of());
|
||||||
|
|
||||||
|
final ListMultimap<ShaderState, DrawCall> drawCalls;
|
||||||
|
|
||||||
|
public DrawSet(RenderStage renderStage) {
|
||||||
|
drawCalls = ArrayListMultimap.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawSet(ListMultimap<ShaderState, DrawCall> drawCalls) {
|
||||||
|
this.drawCalls = drawCalls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void delete() {
|
||||||
|
drawCalls.values()
|
||||||
|
.forEach(DrawCall::delete);
|
||||||
|
drawCalls.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(ShaderState shaderState, DrawCall layer) {
|
||||||
|
drawCalls.put(shaderState, layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return drawCalls.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Iterator<Map.Entry<ShaderState, Collection<DrawCall>>> iterator() {
|
||||||
|
return drawCalls.asMap()
|
||||||
|
.entrySet()
|
||||||
|
.iterator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record UninitializedModel(GPUInstancer<?> instancer, Model model) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.instancing;
|
package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -8,7 +7,6 @@ import java.util.Map;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.lwjgl.opengl.GL32;
|
import org.lwjgl.opengl.GL32;
|
||||||
|
|
||||||
import com.google.common.collect.ListMultimap;
|
|
||||||
import com.jozufozu.flywheel.api.RenderStage;
|
import com.jozufozu.flywheel.api.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
@ -43,8 +41,7 @@ public class InstancingEngine implements Engine {
|
||||||
|
|
||||||
protected final Map<StructType<?>, GPUInstancerFactory<?>> factories = new HashMap<>();
|
protected final Map<StructType<?>, GPUInstancerFactory<?>> factories = new HashMap<>();
|
||||||
|
|
||||||
protected final List<InstancedModel<?>> uninitializedModels = new ArrayList<>();
|
protected final InstancingDrawManager drawManager = new InstancingDrawManager();
|
||||||
protected final RenderLists renderLists = new RenderLists();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The set of instance managers that are attached to this engine.
|
* The set of instance managers that are attached to this engine.
|
||||||
|
@ -66,30 +63,20 @@ public class InstancingEngine implements Engine {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private <D extends InstancedPart> GPUInstancerFactory<D> createFactory(StructType<D> type) {
|
private <D extends InstancedPart> GPUInstancerFactory<D> createFactory(StructType<D> type) {
|
||||||
return new GPUInstancerFactory<>(type, uninitializedModels::add);
|
return new GPUInstancerFactory<>(type, drawManager::create);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
|
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
|
||||||
var multimap = renderLists.get(stage);
|
var drawSet = drawManager.get(stage);
|
||||||
|
|
||||||
setup();
|
if (drawSet.isEmpty()) {
|
||||||
|
|
||||||
render(multimap);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Is this useful? Should it be added to the base interface? Currently it is only used for the old CrumblingRenderer.
|
|
||||||
@Deprecated
|
|
||||||
public void renderAll(TaskEngine taskEngine, RenderContext context) {
|
|
||||||
if (renderLists.isEmpty()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
for (var multimap : renderLists.getAll()) {
|
render(drawSet);
|
||||||
render(multimap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setup() {
|
private void setup() {
|
||||||
|
@ -103,12 +90,12 @@ public class InstancingEngine implements Engine {
|
||||||
RenderSystem.enableCull();
|
RenderSystem.enableCull();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void render(ListMultimap<ShaderState, DrawCall> multimap) {
|
protected void render(InstancingDrawManager.DrawSet drawSet) {
|
||||||
if (multimap.isEmpty()) {
|
if (drawSet.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var entry : multimap.asMap().entrySet()) {
|
for (var entry : drawSet) {
|
||||||
var shader = entry.getKey();
|
var shader = entry.getKey();
|
||||||
var drawCalls = entry.getValue();
|
var drawCalls = entry.getValue();
|
||||||
|
|
||||||
|
@ -144,16 +131,10 @@ public class InstancingEngine implements Engine {
|
||||||
UniformBuffer.getInstance().sync();
|
UniformBuffer.getInstance().sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearAll() {
|
|
||||||
factories.values().forEach(GPUInstancerFactory::clear);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void delete() {
|
||||||
factories.values()
|
|
||||||
.forEach(GPUInstancerFactory::delete);
|
|
||||||
|
|
||||||
factories.clear();
|
factories.clear();
|
||||||
|
drawManager.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -183,19 +164,13 @@ public class InstancingEngine implements Engine {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
|
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
|
||||||
for (var model : uninitializedModels) {
|
drawManager.flush();
|
||||||
model.init(renderLists);
|
|
||||||
}
|
|
||||||
uninitializedModels.clear();
|
|
||||||
|
|
||||||
MeshPool.getInstance()
|
|
||||||
.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shiftListeners(int cX, int cY, int cZ) {
|
private void shiftListeners(int cX, int cY, int cZ) {
|
||||||
originCoordinate = new BlockPos(cX, cY, cZ);
|
originCoordinate = new BlockPos(cX, cY, cZ);
|
||||||
|
|
||||||
factories.values().forEach(GPUInstancerFactory::clear);
|
drawManager.clearInstancers();
|
||||||
|
|
||||||
instanceManagers.forEach(InstanceManager::onOriginShift);
|
instanceManagers.forEach(InstanceManager::onOriginShift);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,28 +17,12 @@ import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
||||||
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
import com.jozufozu.flywheel.core.model.Mesh;
|
||||||
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
|
||||||
|
|
||||||
public class MeshPool {
|
public class MeshPool {
|
||||||
|
|
||||||
private static MeshPool allocator;
|
|
||||||
|
|
||||||
public static MeshPool getInstance() {
|
|
||||||
if (allocator == null) {
|
|
||||||
allocator = new MeshPool();
|
|
||||||
}
|
|
||||||
return allocator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void reset(ReloadRenderersEvent ignored) {
|
|
||||||
if (allocator != null) {
|
|
||||||
allocator.delete();
|
|
||||||
allocator = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private final VertexType vertexType;
|
||||||
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
|
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
|
||||||
private final List<BufferedMesh> allBuffered = new ArrayList<>();
|
private final List<BufferedMesh> allBuffered = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -54,10 +38,12 @@ public class MeshPool {
|
||||||
/**
|
/**
|
||||||
* Create a new mesh pool.
|
* Create a new mesh pool.
|
||||||
*/
|
*/
|
||||||
public MeshPool() {
|
public MeshPool(VertexType vertexType) {
|
||||||
vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER);
|
this.vertexType = vertexType;
|
||||||
|
int stride = vertexType.getStride();
|
||||||
|
this.vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER);
|
||||||
|
|
||||||
vbo.setGrowthMargin(2048);
|
this.vbo.setGrowthMargin(stride * 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,6 +54,10 @@ public class MeshPool {
|
||||||
*/
|
*/
|
||||||
public BufferedMesh alloc(Mesh mesh) {
|
public BufferedMesh alloc(Mesh mesh) {
|
||||||
return meshes.computeIfAbsent(mesh, m -> {
|
return meshes.computeIfAbsent(mesh, m -> {
|
||||||
|
if (m.getVertexType() != vertexType) {
|
||||||
|
throw new IllegalArgumentException("Mesh has wrong vertex type");
|
||||||
|
}
|
||||||
|
|
||||||
BufferedMesh bufferedModel = new BufferedMesh(m, byteSize);
|
BufferedMesh bufferedModel = new BufferedMesh(m, byteSize);
|
||||||
byteSize += m.size();
|
byteSize += m.size();
|
||||||
allBuffered.add(bufferedModel);
|
allBuffered.add(bufferedModel);
|
||||||
|
@ -173,25 +163,24 @@ public class MeshPool {
|
||||||
pendingUpload.clear();
|
pendingUpload.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MeshPool{" + "vertexType=" + vertexType + ", byteSize=" + byteSize + ", meshCount=" + meshes.size() + '}';
|
||||||
|
}
|
||||||
|
|
||||||
public class BufferedMesh {
|
public class BufferedMesh {
|
||||||
|
|
||||||
private final ElementBuffer ebo;
|
private final ElementBuffer ebo;
|
||||||
private final Mesh mesh;
|
private final Mesh mesh;
|
||||||
private final BufferLayout layout;
|
|
||||||
private long byteIndex;
|
private long byteIndex;
|
||||||
|
|
||||||
private boolean deleted;
|
private boolean deleted;
|
||||||
|
|
||||||
private boolean gpuResident = false;
|
|
||||||
|
|
||||||
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
||||||
|
|
||||||
public BufferedMesh(Mesh mesh, long byteIndex) {
|
public BufferedMesh(Mesh mesh, long byteIndex) {
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
this.byteIndex = byteIndex;
|
this.byteIndex = byteIndex;
|
||||||
this.ebo = mesh.createEBO();
|
this.ebo = mesh.createEBO();
|
||||||
this.layout = mesh.getVertexType()
|
|
||||||
.getLayout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawCall(GlVertexArray vao) {
|
public void drawCall(GlVertexArray vao) {
|
||||||
|
@ -199,7 +188,9 @@ public class MeshPool {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawInstances(GlVertexArray vao, int instanceCount) {
|
public void drawInstances(GlVertexArray vao, int instanceCount) {
|
||||||
if (hasAnythingToRender()) return;
|
if (hasAnythingToRender()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setup(vao);
|
setup(vao);
|
||||||
|
|
||||||
|
@ -221,7 +212,7 @@ public class MeshPool {
|
||||||
private void setup(GlVertexArray vao) {
|
private void setup(GlVertexArray vao) {
|
||||||
if (this.boundTo.add(vao)) {
|
if (this.boundTo.add(vao)) {
|
||||||
vao.enableArrays(getAttributeCount());
|
vao.enableArrays(getAttributeCount());
|
||||||
vao.bindAttributes(MeshPool.this.vbo, 0, this.layout, this.byteIndex);
|
vao.bindAttributes(MeshPool.this.vbo, 0, vertexType.getLayout(), this.byteIndex);
|
||||||
}
|
}
|
||||||
vao.bindElementArray(this.ebo.buffer);
|
vao.bindElementArray(this.ebo.buffer);
|
||||||
vao.bind();
|
vao.bind();
|
||||||
|
@ -241,19 +232,14 @@ public class MeshPool {
|
||||||
this.mesh.write(ptr + byteIndex);
|
this.mesh.write(ptr + byteIndex);
|
||||||
|
|
||||||
this.boundTo.clear();
|
this.boundTo.clear();
|
||||||
this.gpuResident = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getAttributeCount() {
|
public int getAttributeCount() {
|
||||||
return this.layout.getAttributeCount();
|
return vertexType.getLayout().getAttributeCount();
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGpuResident() {
|
|
||||||
return gpuResident;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VertexType getVertexType() {
|
public VertexType getVertexType() {
|
||||||
return this.mesh.getVertexType();
|
return vertexType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.instancing;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
|
||||||
import com.google.common.collect.ImmutableListMultimap;
|
|
||||||
import com.google.common.collect.ListMultimap;
|
|
||||||
import com.jozufozu.flywheel.api.RenderStage;
|
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
|
||||||
|
|
||||||
public class RenderLists {
|
|
||||||
|
|
||||||
public final Map<RenderStage, ListMultimap<ShaderState, DrawCall>> renderLists = new EnumMap<>(RenderStage.class);
|
|
||||||
|
|
||||||
public ListMultimap<ShaderState, DrawCall> get(RenderStage stage) {
|
|
||||||
var renderList = renderLists.get(stage);
|
|
||||||
if (renderList == null) {
|
|
||||||
return ImmutableListMultimap.of();
|
|
||||||
}
|
|
||||||
return renderList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(ShaderState shaderState, DrawCall layer) {
|
|
||||||
Material material = shaderState.material();
|
|
||||||
|
|
||||||
renderLists.computeIfAbsent(material.getRenderStage(), k -> ArrayListMultimap.create())
|
|
||||||
.put(shaderState, layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return renderLists.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ListMultimap<ShaderState, DrawCall>> getAll() {
|
|
||||||
return renderLists.values();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,146 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.crumbling;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.SortedSet;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
|
|
||||||
import com.jozufozu.flywheel.core.Components;
|
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
|
||||||
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
|
||||||
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
|
|
||||||
import com.jozufozu.flywheel.util.Lazy;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
import net.minecraft.client.renderer.LevelRenderer;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.server.level.BlockDestructionProgress;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
|
|
||||||
// TODO: merge directly into InstancingEngine for efficiency
|
|
||||||
/**
|
|
||||||
* Responsible for rendering the crumbling overlay for instanced block entities.
|
|
||||||
*/
|
|
||||||
public class CrumblingRenderer {
|
|
||||||
|
|
||||||
private static Lazy<State> STATE;
|
|
||||||
|
|
||||||
static {
|
|
||||||
_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void renderCrumbling(RenderContext context) {
|
|
||||||
// TODO: one pass base/crumbling
|
|
||||||
if (true) return;
|
|
||||||
|
|
||||||
Int2ObjectMap<List<BlockEntity>> activeStages = getActiveStageBlockEntities(context.renderer(), context.level());
|
|
||||||
if (activeStages.isEmpty()) return;
|
|
||||||
|
|
||||||
try (var restoreState = GlStateTracker.getRestoreState()) {
|
|
||||||
State state = STATE.get();
|
|
||||||
var instanceManager = state.instanceManager;
|
|
||||||
var engine = state.instancerManager;
|
|
||||||
|
|
||||||
renderCrumblingInner(activeStages, instanceManager, engine, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void renderCrumblingInner(Int2ObjectMap<List<BlockEntity>> activeStages, InstanceManager<BlockEntity> instanceManager, CrumblingEngine engine, RenderContext ctx) {
|
|
||||||
for (Int2ObjectMap.Entry<List<BlockEntity>> stage : activeStages.int2ObjectEntrySet()) {
|
|
||||||
RenderType currentLayer = ModelBakery.DESTROY_TYPES.get(stage.getIntKey());
|
|
||||||
|
|
||||||
// something about when we call this means that the textures are not ready for use on the first frame they should appear
|
|
||||||
if (currentLayer != null) {
|
|
||||||
stage.getValue().forEach(instanceManager::add);
|
|
||||||
|
|
||||||
instanceManager.beginFrame(SerialTaskEngine.INSTANCE, ctx);
|
|
||||||
engine.beginFrame(SerialTaskEngine.INSTANCE, ctx);
|
|
||||||
|
|
||||||
engine.renderAll(SerialTaskEngine.INSTANCE, ctx);
|
|
||||||
|
|
||||||
instanceManager.invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Associate each breaking stage with a list of all block entities at that stage.
|
|
||||||
*/
|
|
||||||
private static Int2ObjectMap<List<BlockEntity>> getActiveStageBlockEntities(LevelRenderer levelRenderer, ClientLevel level) {
|
|
||||||
Long2ObjectMap<SortedSet<BlockDestructionProgress>> destructionProgress = ((LevelRendererAccessor) levelRenderer).flywheel$getDestructionProgress();
|
|
||||||
if (destructionProgress.isEmpty()) {
|
|
||||||
return Int2ObjectMaps.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
Int2ObjectMap<List<BlockEntity>> breakingEntities = new Int2ObjectArrayMap<>();
|
|
||||||
BlockPos.MutableBlockPos breakingPos = new BlockPos.MutableBlockPos();
|
|
||||||
|
|
||||||
for (Long2ObjectMap.Entry<SortedSet<BlockDestructionProgress>> entry : destructionProgress.long2ObjectEntrySet()) {
|
|
||||||
breakingPos.set(entry.getLongKey());
|
|
||||||
|
|
||||||
SortedSet<BlockDestructionProgress> progresses = entry.getValue();
|
|
||||||
if (progresses != null && !progresses.isEmpty()) {
|
|
||||||
int progress = progresses.last()
|
|
||||||
.getProgress();
|
|
||||||
if (progress >= 0) {
|
|
||||||
BlockEntity blockEntity = level.getBlockEntity(breakingPos);
|
|
||||||
|
|
||||||
if (blockEntity != null) {
|
|
||||||
List<BlockEntity> blockEntities = breakingEntities.computeIfAbsent(progress, $ -> new ArrayList<>());
|
|
||||||
blockEntities.add(blockEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return breakingEntities;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onReloadRenderers(ReloadRenderersEvent event) {
|
|
||||||
ClientLevel world = event.getWorld();
|
|
||||||
if (Backend.isOn() && world != null) {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void reset() {
|
|
||||||
STATE.ifPresent(State::kill);
|
|
||||||
_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void _init() {
|
|
||||||
STATE = Lazy.of(State::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class State {
|
|
||||||
private final CrumblingEngine instancerManager;
|
|
||||||
private final InstanceManager<BlockEntity> instanceManager;
|
|
||||||
|
|
||||||
private State() {
|
|
||||||
instancerManager = new CrumblingEngine();
|
|
||||||
instanceManager = new CrumblingInstanceManager(instancerManager);
|
|
||||||
instancerManager.attachManagers(instanceManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void kill() {
|
|
||||||
instancerManager.delete();
|
|
||||||
instanceManager.invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class CrumblingEngine extends InstancingEngine {
|
|
||||||
public CrumblingEngine() {
|
|
||||||
super(Components.CRUMBLING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,7 +14,6 @@ import com.jozufozu.flywheel.api.RenderStage;
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
|
|
||||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
||||||
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
||||||
import com.jozufozu.flywheel.event.RenderStageEvent;
|
import com.jozufozu.flywheel.event.RenderStageEvent;
|
||||||
|
@ -66,7 +65,7 @@ public class LevelRendererMixin {
|
||||||
), method = "renderLevel")
|
), method = "renderLevel")
|
||||||
private void renderCrumbling(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci) {
|
private void renderCrumbling(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci) {
|
||||||
if (renderContext != null) {
|
if (renderContext != null) {
|
||||||
CrumblingRenderer.renderCrumbling(renderContext);
|
// TODO: Crumbling
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue