mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-11-14 06:24:12 +01:00
Eviction notice
- For instancing, remove empty instancers and delete meshes - Specify in InstancerProvider's contract that instancers should not be kept around, but reusing them within one frame in guaranteed to be safe - Do all instancer updates in flush, and remove unnecessary checks that would be made later in the frame - Remove isEmpty from Mesh - Remove staging buffer param from indirect mesh pool
This commit is contained in:
parent
593784d614
commit
7d59fdc86c
@ -9,7 +9,11 @@ public interface InstancerProvider {
|
|||||||
/**
|
/**
|
||||||
* Get an instancer for the given instance type rendering the given model.
|
* Get an instancer for the given instance type rendering the given model.
|
||||||
*
|
*
|
||||||
* <p>Calling this method twice with the same arguments will return the same instancer.</p>
|
* <p>Calling this method twice with the same arguments in the
|
||||||
|
* same frame will return the same instancer.</p>
|
||||||
|
*
|
||||||
|
* <p>It is not safe to store instancers between frames. Each
|
||||||
|
* time you need an instancer, you should call this method.</p>
|
||||||
*
|
*
|
||||||
* @return An instancer for the given instance type rendering the given model.
|
* @return An instancer for the given instance type rendering the given model.
|
||||||
*/
|
*/
|
||||||
|
@ -13,14 +13,6 @@ public interface Mesh {
|
|||||||
*/
|
*/
|
||||||
int vertexCount();
|
int vertexCount();
|
||||||
|
|
||||||
/**
|
|
||||||
* Is there nothing to render?
|
|
||||||
* @return true if there are no vertices.
|
|
||||||
*/
|
|
||||||
default boolean isEmpty() {
|
|
||||||
return vertexCount() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write this mesh into a vertex list. Vertices with index {@literal <}0 or {@literal >=}{@link #vertexCount()} will not be
|
* Write this mesh into a vertex list. Vertices with index {@literal <}0 or {@literal >=}{@link #vertexCount()} will not be
|
||||||
* read or modified.
|
* read or modified.
|
||||||
|
@ -59,11 +59,7 @@ public abstract class InstancerStorage<N extends AbstractInstancer<?>> {
|
|||||||
var out = create(key);
|
var out = create(key);
|
||||||
|
|
||||||
// Only queue the instancer for initialization if it has anything to render.
|
// Only queue the instancer for initialization if it has anything to render.
|
||||||
if (key.model()
|
if (checkAndWarnEmptyModel(key.model())) {
|
||||||
.meshes()
|
|
||||||
.isEmpty()) {
|
|
||||||
warnEmptyModel();
|
|
||||||
} else {
|
|
||||||
// Thread safety: this method is called atomically from within computeIfAbsent,
|
// Thread safety: this method is called atomically from within computeIfAbsent,
|
||||||
// so we don't need extra synchronization to protect the queue.
|
// so we don't need extra synchronization to protect the queue.
|
||||||
initializationQueue.add(new UninitializedInstancer<>(key, out));
|
initializationQueue.add(new UninitializedInstancer<>(key, out));
|
||||||
@ -75,7 +71,11 @@ public abstract class InstancerStorage<N extends AbstractInstancer<?>> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void warnEmptyModel() {
|
private static boolean checkAndWarnEmptyModel(Model model) {
|
||||||
|
if (!model.meshes().isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.append("Creating an instancer for a model with no meshes! Stack trace:");
|
builder.append("Creating an instancer for a model with no meshes! Stack trace:");
|
||||||
|
|
||||||
@ -85,5 +85,7 @@ public abstract class InstancerStorage<N extends AbstractInstancer<?>> {
|
|||||||
.append(f.toString()));
|
.append(f.toString()));
|
||||||
|
|
||||||
Flywheel.LOGGER.warn(builder.toString());
|
Flywheel.LOGGER.warn(builder.toString());
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||||||
buffers.updateCounts(instanceCountThisFrame, indirectModels.size(), indirectDraws.size());
|
buffers.updateCounts(instanceCountThisFrame, indirectModels.size(), indirectDraws.size());
|
||||||
|
|
||||||
// Must flush the mesh pool first so everything else has the right baseVertex and baseIndex.
|
// Must flush the mesh pool first so everything else has the right baseVertex and baseIndex.
|
||||||
meshPool.flush(stagingBuffer);
|
meshPool.flush();
|
||||||
|
|
||||||
// Upload only objects that have changed.
|
// Upload only objects that have changed.
|
||||||
uploadObjects(stagingBuffer);
|
uploadObjects(stagingBuffer);
|
||||||
|
@ -62,15 +62,14 @@ public class IndirectMeshPool {
|
|||||||
return meshes.get(mesh);
|
return meshes.get(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush(StagingBuffer stagingBuffer) {
|
public void flush() {
|
||||||
if (dirty) {
|
if (dirty) {
|
||||||
// TODO: use the staging buffer and be smarter about allocation in general.
|
uploadAll();
|
||||||
uploadAll(stagingBuffer);
|
|
||||||
dirty = false;
|
dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadAll(StagingBuffer stagingBuffer) {
|
private void uploadAll() {
|
||||||
long neededSize = 0;
|
long neededSize = 0;
|
||||||
int maxQuadIndexCount = 0;
|
int maxQuadIndexCount = 0;
|
||||||
int nonQuadIndexCount = 0;
|
int nonQuadIndexCount = 0;
|
||||||
|
@ -11,32 +11,27 @@ public class DrawCall {
|
|||||||
private final InstancedInstancer<?> instancer;
|
private final InstancedInstancer<?> instancer;
|
||||||
private final InstancedMeshPool.BufferedMesh mesh;
|
private final InstancedMeshPool.BufferedMesh mesh;
|
||||||
|
|
||||||
@Nullable
|
private final GlVertexArray vao;
|
||||||
private GlVertexArray vao;
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private GlVertexArray vaoScratch;
|
private GlVertexArray vaoScratch;
|
||||||
|
private boolean deleted;
|
||||||
|
|
||||||
public DrawCall(InstancedInstancer<?> instancer, InstancedMeshPool.BufferedMesh mesh, ShaderState shaderState) {
|
public DrawCall(InstancedInstancer<?> instancer, InstancedMeshPool.BufferedMesh mesh, ShaderState shaderState) {
|
||||||
this.instancer = instancer;
|
this.instancer = instancer;
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
this.shaderState = shaderState;
|
this.shaderState = shaderState;
|
||||||
|
|
||||||
|
mesh.acquire();
|
||||||
|
|
||||||
vao = GlVertexArray.create();
|
vao = GlVertexArray.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInvalid() {
|
public boolean deleted() {
|
||||||
return instancer.isInvalid() || vao == null;
|
return deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render() {
|
public void render() {
|
||||||
if (isInvalid() || mesh.isEmpty()) {
|
if (mesh.invalid()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
instancer.update();
|
|
||||||
|
|
||||||
int instanceCount = instancer.getInstanceCount();
|
|
||||||
if (instanceCount <= 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,16 +40,14 @@ public class DrawCall {
|
|||||||
|
|
||||||
vao.bindForDraw();
|
vao.bindForDraw();
|
||||||
|
|
||||||
mesh.draw(instanceCount);
|
mesh.draw(instancer.getInstanceCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderOne(InstanceHandleImpl impl) {
|
public void renderOne(InstanceHandleImpl impl) {
|
||||||
if (isInvalid() || mesh.isEmpty()) {
|
if (mesh.invalid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
instancer.update();
|
|
||||||
|
|
||||||
int instanceCount = instancer.getInstanceCount();
|
int instanceCount = instancer.getInstanceCount();
|
||||||
if (instanceCount <= 0 || impl.index >= instanceCount) {
|
if (instanceCount <= 0 || impl.index >= instanceCount) {
|
||||||
return;
|
return;
|
||||||
@ -78,14 +71,19 @@ public class DrawCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
if (vao != null) {
|
if (deleted) {
|
||||||
vao.delete();
|
return;
|
||||||
vao = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vao.delete();
|
||||||
|
|
||||||
if (vaoScratch != null) {
|
if (vaoScratch != null) {
|
||||||
vaoScratch.delete();
|
vaoScratch.delete();
|
||||||
vaoScratch = null;
|
vaoScratch = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mesh.drop();
|
||||||
|
|
||||||
|
deleted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,11 +98,7 @@ public class InstancedCrumbling {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DrawCall> draws = instancer.drawCalls();
|
for (DrawCall draw : instancer.drawCalls()) {
|
||||||
|
|
||||||
draws.removeIf(DrawCall::isInvalid);
|
|
||||||
|
|
||||||
for (DrawCall draw : draws) {
|
|
||||||
out.computeIfAbsent(draw.shaderState, $ -> new Int2ObjectArrayMap<>())
|
out.computeIfAbsent(draw.shaderState, $ -> new Int2ObjectArrayMap<>())
|
||||||
.computeIfAbsent(progress, $ -> new ArrayList<>())
|
.computeIfAbsent(progress, $ -> new ArrayList<>())
|
||||||
.add(() -> draw.renderOne(impl));
|
.add(() -> draw.renderOne(impl));
|
||||||
|
@ -10,7 +10,6 @@ import com.google.common.collect.ImmutableListMultimap;
|
|||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.model.Mesh;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
||||||
|
|
||||||
@ -19,11 +18,11 @@ public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>
|
|||||||
* The set of draw calls to make in each {@link RenderStage}.
|
* The set of draw calls to make in each {@link RenderStage}.
|
||||||
*/
|
*/
|
||||||
private final Map<RenderStage, DrawSet> drawSets = new EnumMap<>(RenderStage.class);
|
private final Map<RenderStage, DrawSet> drawSets = new EnumMap<>(RenderStage.class);
|
||||||
|
private final EboCache eboCache = new EboCache();
|
||||||
/**
|
/**
|
||||||
* A map of vertex types to their mesh pools.
|
* A map of vertex types to their mesh pools.
|
||||||
*/
|
*/
|
||||||
private final InstancedMeshPool meshPool = new InstancedMeshPool();
|
private final InstancedMeshPool meshPool = new InstancedMeshPool(eboCache);
|
||||||
private final EboCache eboCache = new EboCache();
|
|
||||||
|
|
||||||
public DrawSet get(RenderStage stage) {
|
public DrawSet get(RenderStage stage) {
|
||||||
return drawSets.getOrDefault(stage, DrawSet.EMPTY);
|
return drawSets.getOrDefault(stage, DrawSet.EMPTY);
|
||||||
@ -32,6 +31,24 @@ public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>
|
|||||||
public void flush() {
|
public void flush() {
|
||||||
super.flush();
|
super.flush();
|
||||||
|
|
||||||
|
var instancers = this.instancers.values();
|
||||||
|
instancers.removeIf(instancer -> {
|
||||||
|
// Update the instancers and remove any that are empty.
|
||||||
|
instancer.update();
|
||||||
|
|
||||||
|
if (instancer.getInstanceCount() == 0) {
|
||||||
|
instancer.delete();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (DrawSet drawSet : drawSets.values()) {
|
||||||
|
// Remove the draw calls for any instancers we deleted.
|
||||||
|
drawSet.prune();
|
||||||
|
}
|
||||||
|
|
||||||
meshPool.flush();
|
meshPool.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,10 +67,6 @@ public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>
|
|||||||
eboCache.invalidate();
|
eboCache.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private InstancedMeshPool.BufferedMesh alloc(Mesh mesh) {
|
|
||||||
return meshPool.alloc(mesh, eboCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <I extends Instance> InstancedInstancer<I> create(InstancerKey<I> key) {
|
protected <I extends Instance> InstancedInstancer<I> create(InstancerKey<I> key) {
|
||||||
return new InstancedInstancer<>(key.type(), key.context());
|
return new InstancedInstancer<>(key.type(), key.context());
|
||||||
@ -68,7 +81,7 @@ public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>
|
|||||||
var meshes = key.model()
|
var meshes = key.model()
|
||||||
.meshes();
|
.meshes();
|
||||||
for (var entry : meshes.entrySet()) {
|
for (var entry : meshes.entrySet()) {
|
||||||
var mesh = alloc(entry.getValue());
|
var mesh = meshPool.alloc(entry.getValue());
|
||||||
|
|
||||||
ShaderState shaderState = new ShaderState(entry.getKey(), key.type(), key.context());
|
ShaderState shaderState = new ShaderState(entry.getKey(), key.type(), key.context());
|
||||||
DrawCall drawCall = new DrawCall(instancer, mesh, shaderState);
|
DrawCall drawCall = new DrawCall(instancer, mesh, shaderState);
|
||||||
@ -111,5 +124,10 @@ public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>
|
|||||||
.entrySet()
|
.entrySet()
|
||||||
.iterator();
|
.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void prune() {
|
||||||
|
drawCalls.values()
|
||||||
|
.removeIf(DrawCall::deleted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,14 +39,6 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||||||
writer = type.writer();
|
writer = type.writer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getAttributeCount() {
|
|
||||||
return instanceAttributes.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isInvalid() {
|
|
||||||
return vbo == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
if (vbo != null) {
|
if (vbo != null) {
|
||||||
return;
|
return;
|
||||||
@ -58,28 +50,22 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
removeDeletedInstances();
|
removeDeletedInstances();
|
||||||
ensureBufferCapacity();
|
|
||||||
updateBuffer();
|
updateBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureBufferCapacity() {
|
|
||||||
int count = instances.size();
|
|
||||||
int byteSize = instanceStride * count;
|
|
||||||
if (vbo.ensureCapacity(byteSize)) {
|
|
||||||
// The vbo has moved, so we need to re-bind attributes
|
|
||||||
boundTo.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateBuffer() {
|
private void updateBuffer() {
|
||||||
if (changed.isEmpty() || vbo == null) {
|
if (changed.isEmpty() || vbo == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (MappedBuffer buf = vbo.map()) {
|
int byteSize = instanceStride * instances.size();
|
||||||
long ptr = buf.ptr();
|
if (vbo.ensureCapacity(byteSize)) {
|
||||||
|
// The vbo has moved, so we need to re-bind attributes
|
||||||
|
boundTo.clear();
|
||||||
|
}
|
||||||
|
|
||||||
writeChanged(ptr);
|
try (MappedBuffer buf = vbo.map()) {
|
||||||
|
writeChanged(buf.ptr());
|
||||||
|
|
||||||
changed.clear();
|
changed.clear();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -129,6 +115,10 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||||||
}
|
}
|
||||||
vbo.delete();
|
vbo.delete();
|
||||||
vbo = null;
|
vbo = null;
|
||||||
|
|
||||||
|
for (DrawCall drawCall : drawCalls) {
|
||||||
|
drawCall.delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDrawCall(DrawCall drawCall) {
|
public void addDrawCall(DrawCall drawCall) {
|
||||||
|
@ -21,11 +21,11 @@ import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
|||||||
|
|
||||||
public class InstancedMeshPool {
|
public class InstancedMeshPool {
|
||||||
private final VertexView vertexView;
|
private final VertexView vertexView;
|
||||||
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
|
private final Map<Mesh, BufferedMesh> byMesh = new HashMap<>();
|
||||||
private final List<BufferedMesh> allBuffered = new ArrayList<>();
|
private final List<BufferedMesh> ordered = new ArrayList<>();
|
||||||
private final List<BufferedMesh> pendingUpload = new ArrayList<>();
|
|
||||||
|
|
||||||
private final GlBuffer vbo;
|
private final GlBuffer vbo;
|
||||||
|
private final EboCache eboCache;
|
||||||
private long byteSize;
|
private long byteSize;
|
||||||
|
|
||||||
private boolean dirty;
|
private boolean dirty;
|
||||||
@ -34,35 +34,34 @@ public class InstancedMeshPool {
|
|||||||
/**
|
/**
|
||||||
* Create a new mesh pool.
|
* Create a new mesh pool.
|
||||||
*/
|
*/
|
||||||
public InstancedMeshPool() {
|
public InstancedMeshPool(EboCache eboCache) {
|
||||||
|
this.eboCache = eboCache;
|
||||||
vertexView = InternalVertex.createVertexView();
|
vertexView = InternalVertex.createVertexView();
|
||||||
int stride = InternalVertex.STRIDE;
|
|
||||||
vbo = new GlBuffer();
|
vbo = new GlBuffer();
|
||||||
vbo.growthFunction(l -> Math.max(l + stride * 128L, (long) (l * 1.6)));
|
vbo.growthFunction(l -> Math.max(l + InternalVertex.STRIDE * 128L, (long) (l * 1.6)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate a mesh in the arena.
|
* Allocate a mesh in the arena.
|
||||||
*
|
*
|
||||||
* @param mesh The mesh to allocate.
|
* @param mesh The mesh to allocate.
|
||||||
* @param eboCache The EBO cache to use.
|
|
||||||
* @return A handle to the allocated mesh.
|
* @return A handle to the allocated mesh.
|
||||||
*/
|
*/
|
||||||
public BufferedMesh alloc(Mesh mesh, EboCache eboCache) {
|
public BufferedMesh alloc(Mesh mesh) {
|
||||||
return meshes.computeIfAbsent(mesh, m -> {
|
return byMesh.computeIfAbsent(mesh, this::_alloc);
|
||||||
BufferedMesh bufferedMesh = new BufferedMesh(m, byteSize, eboCache);
|
}
|
||||||
byteSize += bufferedMesh.size();
|
|
||||||
allBuffered.add(bufferedMesh);
|
private BufferedMesh _alloc(Mesh m) {
|
||||||
pendingUpload.add(bufferedMesh);
|
BufferedMesh bufferedMesh = new BufferedMesh(m, this.eboCache);
|
||||||
|
ordered.add(bufferedMesh);
|
||||||
|
|
||||||
dirty = true;
|
dirty = true;
|
||||||
return bufferedMesh;
|
return bufferedMesh;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public BufferedMesh get(Mesh mesh) {
|
public BufferedMesh get(Mesh mesh) {
|
||||||
return meshes.get(mesh);
|
return byMesh.get(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush() {
|
public void flush() {
|
||||||
@ -71,53 +70,59 @@ public class InstancedMeshPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (anyToRemove) {
|
if (anyToRemove) {
|
||||||
|
anyToRemove = false;
|
||||||
processDeletions();
|
processDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var forUpload = calculateByteSizeAndGetMeshesForUpload();
|
||||||
|
|
||||||
|
if (!forUpload.isEmpty()) {
|
||||||
vbo.ensureCapacity(byteSize);
|
vbo.ensureCapacity(byteSize);
|
||||||
|
|
||||||
uploadPending();
|
upload(forUpload);
|
||||||
|
}
|
||||||
|
|
||||||
dirty = false;
|
dirty = false;
|
||||||
pendingUpload.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processDeletions() {
|
private void processDeletions() {
|
||||||
// remove deleted meshes
|
// remove deleted meshes
|
||||||
allBuffered.removeIf(bufferedMesh -> {
|
ordered.removeIf(bufferedMesh -> {
|
||||||
boolean deleted = bufferedMesh.isDeleted();
|
boolean deleted = bufferedMesh.deleted();
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
meshes.remove(bufferedMesh.mesh);
|
byMesh.remove(bufferedMesh.mesh);
|
||||||
}
|
}
|
||||||
return deleted;
|
return deleted;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// re-evaluate first vertex for each mesh
|
private List<BufferedMesh> calculateByteSizeAndGetMeshesForUpload() {
|
||||||
int byteIndex = 0;
|
List<BufferedMesh> out = new ArrayList<>();
|
||||||
for (BufferedMesh mesh : allBuffered) {
|
|
||||||
|
long byteIndex = 0;
|
||||||
|
for (BufferedMesh mesh : ordered) {
|
||||||
if (mesh.byteIndex != byteIndex) {
|
if (mesh.byteIndex != byteIndex) {
|
||||||
pendingUpload.add(mesh);
|
out.add(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh.byteIndex = byteIndex;
|
mesh.byteIndex = byteIndex;
|
||||||
|
|
||||||
byteIndex += mesh.size();
|
byteIndex += mesh.byteSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.byteSize = byteIndex;
|
this.byteSize = byteIndex;
|
||||||
this.anyToRemove = false;
|
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadPending() {
|
private void upload(List<BufferedMesh> meshes) {
|
||||||
try (MappedBuffer mapped = vbo.map()) {
|
try (MappedBuffer mapped = vbo.map()) {
|
||||||
long ptr = mapped.ptr();
|
long ptr = mapped.ptr();
|
||||||
|
|
||||||
for (BufferedMesh mesh : pendingUpload) {
|
for (BufferedMesh mesh : meshes) {
|
||||||
mesh.write(ptr, vertexView);
|
mesh.write(ptr, vertexView);
|
||||||
mesh.boundTo.clear();
|
mesh.boundTo.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingUpload.clear();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
|
Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
|
||||||
}
|
}
|
||||||
@ -125,14 +130,13 @@ public class InstancedMeshPool {
|
|||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
vbo.delete();
|
vbo.delete();
|
||||||
meshes.clear();
|
byMesh.clear();
|
||||||
allBuffered.clear();
|
ordered.clear();
|
||||||
pendingUpload.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "InstancedMeshPool{" + "byteSize=" + byteSize + ", meshCount=" + meshes.size() + '}';
|
return "InstancedMeshPool{" + "byteSize=" + byteSize + ", meshCount=" + byMesh.size() + '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BufferedMesh {
|
public class BufferedMesh {
|
||||||
@ -141,37 +145,28 @@ public class InstancedMeshPool {
|
|||||||
private final int byteSize;
|
private final int byteSize;
|
||||||
private final int ebo;
|
private final int ebo;
|
||||||
|
|
||||||
private long byteIndex;
|
private long byteIndex = -1;
|
||||||
private boolean deleted;
|
private int referenceCount = 0;
|
||||||
|
|
||||||
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
||||||
|
|
||||||
private BufferedMesh(Mesh mesh, long byteIndex, EboCache eboCache) {
|
private BufferedMesh(Mesh mesh, EboCache eboCache) {
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
vertexCount = mesh.vertexCount();
|
vertexCount = mesh.vertexCount();
|
||||||
byteSize = vertexCount * InternalVertex.STRIDE;
|
byteSize = vertexCount * InternalVertex.STRIDE;
|
||||||
this.byteIndex = byteIndex;
|
|
||||||
this.ebo = eboCache.get(mesh.indexSequence(), mesh.indexCount());
|
this.ebo = eboCache.get(mesh.indexSequence(), mesh.indexCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int vertexCount() {
|
public boolean deleted() {
|
||||||
return vertexCount;
|
return referenceCount <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public boolean invalid() {
|
||||||
return byteSize;
|
return mesh.vertexCount() == 0 || deleted() || byteIndex == -1;
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDeleted() {
|
|
||||||
return deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return mesh.isEmpty() || isDeleted();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void write(long ptr, VertexView vertexView) {
|
private void write(long ptr, VertexView vertexView) {
|
||||||
if (isEmpty()) {
|
if (invalid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,10 +191,15 @@ public class InstancedMeshPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void acquire() {
|
||||||
deleted = true;
|
referenceCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drop() {
|
||||||
|
if (--referenceCount == 0) {
|
||||||
InstancedMeshPool.this.dirty = true;
|
InstancedMeshPool.this.dirty = true;
|
||||||
InstancedMeshPool.this.anyToRemove = true;
|
InstancedMeshPool.this.anyToRemove = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,8 +94,6 @@ public class InstancingEngine extends AbstractEngine {
|
|||||||
var shader = entry.getKey();
|
var shader = entry.getKey();
|
||||||
var drawCalls = entry.getValue();
|
var drawCalls = entry.getValue();
|
||||||
|
|
||||||
drawCalls.removeIf(DrawCall::isInvalid);
|
|
||||||
|
|
||||||
if (drawCalls.isEmpty()) {
|
if (drawCalls.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user