mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-28 07:56:26 +01:00
Better draw call organization
- InstancingEngine takes over tracking individual draw calls - Many draw calls are associated with a single ShaderState - Each ShaderState will bind one shader program - Make Material a record - Inline Renderable and move InstancedModel.Layer to DrawCall
This commit is contained in:
parent
5dd72a4ba7
commit
cdfddba35a
9 changed files with 185 additions and 203 deletions
|
@ -4,26 +4,6 @@ import com.jozufozu.flywheel.core.source.FileResolution;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
|
||||||
public class Material {
|
public record Material(RenderType renderType, FileResolution vertexShader, FileResolution fragmentShader) {
|
||||||
protected final RenderType renderType;
|
|
||||||
protected final FileResolution vertexShader;
|
|
||||||
protected final FileResolution fragmentShader;
|
|
||||||
|
|
||||||
public Material(RenderType renderType, FileResolution vertexShader, FileResolution fragmentShader) {
|
|
||||||
this.renderType = renderType;
|
|
||||||
this.vertexShader = vertexShader;
|
|
||||||
this.fragmentShader = fragmentShader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RenderType getRenderType() {
|
|
||||||
return renderType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileResolution getVertexShader() {
|
|
||||||
return vertexShader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileResolution getFragmentShader() {
|
|
||||||
return fragmentShader;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
||||||
|
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
||||||
|
import com.jozufozu.flywheel.backend.model.MeshPool;
|
||||||
|
import com.jozufozu.flywheel.core.model.Mesh;
|
||||||
|
|
||||||
|
public class DrawCall {
|
||||||
|
|
||||||
|
final Material material;
|
||||||
|
private final GPUInstancer<?> instancer;
|
||||||
|
MeshPool.BufferedMesh bufferedMesh;
|
||||||
|
GlVertexArray vao;
|
||||||
|
|
||||||
|
DrawCall(GPUInstancer<?> instancer, Material material, Mesh mesh) {
|
||||||
|
this.instancer = instancer;
|
||||||
|
this.material = material;
|
||||||
|
this.vao = new GlVertexArray();
|
||||||
|
this.bufferedMesh = MeshPool.getInstance()
|
||||||
|
.alloc(mesh);
|
||||||
|
this.instancer.attributeBaseIndex = this.bufferedMesh.getAttributeCount();
|
||||||
|
this.vao.enableArrays(this.bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Material getMaterial() {
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VertexType getVertexType() {
|
||||||
|
return bufferedMesh.getVertexType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render() {
|
||||||
|
if (invalid()) return;
|
||||||
|
|
||||||
|
try (var ignored = GlStateTracker.getRestoreState()) {
|
||||||
|
|
||||||
|
this.instancer.renderSetup(vao);
|
||||||
|
|
||||||
|
if (this.instancer.glInstanceCount > 0) {
|
||||||
|
bufferedMesh.drawInstances(vao, this.instancer.glInstanceCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldRemove() {
|
||||||
|
return invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only {@code true} if the InstancedModel has been destroyed.
|
||||||
|
*/
|
||||||
|
private boolean invalid() {
|
||||||
|
return this.instancer.vbo == null || bufferedMesh == null || vao == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
if (invalid()) return;
|
||||||
|
|
||||||
|
vao.delete();
|
||||||
|
bufferedMesh.delete();
|
||||||
|
|
||||||
|
vao = null;
|
||||||
|
bufferedMesh = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,9 @@
|
||||||
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.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
|
||||||
import com.google.common.collect.HashMultimap;
|
|
||||||
import com.google.common.collect.ListMultimap;
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
import com.jozufozu.flywheel.api.InstancedPart;
|
import com.jozufozu.flywheel.api.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.Instancer;
|
import com.jozufozu.flywheel.api.Instancer;
|
||||||
import com.jozufozu.flywheel.api.InstancerFactory;
|
import com.jozufozu.flywheel.api.InstancerFactory;
|
||||||
|
@ -26,13 +20,11 @@ public class GPUInstancerFactory<D extends InstancedPart> implements InstancerFa
|
||||||
|
|
||||||
protected final Map<ModelSupplier, InstancedModel<D>> models = new HashMap<>();
|
protected final Map<ModelSupplier, InstancedModel<D>> models = new HashMap<>();
|
||||||
protected final StructType<D> type;
|
protected final StructType<D> type;
|
||||||
|
private final Consumer<InstancedModel<D>> creationListener;
|
||||||
|
|
||||||
protected final List<InstancedModel<D>> uninitialized = new ArrayList<>();
|
public GPUInstancerFactory(StructType<D> type, Consumer<InstancedModel<D>> creationListener) {
|
||||||
|
|
||||||
private final ListMultimap<RenderType, Renderable> renderLists = ArrayListMultimap.create();
|
|
||||||
|
|
||||||
public GPUInstancerFactory(StructType<D> type) {
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.creationListener = creationListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -58,7 +50,6 @@ public class GPUInstancerFactory<D extends InstancedPart> implements InstancerFa
|
||||||
public void delete() {
|
public void delete() {
|
||||||
models.values().forEach(InstancedModel::delete);
|
models.values().forEach(InstancedModel::delete);
|
||||||
models.clear();
|
models.clear();
|
||||||
renderLists.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,35 +62,9 @@ public class GPUInstancerFactory<D extends InstancedPart> implements InstancerFa
|
||||||
.forEach(GPUInstancer::clear);
|
.forEach(GPUInstancer::clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
|
||||||
for (var instanced : uninitialized) {
|
|
||||||
instanced.init();
|
|
||||||
|
|
||||||
for (Renderable renderable : instanced.getLayers()) {
|
|
||||||
renderLists.put(renderable.getMaterial()
|
|
||||||
.getRenderType(), renderable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uninitialized.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private InstancedModel<D> createInstancer(ModelSupplier model) {
|
private InstancedModel<D> createInstancer(ModelSupplier model) {
|
||||||
var instancer = new InstancedModel<>(type, model);
|
var instancer = new InstancedModel<>(type, model);
|
||||||
uninitialized.add(instancer);
|
this.creationListener.accept(instancer);
|
||||||
return instancer;
|
return instancer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds all the RenderTypes that this InstancerFactory will render to the given set.
|
|
||||||
* @param layersToProcess The set of RenderTypes that the InstancingEngine will process.
|
|
||||||
*/
|
|
||||||
public void gatherLayers(Set<RenderType> layersToProcess) {
|
|
||||||
layersToProcess.addAll(renderLists.keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Renderable> getRenderList(RenderType type) {
|
|
||||||
var out = renderLists.get(type);
|
|
||||||
out.removeIf(Renderable::shouldRemove);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,108 +1,35 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.instancing;
|
package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.jozufozu.flywheel.api.InstancedPart;
|
import com.jozufozu.flywheel.api.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
|
||||||
import com.jozufozu.flywheel.backend.model.MeshPool;
|
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
|
||||||
import com.jozufozu.flywheel.core.model.ModelSupplier;
|
import com.jozufozu.flywheel.core.model.ModelSupplier;
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
|
||||||
|
|
||||||
public class InstancedModel<D extends InstancedPart> {
|
public class InstancedModel<D extends InstancedPart> {
|
||||||
|
|
||||||
public final GPUInstancer<D> instancer;
|
public final GPUInstancer<D> instancer;
|
||||||
public final ModelSupplier model;
|
public final ModelSupplier model;
|
||||||
private List<Layer> layers;
|
private final StructType<D> type;
|
||||||
|
private List<DrawCall> layers;
|
||||||
|
|
||||||
public InstancedModel(StructType<D> type, ModelSupplier model) {
|
public InstancedModel(StructType<D> type, ModelSupplier model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.instancer = new GPUInstancer<>(this, type);
|
this.instancer = new GPUInstancer<>(this, type);
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init(RenderLists renderLists) {
|
||||||
instancer.init();
|
instancer.init();
|
||||||
|
|
||||||
buildLayers();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<? extends Renderable> getLayers() {
|
|
||||||
return layers;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildLayers() {
|
|
||||||
layers = model.get()
|
layers = model.get()
|
||||||
.entrySet()
|
.entrySet()
|
||||||
.stream()
|
.stream()
|
||||||
.map(entry -> new Layer(entry.getKey(), entry.getValue()))
|
.map(entry -> new DrawCall(instancer, entry.getKey(), entry.getValue()))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
|
||||||
|
|
||||||
private class Layer implements Renderable {
|
for (DrawCall layer : layers) {
|
||||||
|
renderLists.add(new ShaderState(layer.material, layer.getVertexType(), type), layer);
|
||||||
final Material material;
|
|
||||||
MeshPool.BufferedMesh bufferedMesh;
|
|
||||||
GlVertexArray vao;
|
|
||||||
|
|
||||||
private Layer(Material material, Mesh mesh) {
|
|
||||||
this.material = material;
|
|
||||||
vao = new GlVertexArray();
|
|
||||||
bufferedMesh = MeshPool.getInstance()
|
|
||||||
.alloc(mesh);
|
|
||||||
instancer.attributeBaseIndex = bufferedMesh.getAttributeCount();
|
|
||||||
vao.enableArrays(bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Material getMaterial() {
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VertexType getVertexType() {
|
|
||||||
return bufferedMesh.getVertexType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render() {
|
|
||||||
if (invalid()) return;
|
|
||||||
|
|
||||||
try (var ignored = GlStateTracker.getRestoreState()) {
|
|
||||||
|
|
||||||
instancer.renderSetup(vao);
|
|
||||||
|
|
||||||
if (instancer.glInstanceCount > 0) {
|
|
||||||
bufferedMesh.drawInstances(vao, instancer.glInstanceCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldRemove() {
|
|
||||||
return invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only {@code true} if the InstancedModel has been destroyed.
|
|
||||||
*/
|
|
||||||
private boolean invalid() {
|
|
||||||
return instancer.vbo == null || bufferedMesh == null || vao == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
if (invalid()) return;
|
|
||||||
|
|
||||||
vao.delete();
|
|
||||||
bufferedMesh.delete();
|
|
||||||
|
|
||||||
vao = null;
|
|
||||||
bufferedMesh = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,16 @@ package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.google.common.collect.ListMultimap;
|
||||||
|
import com.google.common.collect.Multimaps;
|
||||||
import com.jozufozu.flywheel.api.InstancedPart;
|
import com.jozufozu.flywheel.api.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
|
@ -34,6 +34,7 @@ import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
import com.jozufozu.flywheel.core.model.Mesh;
|
||||||
import com.jozufozu.flywheel.core.model.ModelSupplier;
|
import com.jozufozu.flywheel.core.model.ModelSupplier;
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||||
|
import com.jozufozu.flywheel.core.source.FileResolution;
|
||||||
import com.jozufozu.flywheel.core.vertex.Formats;
|
import com.jozufozu.flywheel.core.vertex.Formats;
|
||||||
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
|
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
|
||||||
import com.jozufozu.flywheel.util.Textures;
|
import com.jozufozu.flywheel.util.Textures;
|
||||||
|
@ -65,7 +66,8 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
|
|
||||||
protected final Map<StructType<? extends InstancedPart>, GPUInstancerFactory<?>> factories = new HashMap<>();
|
protected final Map<StructType<? extends InstancedPart>, GPUInstancerFactory<?>> factories = new HashMap<>();
|
||||||
|
|
||||||
protected final Set<RenderType> layersToProcess = new HashSet<>();
|
protected final List<InstancedModel<?>> uninitializedModels = new ArrayList<>();
|
||||||
|
protected final RenderLists renderLists = new RenderLists();
|
||||||
|
|
||||||
private final WeakHashSet<OriginShiftListener> listeners;
|
private final WeakHashSet<OriginShiftListener> listeners;
|
||||||
private int vertexCount;
|
private int vertexCount;
|
||||||
|
@ -81,7 +83,12 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public <D extends InstancedPart> GPUInstancerFactory<D> factory(StructType<D> type) {
|
public <D extends InstancedPart> GPUInstancerFactory<D> factory(StructType<D> type) {
|
||||||
return (GPUInstancerFactory<D>) factories.computeIfAbsent(type, GPUInstancerFactory::new);
|
return (GPUInstancerFactory<D>) factories.computeIfAbsent(type, this::createFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private <D extends InstancedPart> GPUInstancerFactory<D> createFactory(StructType<D> type) {
|
||||||
|
return new GPUInstancerFactory<>(type, uninitializedModels::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -94,15 +101,14 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
var vp = context.viewProjection().copy();
|
var vp = context.viewProjection().copy();
|
||||||
vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ);
|
vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ);
|
||||||
|
|
||||||
for (RenderType renderType : layersToProcess) {
|
for (RenderType renderType : renderLists.drainLayers()) {
|
||||||
render(renderType, camX, camY, camZ, vp, context.level());
|
render(renderType, camX, camY, camZ, vp, context.level());
|
||||||
}
|
}
|
||||||
layersToProcess.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderSpecificType(TaskEngine taskEngine, RenderContext context, RenderType type) {
|
public void renderSpecificType(TaskEngine taskEngine, RenderContext context, RenderType type) {
|
||||||
if (!layersToProcess.remove(type)) {
|
if (!renderLists.process(type)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,37 +127,50 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
vertexCount = 0;
|
vertexCount = 0;
|
||||||
instanceCount = 0;
|
instanceCount = 0;
|
||||||
|
|
||||||
|
var multimap = renderLists.get(type);
|
||||||
|
|
||||||
|
if (multimap.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
render(type, multimap, camX, camY, camZ, viewProjection, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void render(RenderType type, ListMultimap<ShaderState, DrawCall> multimap, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) {
|
||||||
type.setupRenderState();
|
type.setupRenderState();
|
||||||
Textures.bindActiveTextures();
|
Textures.bindActiveTextures();
|
||||||
CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
|
CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
|
||||||
|
|
||||||
for (var entry : factories.entrySet()) {
|
for (var entry : Multimaps.asMap(multimap).entrySet()) {
|
||||||
var instanceType = entry.getKey();
|
var shader = entry.getKey();
|
||||||
var factory = entry.getValue();
|
var drawCalls = entry.getValue();
|
||||||
|
|
||||||
var toRender = factory.getRenderList(type);
|
drawCalls.removeIf(DrawCall::shouldRemove);
|
||||||
|
|
||||||
if (toRender.isEmpty()) {
|
if (drawCalls.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Renderable renderable : toRender) {
|
setup(shader, coreShaderInfo, camX, camY, camZ, viewProjection, level);
|
||||||
setup(instanceType, renderable.getMaterial(), coreShaderInfo, camX, camY, camZ, viewProjection, level, renderable.getVertexType());
|
|
||||||
|
|
||||||
renderable.render();
|
for (var drawCall : drawCalls) {
|
||||||
|
drawCall.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceCount += factory.getInstanceCount();
|
|
||||||
vertexCount += factory.getVertexCount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type.clearRenderState();
|
type.clearRenderState();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected P setup(StructType<?> instanceType, Material material, CoreShaderInfo coreShaderInfo, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level, VertexType vertexType) {
|
protected P setup(ShaderState desc, CoreShaderInfo coreShaderInfo, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) {
|
||||||
|
|
||||||
P program = context.getProgram(new ProgramCompiler.Context(vertexType, instanceType.getInstanceShader(),
|
VertexType vertexType = desc.vertex();
|
||||||
material.getVertexShader(), material.getFragmentShader(), coreShaderInfo.getAdjustedAlphaDiscard(),
|
FileResolution instanceShader = desc.instance()
|
||||||
|
.getInstanceShader();
|
||||||
|
Material material = desc.material();
|
||||||
|
|
||||||
|
P program = context.getProgram(new ProgramCompiler.Context(vertexType, instanceShader,
|
||||||
|
material.vertexShader(), material.fragmentShader(), coreShaderInfo.getAdjustedAlphaDiscard(),
|
||||||
coreShaderInfo.fogType(), GameStateRegistry.takeSnapshot()));
|
coreShaderInfo.fogType(), GameStateRegistry.takeSnapshot()));
|
||||||
|
|
||||||
program.bind();
|
program.bind();
|
||||||
|
@ -190,12 +209,12 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
public void beginFrame(Camera info) {
|
public void beginFrame(Camera info) {
|
||||||
checkOriginDistance(info);
|
checkOriginDistance(info);
|
||||||
|
|
||||||
|
for (var factory : uninitializedModels) {
|
||||||
for (GPUInstancerFactory<?> factory : factories.values()) {
|
factory.init(renderLists);
|
||||||
factory.init();
|
|
||||||
|
|
||||||
factory.gatherLayers(layersToProcess);
|
|
||||||
}
|
}
|
||||||
|
uninitializedModels.clear();
|
||||||
|
|
||||||
|
renderLists.prepare();
|
||||||
|
|
||||||
MeshPool.getInstance()
|
MeshPool.getInstance()
|
||||||
.flush();
|
.flush();
|
||||||
|
@ -279,13 +298,13 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
material.getRenderType().setupRenderState();
|
material.renderType().setupRenderState();
|
||||||
|
|
||||||
CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
|
CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
|
||||||
|
|
||||||
|
|
||||||
CrumblingProgram program = Contexts.CRUMBLING.getProgram(new ProgramCompiler.Context(Formats.POS_TEX_NORMAL,
|
CrumblingProgram program = Contexts.CRUMBLING.getProgram(new ProgramCompiler.Context(Formats.POS_TEX_NORMAL,
|
||||||
structType.getInstanceShader(), material.getVertexShader(), material.getFragmentShader(),
|
structType.getInstanceShader(), material.vertexShader(), material.fragmentShader(),
|
||||||
coreShaderInfo.getAdjustedAlphaDiscard(), coreShaderInfo.fogType(),
|
coreShaderInfo.getAdjustedAlphaDiscard(), coreShaderInfo.fogType(),
|
||||||
GameStateRegistry.takeSnapshot()));
|
GameStateRegistry.takeSnapshot()));
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
|
import com.google.common.collect.ListMultimap;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
|
||||||
|
public class RenderLists {
|
||||||
|
|
||||||
|
private Map<RenderType, ListMultimap<ShaderState, DrawCall>> renderLists = new HashMap<>();
|
||||||
|
public final Set<RenderType> layersToProcess = new HashSet<>();
|
||||||
|
|
||||||
|
public ListMultimap<ShaderState, DrawCall> get(RenderType type) {
|
||||||
|
return renderLists.computeIfAbsent(type, k -> ArrayListMultimap.create());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(ShaderState shaderState, DrawCall layer) {
|
||||||
|
RenderType renderType = shaderState.material()
|
||||||
|
.renderType();
|
||||||
|
|
||||||
|
get(renderType).put(shaderState, layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void prepare() {
|
||||||
|
layersToProcess.clear();
|
||||||
|
|
||||||
|
layersToProcess.addAll(renderLists.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<? extends RenderType> drainLayers() {
|
||||||
|
var out = new HashSet<>(layersToProcess);
|
||||||
|
layersToProcess.clear();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check and mark a layer as processed.
|
||||||
|
* @param type The layer to check.
|
||||||
|
* @return {@code true} if the layer should be processed.
|
||||||
|
*/
|
||||||
|
public boolean process(RenderType type) {
|
||||||
|
return layersToProcess.remove(type);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,8 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.instancing;
|
package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
|
||||||
public interface Renderable {
|
public record ShaderState(Material material, VertexType vertex, StructType<?> instance) {
|
||||||
|
|
||||||
void render();
|
|
||||||
|
|
||||||
boolean shouldRemove();
|
|
||||||
|
|
||||||
Material getMaterial();
|
|
||||||
|
|
||||||
VertexType getVertexType();
|
|
||||||
}
|
}
|
|
@ -9,14 +9,11 @@ import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||||
import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
|
import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
|
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.instancing.Renderable;
|
|
||||||
import com.jozufozu.flywheel.core.Contexts;
|
import com.jozufozu.flywheel.core.Contexts;
|
||||||
import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo;
|
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
||||||
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
|
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
|
||||||
import com.jozufozu.flywheel.util.Lazy;
|
import com.jozufozu.flywheel.util.Lazy;
|
||||||
import com.jozufozu.flywheel.util.Textures;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.math.Matrix4f;
|
import com.mojang.math.Matrix4f;
|
||||||
|
|
||||||
|
@ -168,30 +165,13 @@ public class CrumblingRenderer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentLayer.setupRenderState();
|
var multimap = renderLists.get(type);
|
||||||
Textures.bindActiveTextures();
|
|
||||||
CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
|
|
||||||
|
|
||||||
for (var entry : factories.entrySet()) {
|
|
||||||
var instanceType = entry.getKey();
|
|
||||||
var factory = entry.getValue();
|
|
||||||
|
|
||||||
var toRender = factory.getRenderList(type);
|
|
||||||
|
|
||||||
if (toRender.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var renderable : toRender) {
|
|
||||||
|
|
||||||
setup(instanceType, renderable.getMaterial(), coreShaderInfo, camX, camY, camZ, viewProjection, level, renderable.getVertexType());
|
|
||||||
|
|
||||||
renderable.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (multimap.isEmpty()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentLayer.clearRenderState();
|
render(currentLayer, multimap, camX, camY, camZ, viewProjection, level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,9 @@ import net.minecraft.world.level.block.state.properties.ChestType;
|
||||||
|
|
||||||
public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends BlockEntityInstance<T> implements DynamicInstance {
|
public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends BlockEntityInstance<T> implements DynamicInstance {
|
||||||
|
|
||||||
private static final BiFunction<ChestType, Material, BasicModelSupplier> LID = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createLidModel(type, mat.sprite()), new com.jozufozu.flywheel.api.material.Material(Sheets.chestSheet(), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT)));
|
private static final com.jozufozu.flywheel.api.material.Material CHEST_MATERIAL = new com.jozufozu.flywheel.api.material.Material(Sheets.chestSheet(), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT);
|
||||||
private static final BiFunction<ChestType, Material, BasicModelSupplier> BASE = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createBaseModel(type, mat.sprite()), new com.jozufozu.flywheel.api.material.Material(Sheets.chestSheet(), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT)));
|
private static final BiFunction<ChestType, Material, BasicModelSupplier> LID = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createLidModel(type, mat.sprite()), CHEST_MATERIAL));
|
||||||
|
private static final BiFunction<ChestType, Material, BasicModelSupplier> BASE = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createBaseModel(type, mat.sprite()), CHEST_MATERIAL));
|
||||||
|
|
||||||
private final OrientedPart body;
|
private final OrientedPart body;
|
||||||
private final TransformedPart lid;
|
private final TransformedPart lid;
|
||||||
|
|
Loading…
Reference in a new issue