Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/main/java/me/cortex/voxy/client/VoxyClientInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import me.cortex.voxy.client.compat.FlashbackCompat;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import me.cortex.voxy.client.mixin.sodium.AccessorSodiumWorldRenderer;
import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.StorageConfigUtil;
Expand Down Expand Up @@ -38,6 +39,16 @@ public VoxyClientInstance() {
this.basePath = path;
this.storageConfig = StorageConfigUtil.getCreateStorageConfig(Config.class, c->c.version==1&&c.sectionStorageConfig!=null, ()->DEFAULT_STORAGE_CONFIG, path).sectionStorageConfig;
this.updateDedicatedThreads();
this.updateLodThreadLimits();
}

public void updateLodThreadLimits() {
var config = VoxyConfig.CONFIG;
super.updateLodThreadLimits(config.lodIngestThreads, config.lodSaveThreads, config.lodLoadThreads);
var renderSystem = IGetVoxyRenderSystem.getNullable();
if (renderSystem != null) {
renderSystem.updateLodThreadLimits();
}
}

@Override
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/me/cortex/voxy/client/config/VoxyConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public class VoxyConfig implements OptionStorage<VoxyConfig> {
public boolean ingestEnabled = true;
public int sectionRenderDistance = 16;
public int serviceThreads = (int) Math.max(CpuLayout.getCoreCount()/1.5, 1);
public int lodLoadThreads = Math.max(1, CpuLayout.getCoreCount() / 4);
public int lodIngestThreads = Math.max(1, CpuLayout.getCoreCount() / 4);
public int lodMeshThreads = Math.max(1, CpuLayout.getCoreCount() / 4);
public int lodSaveThreads = 1;
public float subDivisionSize = 64;
public boolean useEnvironmentalFog = true;
public boolean dontUseSodiumBuilderThreads = false;
Expand All @@ -40,6 +44,7 @@ private static VoxyConfig loadOrCreate() {
try (FileReader reader = new FileReader(path.toFile())) {
var conf = GSON.fromJson(reader, VoxyConfig.class);
if (conf != null) {
conf.applyDefaults();
conf.save();
return conf;
} else {
Expand Down Expand Up @@ -88,4 +93,23 @@ private static Path getConfigPath() {
public boolean isRenderingEnabled() {
return VoxyCommon.isAvailable() && this.enabled && this.enableRendering;
}

private void applyDefaults() {
int cores = CpuLayout.getCoreCount();
if (this.serviceThreads <= 0) {
this.serviceThreads = (int) Math.max(cores / 1.5, 1);
}
if (this.lodLoadThreads <= 0) {
this.lodLoadThreads = Math.max(1, cores / 4);
}
if (this.lodIngestThreads <= 0) {
this.lodIngestThreads = Math.max(1, cores / 4);
}
if (this.lodMeshThreads <= 0) {
this.lodMeshThreads = Math.max(1, cores / 4);
}
if (this.lodSaveThreads <= 0) {
this.lodSaveThreads = 1;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package me.cortex.voxy.client.config;

import com.google.common.collect.ImmutableList;
import me.cortex.voxy.client.RenderStatistics;
import me.cortex.voxy.client.VoxyClientInstance;
import me.cortex.voxy.client.RenderStatistics;
import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
import me.cortex.voxy.common.util.cpu.CpuLayout;
import me.cortex.voxy.commonImpl.VoxyCommon;
Expand All @@ -19,6 +19,21 @@ private VoxyConfigPageSodium(){}

public static OptionPage voxyOptionPage = null;

private static int maxThreadSlider() {
if (CpuLayout.CORES != null) {
return CpuLayout.CORES.length;
}
return Runtime.getRuntime().availableProcessors();
}

private static void applyLodThreadLimits() {
var instance = VoxyCommon.getInstance();
if (instance instanceof VoxyClientInstance clientInstance) {
clientInstance.updateLodThreadLimits();
}
VoxyConfig.CONFIG.save();
}

public static OptionPage page() {
List<OptionGroup> groups = new ArrayList<>();
VoxyConfig storage = VoxyConfig.CONFIG;
Expand Down Expand Up @@ -57,8 +72,7 @@ public static OptionPage page() {
.setName(Component.translatable("voxy.config.general.serviceThreads"))
.setTooltip(Component.translatable("voxy.config.general.serviceThreads.tooltip"))
.setControl(opt->new SliderControl(opt, 1,
CpuLayout.CORES.length, //Just do core size as max
//Runtime.getRuntime().availableProcessors(),//Note: this is threads not cores, the default value is half the core count, is fine as this should technically be the limit but CpuLayout.CORES.length is more realistic
maxThreadSlider(),
1, v->Component.literal(Integer.toString(v))))
.setBinding((s, v)->{
s.serviceThreads = v;
Expand Down Expand Up @@ -93,6 +107,50 @@ public static OptionPage page() {
).build()
);

groups.add(OptionGroup.createBuilder()
.add(OptionImpl.createBuilder(int.class, storage)
.setName(Component.translatable("voxy.config.lod.loadThreads"))
.setTooltip(Component.translatable("voxy.config.lod.loadThreads.tooltip"))
.setControl(opt->new SliderControl(opt, 1, maxThreadSlider(), 1, v->Component.literal(Integer.toString(v))))
.setBinding((s, v)->{
s.lodLoadThreads = v;
applyLodThreadLimits();
}, s -> s.lodLoadThreads)
.setImpact(OptionImpact.HIGH)
.build()
).add(OptionImpl.createBuilder(int.class, storage)
.setName(Component.translatable("voxy.config.lod.ingestThreads"))
.setTooltip(Component.translatable("voxy.config.lod.ingestThreads.tooltip"))
.setControl(opt->new SliderControl(opt, 1, maxThreadSlider(), 1, v->Component.literal(Integer.toString(v))))
.setBinding((s, v)->{
s.lodIngestThreads = v;
applyLodThreadLimits();
}, s -> s.lodIngestThreads)
.setImpact(OptionImpact.HIGH)
.build()
).add(OptionImpl.createBuilder(int.class, storage)
.setName(Component.translatable("voxy.config.lod.meshThreads"))
.setTooltip(Component.translatable("voxy.config.lod.meshThreads.tooltip"))
.setControl(opt->new SliderControl(opt, 1, maxThreadSlider(), 1, v->Component.literal(Integer.toString(v))))
.setBinding((s, v)->{
s.lodMeshThreads = v;
applyLodThreadLimits();
}, s -> s.lodMeshThreads)
.setImpact(OptionImpact.HIGH)
.build()
).add(OptionImpl.createBuilder(int.class, storage)
.setName(Component.translatable("voxy.config.lod.saveThreads"))
.setTooltip(Component.translatable("voxy.config.lod.saveThreads.tooltip"))
.setControl(opt->new SliderControl(opt, 1, maxThreadSlider(), 1, v->Component.literal(Integer.toString(v))))
.setBinding((s, v)->{
s.lodSaveThreads = v;
applyLodThreadLimits();
}, s -> s.lodSaveThreads)
.setImpact(OptionImpact.MEDIUM)
.build()
).build()
);

groups.add(OptionGroup.createBuilder()
.add(OptionImpl.createBuilder(boolean.class, storage)
.setName(Component.translatable("voxy.config.general.rendering"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ public VoxyRenderSystem(WorldEngine world, ServiceManager sm) {
this.setRenderDistance(VoxyConfig.CONFIG.sectionRenderDistance);
}

this.updateLodThreadLimits();

this.chunkBoundRenderer = new ChunkBoundRenderer(this.pipeline);

Logger.info("Voxy render system created with " + geometryCapacity + " geometry capacity, using pipeline '" + this.pipeline.getClass().getSimpleName() + "' with renderer '" + sectionRenderer.getClass().getSimpleName() + "'");
Expand All @@ -163,6 +165,10 @@ public VoxyRenderSystem(WorldEngine world, ServiceManager sm) {
}


public void updateLodThreadLimits() {
this.renderGen.applyThreadLimit(VoxyConfig.CONFIG.lodMeshThreads);
}

public Viewport<?> setupViewport(ChunkRenderMatrices matrices, FogParameters fogParameters, double cameraX, double cameraY, double cameraZ) {
var viewport = this.getViewport();
if (viewport == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,9 @@ public void addDebugData(List<String> debug) {
public int getTaskCount() {
return this.taskQueueCount.get();
}

public void applyThreadLimit(int threads) {
this.service.setMaxConcurrent(threads);
this.service.setWeight(Math.max(1, threads));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,6 @@ public void createRenderer() {
}
}
instance.updateDedicatedThreads();
instance.updateLodThreadLimits();
}
}
41 changes: 38 additions & 3 deletions src/main/java/me/cortex/voxy/common/thread/Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@
import me.cortex.voxy.common.util.Pair;

import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;

public class Service {
private final PerThreadContextExecutor executor;
private final ServiceManager sm;
final long weight;
volatile long weight;
final String name;
final BooleanSupplier limiter;

private final Semaphore tasks = new Semaphore(0);
private final AtomicInteger activeJobs = new AtomicInteger();
private volatile int maxConcurrent = Integer.MAX_VALUE;
private volatile boolean isLive = true;
private volatile boolean isStopping = false;

Expand All @@ -27,6 +30,29 @@ public class Service {
this.executor = new PerThreadContextExecutor(ctxSupplier, e->sm.handleException(this, e));
}

public void setMaxConcurrent(int max) {
this.maxConcurrent = Math.max(1, max);
}

public void setWeight(long weight) {
this.weight = Math.max(1, weight);
}

public int getActiveJobs() {
return this.activeJobs.get();
}

public int getMaxConcurrent() {
return this.maxConcurrent;
}

boolean canAcceptJobs() {
if (this.activeJobs.get() >= this.maxConcurrent) {
return false;
}
return this.limiter == null || this.limiter.getAsBoolean();
}

public void execute() {
if (this.isStopping) {
Logger.error("Tried executing on a dead service");
Expand All @@ -44,8 +70,17 @@ boolean runJob() {
//Failed to get the job, probably due to a race condition
return false;
}
if (!this.executor.run()) {//Run the job
throw new IllegalStateException("Executor failed to run");
if (!this.canAcceptJobs()) {
this.tasks.release();
return false;
}
this.activeJobs.incrementAndGet();
try {
if (!this.executor.run()) {//Run the job
throw new IllegalStateException("Executor failed to run");
}
} finally {
this.activeJobs.decrementAndGet();
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ private int runAJob0() {//Executes a single job on the current thread
continue outer;//We need to refetch the array and start over
}
boolean sc = c--<=0;
if (service.limiter!=null && !service.limiter.getAsBoolean()) {
if (!service.canAcceptJobs()) {
skipMsk |= 1L<<i;
continue;
}
Expand All @@ -97,7 +97,7 @@ private int runAJob0() {//Executes a single job on the current thread

for (int i = 0; i < services.length; i++) {
var service = services[(i+shiftFactor)%services.length];
if (service.limiter!=null && (((skipMsk&(1L<<i))!=0)|| !service.limiter.getAsBoolean())) {
if (((skipMsk&(1L<<i))!=0) || !service.canAcceptJobs()) {
skipMsk |= 1L<<i;
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,12 @@ public WorldSection acquire(long key, boolean nullOnEmpty) {
WorldEngine.getZ(key),
this);

status = this.loader.load(section);
SectionLoadLimiter.acquire();
try {
status = this.loader.load(section);
} finally {
SectionLoadLimiter.release();
}

if (status < 0) {
//TODO: Instead if throwing an exception do something better, like attempting to regen
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/me/cortex/voxy/common/world/SectionLoadLimiter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package me.cortex.voxy.common.world;

import java.util.concurrent.atomic.AtomicInteger;

public final class SectionLoadLimiter {
private static volatile int maxConcurrent = 2;
private static final AtomicInteger active = new AtomicInteger();

private SectionLoadLimiter() {}

public static void setMaxConcurrent(int max) {
maxConcurrent = Math.max(1, max);
}

public static int getMaxConcurrent() {
return maxConcurrent;
}

public static int getActiveCount() {
return active.get();
}

public static void acquire() {
while (active.get() >= maxConcurrent) {
Thread.onSpinWait();
Thread.yield();
}
active.incrementAndGet();
}

public static void release() {
active.decrementAndGet();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,9 @@ public void shutdown() {
public int getTaskCount() {
return this.service.numJobs();
}

public void applyThreadLimit(int threads) {
this.service.setMaxConcurrent(threads);
this.service.setWeight(Math.max(1, threads));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ public int getTaskCount() {
return this.service.numJobs();
}

public void applyThreadLimit(int threads) {
this.service.setMaxConcurrent(threads);
this.service.setWeight(Math.max(1, threads));
}

public void shutdown() {
this.service.shutdown();
}
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/me/cortex/voxy/commonImpl/VoxyInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import me.cortex.voxy.common.thread.ServiceManager;
import me.cortex.voxy.common.thread.UnifiedServiceThreadPool;
import me.cortex.voxy.common.util.MemoryBuffer;
import me.cortex.voxy.common.world.SectionLoadLimiter;
import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.common.world.service.SectionSavingService;
import me.cortex.voxy.common.world.service.VoxelIngestService;
Expand Down Expand Up @@ -68,6 +69,12 @@ public void updateDedicatedThreads() {
this.setNumThreads(3);
}

public void updateLodThreadLimits(int ingestThreads, int saveThreads, int loadThreads) {
this.ingestService.applyThreadLimit(ingestThreads);
this.savingService.applyThreadLimit(saveThreads);
SectionLoadLimiter.setMaxConcurrent(loadThreads);
}

protected ImportManager createImportManager() {
return new ImportManager();
}
Expand Down
Loading