mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-06 04:16:36 +01:00
Consists of inconsistency
- Make architecture of indirect engine and draw manager match that of other engines and draw managers
This commit is contained in:
parent
d3d5797fd3
commit
79464361d2
9 changed files with 169 additions and 195 deletions
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
|
@ -80,7 +80,7 @@ public class InstancingEngine implements Engine {
|
|||
|
||||
try (var restoreState = GlStateTracker.getRestoreState()) {
|
||||
setup();
|
||||
|
||||
|
||||
render(drawSet);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in a new issue