Added build task removal

This commit is contained in:
mcrcortex
2024-09-24 11:05:58 +10:00
parent 10391c48c7
commit 3a800ec9ca
5 changed files with 85 additions and 35 deletions

View File

@@ -1,6 +1,7 @@
package me.cortex.voxy.client.core.rendering.building; package me.cortex.voxy.client.core.rendering.building;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import me.cortex.voxy.client.core.model.IdNotYetComputedException; import me.cortex.voxy.client.core.model.IdNotYetComputedException;
import me.cortex.voxy.client.core.model.ModelBakerySubsystem; import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
@@ -16,8 +17,13 @@ import java.util.function.Supplier;
//TODO: Add a render cache //TODO: Add a render cache
public class RenderGenerationService { public class RenderGenerationService {
public interface TaskChecker {boolean check(int lvl, int x, int y, int z);} private static final class BuildTask {
private record BuildTask(long position, Supplier<WorldSection> sectionSupplier, boolean[] hasDoneModelRequest) {} final long position;
boolean hasDoneModelRequest;
private BuildTask(long position) {
this.position = position;
}
}
private final Long2ObjectLinkedOpenHashMap<BuildTask> taskQueue = new Long2ObjectLinkedOpenHashMap<>(); private final Long2ObjectLinkedOpenHashMap<BuildTask> taskQueue = new Long2ObjectLinkedOpenHashMap<>();
@@ -61,6 +67,10 @@ public class RenderGenerationService {
} }
} }
private WorldSection acquireSection(long pos) {
return this.world.acquireIfExists(pos);
}
//TODO: add a generated render data cache //TODO: add a generated render data cache
private void processJob(RenderDataFactory factory) { private void processJob(RenderDataFactory factory) {
BuildTask task; BuildTask task;
@@ -72,7 +82,7 @@ public class RenderGenerationService {
} }
} }
//long time = BuiltSection.getTime(); //long time = BuiltSection.getTime();
var section = task.sectionSupplier.get(); var section = this.acquireSection(task.position);
if (section == null) { if (section == null) {
this.resultConsumer.accept(BuiltSection.empty(task.position)); this.resultConsumer.accept(BuiltSection.empty(task.position));
return; return;
@@ -85,7 +95,7 @@ public class RenderGenerationService {
if (!this.modelBakery.factory.hasModelForBlockId(e.id)) { if (!this.modelBakery.factory.hasModelForBlockId(e.id)) {
this.modelBakery.requestBlockBake(e.id); this.modelBakery.requestBlockBake(e.id);
} }
if (task.hasDoneModelRequest[0]) { if (task.hasDoneModelRequest) {
try { try {
Thread.sleep(10); Thread.sleep(10);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
@@ -95,11 +105,18 @@ public class RenderGenerationService {
//The reason for the extra id parameter is that we explicitly add/check against the exception id due to e.g. requesting accross a chunk boarder wont be captured in the request //The reason for the extra id parameter is that we explicitly add/check against the exception id due to e.g. requesting accross a chunk boarder wont be captured in the request
this.computeAndRequestRequiredModels(section, e.id); this.computeAndRequestRequiredModels(section, e.id);
} }
//We need to reinsert the build task into the queue
//System.err.println("Render task failed to complete due to un-computed client id"); {
synchronized (this.taskQueue) { //We need to reinsert the build task into the queue
var queuedTask = this.taskQueue.computeIfAbsent(section.key, (a)->task); BuildTask queuedTask;
queuedTask.hasDoneModelRequest[0] = true;//Mark (or remark) the section as having chunks requested synchronized (this.taskQueue) {
queuedTask = this.taskQueue.putIfAbsent(section.key, task);
}
if (queuedTask == null) {
queuedTask = task;
}
queuedTask.hasDoneModelRequest = true;//Mark (or remark) the section as having chunks requested
if (queuedTask == task) {//use the == not .equal to see if we need to release a permit if (queuedTask == task) {//use the == not .equal to see if we need to release a permit
this.threads.execute();//Since we put in queue, release permit this.threads.execute();//Since we put in queue, release permit
@@ -113,37 +130,34 @@ public class RenderGenerationService {
} }
} }
public void enqueueTask(int lvl, int x, int y, int z) {
this.enqueueTask(lvl, x, y, z, (l,x1,y1,z1)->true);
}
public void enqueueTask(long position) { public void enqueueTask(long pos) {
this.enqueueTask(position, (l,x1,y1,z1)->true);
}
public void enqueueTask(int lvl, int x, int y, int z, TaskChecker checker) {
this.enqueueTask(WorldEngine.getWorldSectionId(lvl, x, y, z), checker);
}
public void enqueueTask(long ikey, TaskChecker checker) {
synchronized (this.taskQueue) { synchronized (this.taskQueue) {
this.taskQueue.computeIfAbsent(ikey, key->{ this.taskQueue.computeIfAbsent(pos, key->{
this.threads.execute(); this.threads.execute();
return new BuildTask(ikey, ()->{ return new BuildTask(key);
if (checker.check(WorldEngine.getLevel(ikey), WorldEngine.getX(ikey), WorldEngine.getY(ikey), WorldEngine.getZ(ikey))) {
return this.world.acquireIfExists(WorldEngine.getLevel(ikey), WorldEngine.getX(ikey), WorldEngine.getY(ikey), WorldEngine.getZ(ikey));
} else {
return null;
}
}, new boolean[1]);
}); });
} }
} }
public int getTaskCount() { public void removeTask(long pos) {
return this.threads.getJobCount(); BuildTask task;
synchronized (this.taskQueue) {
task = this.taskQueue.remove(pos);
}
if (task != null) {
if (!this.threads.steal()) {
throw new IllegalStateException("Failed to steal a task!!!");
}
}
} }
/*
public void enqueueTask(int lvl, int x, int y, int z) {
this.enqueueTask(WorldEngine.getWorldSectionId(lvl, x, y, z));
}
*/
public void shutdown() { public void shutdown() {
this.threads.shutdown(); this.threads.shutdown();

View File

@@ -77,7 +77,9 @@ public class ServiceSlice extends TrackedObject {
if (this.activeCount.decrementAndGet() < 0) { if (this.activeCount.decrementAndGet() < 0) {
throw new IllegalStateException("Alive count negative!"); throw new IllegalStateException("Alive count negative!");
} }
this.jobCount2.decrementAndGet(); if (this.jobCount2.decrementAndGet() < 0) {
throw new IllegalStateException("Job count negative!");
}
} }
return true; return true;
} }
@@ -133,4 +135,16 @@ public class ServiceSlice extends TrackedObject {
Thread.yield(); Thread.yield();
} }
} }
//Steal a job, if there is no job available return false
public boolean steal() {
if (!this.jobCount.tryAcquire()) {
return false;
}
if (this.jobCount2.decrementAndGet() < 0) {
throw new IllegalStateException("Job count negative!!!");
}
this.threadPool.steal(this);
return true;
}
} }

View File

@@ -102,6 +102,11 @@ public class ServiceThreadPool {
this.jobCounter.release(1); this.jobCounter.release(1);
} }
void steal(ServiceSlice service) {
this.totalJobWeight.addAndGet(-service.weightPerJob);
this.jobCounter.acquireUninterruptibly(1);
}
private void worker(int threadId) { private void worker(int threadId) {
long seed = 1234342; long seed = 1234342;
int revolvingSelector = 0; int revolvingSelector = 0;

View File

@@ -30,7 +30,10 @@ public class ActiveSectionTracker {
} }
public WorldSection acquire(int lvl, int x, int y, int z, boolean nullOnEmpty) { public WorldSection acquire(int lvl, int x, int y, int z, boolean nullOnEmpty) {
long key = WorldEngine.getWorldSectionId(lvl, x, y, z); return this.acquire(WorldEngine.getWorldSectionId(lvl, x, y, z), nullOnEmpty);
}
public WorldSection acquire(long key, boolean nullOnEmpty) {
var cache = this.loadedSectionCache[this.getCacheArrayIndex(key)]; var cache = this.loadedSectionCache[this.getCacheArrayIndex(key)];
VolatileHolder<WorldSection> holder = null; VolatileHolder<WorldSection> holder = null;
boolean isLoader = false; boolean isLoader = false;
@@ -50,7 +53,12 @@ public class ActiveSectionTracker {
//If this thread was the one to create the reference then its the thread to load the section //If this thread was the one to create the reference then its the thread to load the section
if (isLoader) { if (isLoader) {
var section = new WorldSection(lvl, x, y, z, this); var section = new WorldSection(WorldEngine.getLevel(key),
WorldEngine.getX(key),
WorldEngine.getY(key),
WorldEngine.getZ(key),
this);
int status = -1;//this.dataCache.load(section); int status = -1;//this.dataCache.load(section);
if (status == -1) {//Cache miss if (status == -1) {//Cache miss
status = this.loader.load(section); status = this.loader.load(section);
@@ -85,7 +93,7 @@ public class ActiveSectionTracker {
return section; return section;
} }
} }
return this.acquire(lvl, x, y, z, nullOnEmpty); return this.acquire(key, nullOnEmpty);
} }
} }

View File

@@ -17,6 +17,7 @@ public class WorldEngine {
public static final int UPDATE_TYPE_BLOCK_BIT = 1; public static final int UPDATE_TYPE_BLOCK_BIT = 1;
public static final int UPDATE_TYPE_CHILD_EXISTENCE_BIT = 2; public static final int UPDATE_TYPE_CHILD_EXISTENCE_BIT = 2;
public static final int UPDATE_FLAGS = UPDATE_TYPE_BLOCK_BIT | UPDATE_TYPE_CHILD_EXISTENCE_BIT; public static final int UPDATE_FLAGS = UPDATE_TYPE_BLOCK_BIT | UPDATE_TYPE_CHILD_EXISTENCE_BIT;
public interface ISectionChangeCallback {void accept(WorldSection section, int updateFlags);} public interface ISectionChangeCallback {void accept(WorldSection section, int updateFlags);}
@@ -77,6 +78,14 @@ public class WorldEngine {
return this.sectionTracker.acquire(lvl, x, y, z, false); return this.sectionTracker.acquire(lvl, x, y, z, false);
} }
public WorldSection acquire(long pos) {
return this.sectionTracker.acquire(pos, false);
}
public WorldSection acquireIfExists(long pos) {
return this.sectionTracker.acquire(pos, true);
}
//TODO: Fixme/optimize, cause as the lvl gets higher, the size of x,y,z gets smaller so i can dynamically compact the format //TODO: Fixme/optimize, cause as the lvl gets higher, the size of x,y,z gets smaller so i can dynamically compact the format
// depending on the lvl, which should optimize colisions and whatnot // depending on the lvl, which should optimize colisions and whatnot
public static long getWorldSectionId(int lvl, int x, int y, int z) { public static long getWorldSectionId(int lvl, int x, int y, int z) {