locks and replacing O(N) size queries with counters
This commit is contained in:
@@ -15,6 +15,7 @@ import org.lwjgl.opengl.GL11;
|
|||||||
import java.lang.invoke.VarHandle;
|
import java.lang.invoke.VarHandle;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.ARBFramebufferObject.GL_COLOR_ATTACHMENT0;
|
import static org.lwjgl.opengl.ARBFramebufferObject.GL_COLOR_ATTACHMENT0;
|
||||||
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
|
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
|
||||||
@@ -28,6 +29,7 @@ public class ModelBakerySubsystem {
|
|||||||
|
|
||||||
private final ModelStore storage = new ModelStore();
|
private final ModelStore storage = new ModelStore();
|
||||||
public final ModelFactory factory;
|
public final ModelFactory factory;
|
||||||
|
private final AtomicInteger blockIdCount = new AtomicInteger();
|
||||||
private final ConcurrentLinkedDeque<Integer> blockIdQueue = new ConcurrentLinkedDeque<>();//TODO: replace with custom DS
|
private final ConcurrentLinkedDeque<Integer> blockIdQueue = new ConcurrentLinkedDeque<>();//TODO: replace with custom DS
|
||||||
private final ConcurrentLinkedDeque<Mapper.BiomeEntry> biomeQueue = new ConcurrentLinkedDeque<>();
|
private final ConcurrentLinkedDeque<Mapper.BiomeEntry> biomeQueue = new ConcurrentLinkedDeque<>();
|
||||||
|
|
||||||
@@ -68,19 +70,22 @@ public class ModelBakerySubsystem {
|
|||||||
//TimingStatistics.modelProcess.start();
|
//TimingStatistics.modelProcess.start();
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
VarHandle.fullFence();
|
VarHandle.fullFence();
|
||||||
{
|
if (this.blockIdCount.get() != 0) {
|
||||||
long budget = Math.min(totalBudget-150_000, totalBudget-(this.factory.resultJobs.size()*10_000L))-150_000;
|
long budget = Math.min(totalBudget-150_000, totalBudget-(this.factory.resultJobs.size()*10_000L))-150_000;
|
||||||
|
|
||||||
//Always do 1 iteration minimum
|
//Always do 1 iteration minimum
|
||||||
Integer i = this.blockIdQueue.poll();
|
Integer i = this.blockIdQueue.poll();
|
||||||
|
int j = 0;
|
||||||
if (i != null) {
|
if (i != null) {
|
||||||
do {
|
do {
|
||||||
this.factory.addEntry(i);
|
this.factory.addEntry(i);
|
||||||
|
j++;
|
||||||
if (budget<(System.nanoTime() - start)+1000)
|
if (budget<(System.nanoTime() - start)+1000)
|
||||||
break;
|
break;
|
||||||
i = this.blockIdQueue.poll();
|
i = this.blockIdQueue.poll();
|
||||||
} while (i != null);
|
} while (i != null);
|
||||||
}
|
}
|
||||||
|
this.blockIdCount.addAndGet(-j);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.factory.tick();
|
this.factory.tick();
|
||||||
@@ -103,11 +108,12 @@ public class ModelBakerySubsystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addBiome(Mapper.BiomeEntry biomeEntry) {
|
public void addBiome(Mapper.BiomeEntry biomeEntry) {
|
||||||
|
this.blockIdCount.incrementAndGet();
|
||||||
this.biomeQueue.add(biomeEntry);
|
this.biomeQueue.add(biomeEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDebugData(List<String> debug) {
|
public void addDebugData(List<String> debug) {
|
||||||
debug.add(String.format("MQ/IF/MC: %04d, %03d, %04d", this.blockIdQueue.size(), this.factory.getInflightCount(), this.factory.getBakedCount()));//Model bake queue/in flight/model baked count
|
debug.add(String.format("MQ/IF/MC: %04d, %03d, %04d", this.blockIdCount.get(), this.factory.getInflightCount(), this.factory.getBakedCount()));//Model bake queue/in flight/model baked count
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModelStore getStore() {
|
public ModelStore getStore() {
|
||||||
@@ -115,10 +121,10 @@ public class ModelBakerySubsystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean areQueuesEmpty() {
|
public boolean areQueuesEmpty() {
|
||||||
return this.blockIdQueue.isEmpty() && this.factory.getInflightCount() == 0 && this.biomeQueue.isEmpty();
|
return this.blockIdCount.get()==0 && this.factory.getInflightCount() == 0 && this.biomeQueue.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getProcessingCount() {
|
public int getProcessingCount() {
|
||||||
return this.blockIdQueue.size() + this.factory.getInflightCount();
|
return this.blockIdCount.get() + this.factory.getInflightCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.util.List;
|
|||||||
import java.util.PriorityQueue;
|
import java.util.PriorityQueue;
|
||||||
import java.util.concurrent.PriorityBlockingQueue;
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.StampedLock;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@@ -50,7 +51,9 @@ public class RenderGenerationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final AtomicInteger taskQueueCount = new AtomicInteger();
|
||||||
private final PriorityBlockingQueue<BuildTask> taskQueue = new PriorityBlockingQueue<>(320000, (a,b)-> Long.compareUnsigned(a.priority, b.priority));
|
private final PriorityBlockingQueue<BuildTask> taskQueue = new PriorityBlockingQueue<>(320000, (a,b)-> Long.compareUnsigned(a.priority, b.priority));
|
||||||
|
private final StampedLock taskMapLock = new StampedLock();
|
||||||
private final Long2ObjectOpenHashMap<BuildTask> taskMap = new Long2ObjectOpenHashMap<>(320000);
|
private final Long2ObjectOpenHashMap<BuildTask> taskMap = new Long2ObjectOpenHashMap<>(320000);
|
||||||
|
|
||||||
private final WorldEngine world;
|
private final WorldEngine world;
|
||||||
@@ -124,6 +127,7 @@ public class RenderGenerationService {
|
|||||||
//TODO: add a generated render data cache
|
//TODO: add a generated render data cache
|
||||||
private void processJob(RenderDataFactory45 factory, IntOpenHashSet seenMissedIds) {
|
private void processJob(RenderDataFactory45 factory, IntOpenHashSet seenMissedIds) {
|
||||||
BuildTask task = this.taskQueue.poll();
|
BuildTask task = this.taskQueue.poll();
|
||||||
|
this.taskQueueCount.decrementAndGet();
|
||||||
|
|
||||||
//long time = BuiltSection.getTime();
|
//long time = BuiltSection.getTime();
|
||||||
boolean shouldFreeSection = true;
|
boolean shouldFreeSection = true;
|
||||||
@@ -142,21 +146,24 @@ public class RenderGenerationService {
|
|||||||
section.assertNotFree();
|
section.assertNotFree();
|
||||||
BuiltSection mesh = null;
|
BuiltSection mesh = null;
|
||||||
|
|
||||||
synchronized (this.taskMap) {
|
{
|
||||||
|
long stamp = this.taskMapLock.writeLock();
|
||||||
var rtask = this.taskMap.remove(task.position);
|
var rtask = this.taskMap.remove(task.position);
|
||||||
if (rtask != task) {
|
if (rtask != task) {
|
||||||
|
this.taskMapLock.unlockWrite(stamp);
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
this.taskMapLock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mesh = factory.generateMesh(section);
|
mesh = factory.generateMesh(section);
|
||||||
} catch (IdNotYetComputedException e) {
|
} catch (IdNotYetComputedException e) {
|
||||||
{
|
{
|
||||||
BuildTask other;
|
long stamp = this.taskMapLock.writeLock();
|
||||||
synchronized (this.taskMap) {
|
BuildTask other = this.taskMap.putIfAbsent(task.position, task);
|
||||||
other = this.taskMap.putIfAbsent(task.position, task);
|
this.taskMapLock.unlockWrite(stamp);
|
||||||
}
|
|
||||||
if (other != null) {//Weve been replaced
|
if (other != null) {//Weve been replaced
|
||||||
//Request the block
|
//Request the block
|
||||||
if (e.isIdBlockId) {
|
if (e.isIdBlockId) {
|
||||||
@@ -225,6 +232,7 @@ public class RenderGenerationService {
|
|||||||
|
|
||||||
task.updatePriority();
|
task.updatePriority();
|
||||||
this.taskQueue.add(task);
|
this.taskQueue.add(task);
|
||||||
|
this.taskQueueCount.incrementAndGet();
|
||||||
|
|
||||||
if (this.threads.isAlive()) {//Only execute if were not dead
|
if (this.threads.isAlive()) {//Only execute if were not dead
|
||||||
this.threads.execute();//Since we put in queue, release permit
|
this.threads.execute();//Since we put in queue, release permit
|
||||||
@@ -244,17 +252,18 @@ public class RenderGenerationService {
|
|||||||
|
|
||||||
public void enqueueTask(long pos) {
|
public void enqueueTask(long pos) {
|
||||||
boolean[] isOurs = new boolean[1];
|
boolean[] isOurs = new boolean[1];
|
||||||
BuildTask task;
|
long stamp = this.taskMapLock.writeLock();
|
||||||
synchronized (this.taskMap) {
|
BuildTask task = this.taskMap.computeIfAbsent(pos, p->{
|
||||||
task = this.taskMap.computeIfAbsent(pos, p->{
|
|
||||||
isOurs[0] = true;
|
isOurs[0] = true;
|
||||||
return new BuildTask(p);
|
return new BuildTask(p);
|
||||||
});
|
});
|
||||||
}
|
this.taskMapLock.unlockWrite(stamp);
|
||||||
|
|
||||||
if (isOurs[0]) {//If its not ours we dont care about it
|
if (isOurs[0]) {//If its not ours we dont care about it
|
||||||
//Set priority and insert into queue and execute
|
//Set priority and insert into queue and execute
|
||||||
task.updatePriority();
|
task.updatePriority();
|
||||||
this.taskQueue.add(task);
|
this.taskQueue.add(task);
|
||||||
|
this.taskQueueCount.incrementAndGet();
|
||||||
this.threads.execute();
|
this.threads.execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,8 +279,8 @@ public class RenderGenerationService {
|
|||||||
while (this.threads.hasJobs()) {
|
while (this.threads.hasJobs()) {
|
||||||
int i = this.threads.drain();
|
int i = this.threads.drain();
|
||||||
if (i == 0) break;
|
if (i == 0) break;
|
||||||
|
{
|
||||||
synchronized (this.taskMap) {
|
long stamp = this.taskMapLock.writeLock();
|
||||||
for (int j = 0; j < i; j++) {
|
for (int j = 0; j < i; j++) {
|
||||||
var task = this.taskQueue.remove();
|
var task = this.taskQueue.remove();
|
||||||
if (task.section != null) {
|
if (task.section != null) {
|
||||||
@@ -281,6 +290,8 @@ public class RenderGenerationService {
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.taskMapLock.unlockWrite(stamp);
|
||||||
|
this.taskQueueCount.addAndGet(-i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,22 +301,24 @@ public class RenderGenerationService {
|
|||||||
//Cleanup any remaining data
|
//Cleanup any remaining data
|
||||||
while (!this.taskQueue.isEmpty()) {
|
while (!this.taskQueue.isEmpty()) {
|
||||||
var task = this.taskQueue.remove();
|
var task = this.taskQueue.remove();
|
||||||
|
this.taskQueueCount.decrementAndGet();
|
||||||
if (task.section != null) {
|
if (task.section != null) {
|
||||||
task.section.release();
|
task.section.release();
|
||||||
}
|
}
|
||||||
synchronized (this.taskMap) {
|
|
||||||
if (this.taskMap.remove(task.position) != task) {
|
long stamp = this.taskMapLock.writeLock();
|
||||||
throw new IllegalStateException();
|
if (this.taskMap.remove(task.position) != task) {
|
||||||
}
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
this.taskMapLock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDebugData(List<String> debug) {
|
public void addDebugData(List<String> debug) {
|
||||||
debug.add("RSSQ: " + this.taskQueue.size());//render section service queue
|
debug.add("RSSQ: " + this.taskQueueCount.get());//render section service queue
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTaskCount() {
|
public int getTaskCount() {
|
||||||
return this.taskQueue.size();
|
return this.taskQueueCount.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class MessageQueue <T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int consumeNano(long budget) {
|
public int consumeNano(long budget) {
|
||||||
if (budget < 25_000) return 0;
|
//if (budget < 25_000) return 0;
|
||||||
if (this.count.get() == 0) {
|
if (this.count.get() == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -61,9 +61,13 @@ public class MessageQueue <T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final void clear(Consumer<T> cleaner) {
|
public final void clear(Consumer<T> cleaner) {
|
||||||
while (!this.queue.isEmpty()) {
|
do {
|
||||||
cleaner.accept(this.queue.pop());
|
var v = this.queue.poll();
|
||||||
}
|
if (v == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cleaner.accept(v);
|
||||||
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int count() {
|
public int count() {
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ public class ActiveSectionTracker {
|
|||||||
private final SectionLoader loader;
|
private final SectionLoader loader;
|
||||||
|
|
||||||
private final int lruSize;
|
private final int lruSize;
|
||||||
|
private final StampedLock lruLock = new StampedLock();
|
||||||
private final Long2ObjectLinkedOpenHashMap<WorldSection> lruSecondaryCache;//TODO: THIS NEEDS TO BECOME A GLOBAL STATIC CACHE
|
private final Long2ObjectLinkedOpenHashMap<WorldSection> lruSecondaryCache;//TODO: THIS NEEDS TO BECOME A GLOBAL STATIC CACHE
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public final WorldEngine engine;
|
public final WorldEngine engine;
|
||||||
|
|
||||||
@@ -89,9 +91,9 @@ public class ActiveSectionTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isLoader) {
|
if (isLoader) {
|
||||||
synchronized (this.lruSecondaryCache) {
|
long stamp = this.lruLock.writeLock();
|
||||||
section = this.lruSecondaryCache.remove(key);
|
section = this.lruSecondaryCache.remove(key);
|
||||||
}
|
this.lruLock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
//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
|
||||||
@@ -170,14 +172,15 @@ public class ActiveSectionTracker {
|
|||||||
lock.unlockWrite(stamp);
|
lock.unlockWrite(stamp);
|
||||||
|
|
||||||
if (sec != null) {
|
if (sec != null) {
|
||||||
WorldSection a;
|
stamp = this.lruLock.writeLock();
|
||||||
synchronized (this.lruSecondaryCache) {
|
|
||||||
a = this.lruSecondaryCache.put(section.key, section);
|
WorldSection a = this.lruSecondaryCache.put(section.key, section);
|
||||||
//If cache is bigger than its ment to be, remove the least recently used and free it
|
//If cache is bigger than its ment to be, remove the least recently used and free it
|
||||||
if (a == null && this.lruSize < this.lruSecondaryCache.size()) {
|
if (a == null && this.lruSize < this.lruSecondaryCache.size()) {
|
||||||
a = this.lruSecondaryCache.removeFirst();
|
a = this.lruSecondaryCache.removeFirst();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
this.lruLock.unlockWrite(stamp);
|
||||||
|
|
||||||
if (a != null) {
|
if (a != null) {
|
||||||
a._releaseArray();
|
a._releaseArray();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user