Consists of inconsistency

- Make architecture of indirect engine and draw manager match that of
other engines and draw managers
This commit is contained in:
PepperCode1 2022-09-20 22:03:20 -07:00
parent d3d5797fd3
commit 79464361d2
9 changed files with 169 additions and 195 deletions

View file

@ -23,28 +23,24 @@ public class GlStateTracker {
return program;
}
public static void _setBuffer(GlBufferType type, int buffer) {
BUFFERS[type.ordinal()] = buffer;
}
public static void _setProgram(int id) {
program = id;
public static void _setBuffer(GlBufferType type, int id) {
BUFFERS[type.ordinal()] = id;
}
public static void _setVertexArray(int id) {
vao = id;
}
public static void _setProgram(int id) {
program = id;
}
public static State getRestoreState() {
return new State(BUFFERS.clone(), vao, program);
}
public record State(int[] buffers, int vao, int program) implements AutoCloseable {
public void restore() {
if (program != GlStateTracker.program) {
GlStateManager._glUseProgram(program);
}
if (vao != GlStateTracker.vao) {
GlStateManager._glBindVertexArray(vao);
}
@ -56,6 +52,10 @@ public class GlStateTracker {
GlStateManager._glBindBuffer(values[i].glEnum, buffers[i]);
}
}
if (program != GlStateTracker.program) {
GlStateManager._glUseProgram(program);
}
}
@Override

View file

@ -1,23 +1,66 @@
package com.jozufozu.flywheel.backend.instancing.indirect;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
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.core.model.Mesh;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.util.Pair;
public class IndirectDrawManager {
public final Map<Pair<StructType<?>, VertexType>, IndirectCullingGroup<?>> lists = new HashMap<>();
private final List<UninitializedModel> uninitializedModels = new ArrayList<>();
private final List<IndirectInstancer<?>> allInstancers = new ArrayList<>();
public final Map<Pair<StructType<?>, VertexType>, IndirectCullingGroup<?>> renderLists = new HashMap<>();
public void create(IndirectInstancer<?> instancer, Model model) {
uninitializedModels.add(new UninitializedModel(instancer, model));
}
public void flush() {
for (var model : uninitializedModels) {
add(model.instancer(), model.model());
}
uninitializedModels.clear();
for (IndirectCullingGroup<?> value : renderLists.values()) {
value.beginFrame();
}
}
public void delete() {
renderLists.values()
.forEach(IndirectCullingGroup::delete);
renderLists.clear();
allInstancers.forEach(IndirectInstancer::delete);
allInstancers.clear();
}
public void clearInstancers() {
allInstancers.forEach(IndirectInstancer::clear);
}
@SuppressWarnings("unchecked")
public <D extends InstancedPart> void add(IndirectInstancer<D> instancer, Material material, Mesh mesh) {
var indirectList = (IndirectCullingGroup<D>) lists.computeIfAbsent(Pair.of(instancer.type, mesh.getVertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second()));
private <D extends InstancedPart> void add(IndirectInstancer<D> instancer, Model model) {
var meshes = model.getMeshes();
for (var entry : meshes.entrySet()) {
var material = entry.getKey();
var mesh = entry.getValue();
indirectList.drawSet.add(instancer, material, indirectList.meshPool.alloc(mesh));
var indirectList = (IndirectCullingGroup<D>) renderLists.computeIfAbsent(Pair.of(instancer.type, mesh.getVertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second()));
indirectList.drawSet.add(instancer, material, indirectList.meshPool.alloc(mesh));
break; // TODO: support multiple meshes per model
}
allInstancers.add(instancer);
}
private record UninitializedModel(IndirectInstancer<?> instancer, Model model) {
}
}

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.instancing.indirect;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -30,52 +30,55 @@ import net.minecraft.world.phys.Vec3;
public class IndirectEngine implements Engine {
public static int MAX_ORIGIN_DISTANCE = 100;
protected BlockPos originCoordinate = BlockPos.ZERO;
protected final ContextShader context;
protected final Map<StructType<?>, IndirectFactory<?>> factories = new HashMap<>();
protected final List<IndirectModel<?>> uninitializedModels = new ArrayList<>();
protected final IndirectDrawManager indirectDrawManager = new IndirectDrawManager();
protected final IndirectDrawManager drawManager = new IndirectDrawManager();
protected final Map<StructType<?>, IndirectInstancerFactory<?>> factories = new HashMap<>();
/**
* The set of instance managers that are attached to this engine.
*/
private final WeakHashSet<InstanceManager<?>> instanceManagers;
private final WeakHashSet<InstanceManager<?>> instanceManagers = new WeakHashSet<>();
public IndirectEngine(ContextShader context) {
protected final ContextShader context;
protected final int sqrMaxOriginDistance;
protected BlockPos originCoordinate = BlockPos.ZERO;
public IndirectEngine(ContextShader context, int sqrMaxOriginDistance) {
this.context = context;
this.instanceManagers = new WeakHashSet<>();
this.sqrMaxOriginDistance = sqrMaxOriginDistance;
}
@SuppressWarnings("unchecked")
@NotNull
@Override
public <D extends InstancedPart> IndirectFactory<D> factory(StructType<D> type) {
return (IndirectFactory<D>) factories.computeIfAbsent(type, this::createFactory);
public <D extends InstancedPart> IndirectInstancerFactory<D> factory(StructType<D> type) {
return (IndirectInstancerFactory<D>) factories.computeIfAbsent(type, this::createFactory);
}
@NotNull
private <D extends InstancedPart> IndirectFactory<D> createFactory(StructType<D> type) {
return new IndirectFactory<>(type, uninitializedModels::add);
private <D extends InstancedPart> IndirectInstancerFactory<D> createFactory(StructType<D> type) {
return new IndirectInstancerFactory<>(type, drawManager::create);
}
@Override
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
try (var restoreState = GlStateTracker.getRestoreState()) {
drawManager.flush();
}
}
@Override
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
try (var restoreState = GlStateTracker.getRestoreState()) {
setup();
for (var list : indirectDrawManager.lists.values()) {
for (var list : drawManager.renderLists.values()) {
list.submit(stage);
}
}
}
private void setup() {
protected void setup() {
GlTextureUnit.T2.makeActive();
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
@ -87,14 +90,31 @@ public class IndirectEngine implements Engine {
}
@Override
public void delete() {
factories.values()
.forEach(IndirectFactory::delete);
public boolean maintainOriginCoordinate(Camera camera) {
Vec3 cameraPos = camera.getPosition();
indirectDrawManager.lists.values()
.forEach(IndirectCullingGroup::delete);
double distanceSqr = Vec3.atLowerCornerOf(originCoordinate)
.subtract(cameraPos)
.lengthSqr();
factories.clear();
if (distanceSqr > sqrMaxOriginDistance) {
shiftListeners(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y), Mth.floor(cameraPos.z));
return true;
}
return false;
}
private void shiftListeners(int cX, int cY, int cZ) {
originCoordinate = new BlockPos(cX, cY, cZ);
drawManager.clearInstancers();
instanceManagers.forEach(InstanceManager::onOriginShift);
}
@Override
public void attachManagers(InstanceManager<?>... listener) {
Collections.addAll(instanceManagers, listener);
}
@Override
@ -103,45 +123,9 @@ public class IndirectEngine implements Engine {
}
@Override
public void attachManagers(InstanceManager<?>... listener) {
instanceManagers.addAll(List.of(listener));
}
@Override
public boolean maintainOriginCoordinate(Camera camera) {
Vec3 cameraPos = camera.getPosition();
double distanceSqr = Vec3.atLowerCornerOf(originCoordinate)
.subtract(cameraPos)
.lengthSqr();
if (distanceSqr > MAX_ORIGIN_DISTANCE * MAX_ORIGIN_DISTANCE) {
shiftListeners(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y), Mth.floor(cameraPos.z));
return true;
}
return false;
}
@Override
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
try (var restoreState = GlStateTracker.getRestoreState()) {
for (var model : uninitializedModels) {
model.init(indirectDrawManager);
}
uninitializedModels.clear();
for (IndirectCullingGroup<?> value : indirectDrawManager.lists.values()) {
value.beginFrame();
}
}
}
private void shiftListeners(int cX, int cY, int cZ) {
originCoordinate = new BlockPos(cX, cY, cZ);
factories.values().forEach(IndirectFactory::clear);
instanceManagers.forEach(InstanceManager::onOriginShift);
public void delete() {
factories.clear();
drawManager.delete();
}
@Override

View file

@ -1,50 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.indirect;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.instancer.Instancer;
import com.jozufozu.flywheel.api.instancer.InstancerFactory;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.core.model.Model;
public class IndirectFactory<D extends InstancedPart> implements InstancerFactory<D> {
protected final Map<Model, IndirectModel<D>> models = new HashMap<>();
protected final StructType<D> type;
private final Consumer<IndirectModel<D>> creationListener;
public IndirectFactory(StructType<D> type, Consumer<IndirectModel<D>> creationListener) {
this.type = type;
this.creationListener = creationListener;
}
@Override
public Instancer<D> model(Model modelKey) {
return models.computeIfAbsent(modelKey, this::createInstancer).getInstancer();
}
public void delete() {
models.values().forEach(IndirectModel::delete);
models.clear();
}
/**
* Clear all instance data without freeing resources.
*/
public void clear() {
models.values()
.stream()
.map(IndirectModel::getInstancer)
.forEach(AbstractInstancer::clear);
}
private IndirectModel<D> createInstancer(Model model) {
var instancer = new IndirectModel<>(type, model);
this.creationListener.accept(instancer);
return instancer;
}
}

View file

@ -0,0 +1,34 @@
package com.jozufozu.flywheel.backend.instancing.indirect;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.instancer.Instancer;
import com.jozufozu.flywheel.api.instancer.InstancerFactory;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.model.Model;
public class IndirectInstancerFactory<D extends InstancedPart> implements InstancerFactory<D> {
protected final StructType<D> type;
private final BiConsumer<IndirectInstancer<?>, Model> creationListener;
protected final Map<Model, IndirectInstancer<D>> models = new HashMap<>();
public IndirectInstancerFactory(StructType<D> type, BiConsumer<IndirectInstancer<?>, Model> creationListener) {
this.type = type;
this.creationListener = creationListener;
}
@Override
public Instancer<D> model(Model modelKey) {
return models.computeIfAbsent(modelKey, this::createInstancer);
}
private IndirectInstancer<D> createInstancer(Model model) {
var instancer = new IndirectInstancer<>(type);
creationListener.accept(instancer, model);
return instancer;
}
}

View file

@ -1,28 +1,28 @@
package com.jozufozu.flywheel.backend.instancing.indirect;
import static org.lwjgl.opengl.GL46.*;
import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import static org.lwjgl.opengl.GL44.GL_DYNAMIC_STORAGE_BIT;
import static org.lwjgl.opengl.GL45.glCreateBuffers;
import static org.lwjgl.opengl.GL45.glNamedBufferStorage;
import static org.lwjgl.opengl.GL45.nglNamedBufferSubData;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer;
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.core.model.Mesh;
public class IndirectMeshPool {
private final VertexType vertexType;
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
private final List<BufferedMesh> meshList = new ArrayList<>();
final VertexType vertexType;
final int vbo;
private final MemoryBlock clientStorage;
@ -76,7 +76,7 @@ public class IndirectMeshPool {
model.buffer(ptr);
byteIndex += model.getByteSize();
byteIndex += model.size();
baseVertex += model.mesh.getVertexCount();
}
@ -90,38 +90,44 @@ public class IndirectMeshPool {
meshList.clear();
}
public class BufferedMesh {
public VertexType getVertexType() {
return vertexType;
}
public class BufferedMesh {
public final Mesh mesh;
private final int vertexCount;
private long byteIndex;
private int baseVertex;
public BufferedMesh(Mesh mesh) {
private BufferedMesh(Mesh mesh) {
this.mesh = mesh;
vertexCount = mesh.getVertexCount();
}
private void buffer(long ptr) {
this.mesh.write(ptr + byteIndex);
mesh.write(ptr + byteIndex);
}
public int getByteSize() {
return IndirectMeshPool.this.vertexType.getLayout().getStride() * this.vertexCount;
public Mesh getMesh() {
return mesh;
}
public int size() {
return mesh.size();
}
public int getBaseVertex() {
return baseVertex;
}
public int getVertexCount() {
return this.vertexCount;
public int getIndexCount() {
return vertexCount * 6 / 4;
}
public int getIndexCount() {
return this.vertexCount * 6 / 4;
public VertexType getVertexType() {
return vertexType;
}
}
}

View file

@ -1,43 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.indirect;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.model.Model;
public class IndirectModel<D extends InstancedPart> {
private final Model model;
private final IndirectInstancer<D> instancer;
public IndirectModel(StructType<D> type, Model model) {
this.model = model;
this.instancer = new IndirectInstancer<>(type);
}
public void init(IndirectDrawManager indirectDrawManager) {
var materialMeshMap = this.model.getMeshes();
for (var entry : materialMeshMap.entrySet()) {
var material = entry.getKey();
var mesh = entry.getValue();
indirectDrawManager.add(instancer, material, mesh);
return; // TODO: support multiple meshes per model
}
}
public IndirectInstancer<D> getInstancer() {
return instancer;
}
public Model getModel() {
return model;
}
public int getVertexCount() {
return model.getVertexCount() * instancer.instanceCount;
}
public void delete() {
}
}

View file

@ -80,7 +80,7 @@ public class InstancingEngine implements Engine {
try (var restoreState = GlStateTracker.getRestoreState()) {
setup();
render(drawSet);
}
}

View file

@ -64,7 +64,7 @@ public class BackendTypes {
.properName("GL46 Compute Culling")
.shortName("indirect")
.engineMessage(new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN))
.engineSupplier(() -> new IndirectEngine(Components.WORLD))
.engineSupplier(() -> new IndirectEngine(Components.WORLD, 100 * 100))
.fallback(() -> BackendTypes.INSTANCING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
.supportsIndirect())