Skip to content

Commit 33dabf5

Browse files
authored
feat: make number of chunk processing threads configurable (#5237)
* feat: make number of chunk processing threads configurable
1 parent 1c52f5e commit 33dabf5

File tree

12 files changed

+85
-17
lines changed

12 files changed

+85
-17
lines changed

engine-tests/src/test/java/org/terasology/engine/world/chunks/localChunkProvider/LocalChunkProviderTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import org.junit.jupiter.api.BeforeEach;
1111
import org.junit.jupiter.api.Test;
1212
import org.mockito.ArgumentCaptor;
13+
import org.terasology.engine.config.Config;
14+
import org.terasology.engine.config.RenderingConfig;
1315
import org.terasology.engine.entitySystem.entity.EntityManager;
1416
import org.terasology.engine.entitySystem.entity.EntityRef;
1517
import org.terasology.engine.world.BlockEntityRegistry;
@@ -43,6 +45,7 @@
4345
import static org.mockito.Mockito.atLeast;
4446
import static org.mockito.Mockito.mock;
4547
import static org.mockito.Mockito.verify;
48+
import static org.mockito.Mockito.when;
4649

4750
class LocalChunkProviderTest {
4851

@@ -54,6 +57,7 @@ class LocalChunkProviderTest {
5457
private ExtraBlockDataManager extraDataManager;
5558
private BlockEntityRegistry blockEntityRegistry;
5659
private EntityRef worldEntity;
60+
private Config config;
5761
private Map<Vector3ic, Chunk> chunkCache;
5862
private Block blockAtBlockManager;
5963
private TestStorageManager storageManager;
@@ -71,13 +75,18 @@ public void setUp() {
7175
blockEntityRegistry = mock(BlockEntityRegistry.class);
7276
worldEntity = mock(EntityRef.class);
7377
chunkCache = Maps.newConcurrentMap();
78+
config = mock(Config.class);
79+
RenderingConfig renderConfig = mock(RenderingConfig.class);
80+
when(renderConfig.getChunkThreads()).thenReturn(0);
81+
when(config.getRendering()).thenReturn(renderConfig);
7482
storageManager = new TestStorageManager();
7583
generator = new TestWorldGenerator(blockManager);
7684
chunkProvider = new LocalChunkProvider(storageManager,
7785
entityManager,
7886
generator,
7987
blockManager,
8088
extraDataManager,
89+
config,
8190
chunkCache);
8291
chunkProvider.setBlockEntityRegistry(blockEntityRegistry);
8392
chunkProvider.setWorldEntity(worldEntity);

engine-tests/src/test/java/org/terasology/engine/world/chunks/pipeline/ChunkProcessingPipelineTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class ChunkProcessingPipelineTest extends TerasologyTestingEnvironment {
5151

5252
@Test
5353
void simpleProcessingSuccess() throws ExecutionException, InterruptedException, TimeoutException {
54-
pipeline = new ChunkProcessingPipeline((p) -> null, (o1, o2) -> 0);
54+
pipeline = new ChunkProcessingPipeline(0, (p) -> null, (o1, o2) -> 0);
5555

5656
Vector3i chunkPos = new Vector3i(0, 0, 0);
5757
Chunk chunk = createChunkAt(chunkPos);
@@ -67,7 +67,7 @@ void simpleProcessingSuccess() throws ExecutionException, InterruptedException,
6767

6868
@Test
6969
void simpleStopProcessingSuccess() {
70-
pipeline = new ChunkProcessingPipeline((p) -> null, (o1, o2) -> 0);
70+
pipeline = new ChunkProcessingPipeline(0, (p) -> null, (o1, o2) -> 0);
7171

7272
Vector3i position = new Vector3i(0, 0, 0);
7373
Chunk chunk = createChunkAt(position);
@@ -106,7 +106,7 @@ void multiRequirementsChunksExistsSuccess() throws ExecutionException, Interrupt
106106
Function.identity()
107107
));
108108

109-
pipeline = new ChunkProcessingPipeline(chunkCache::get, (o1, o2) -> 0);
109+
pipeline = new ChunkProcessingPipeline(0, chunkCache::get, (o1, o2) -> 0);
110110
pipeline.addStage(ChunkTaskProvider.createMulti(
111111
"flat merging task",
112112
(chunks) -> chunks.stream()
@@ -140,7 +140,7 @@ void multiRequirementsChunksWillGeneratedSuccess() throws ExecutionException, In
140140
Function.identity()
141141
));
142142

143-
pipeline = new ChunkProcessingPipeline((p) -> null, (o1, o2) -> 0);
143+
pipeline = new ChunkProcessingPipeline(0, (p) -> null, (o1, o2) -> 0);
144144
pipeline.addStage(ChunkTaskProvider.createMulti(
145145
"flat merging task",
146146
(chunks) -> chunks.stream()
@@ -169,7 +169,7 @@ void emulateEntityMoving() throws InterruptedException {
169169
final AtomicReference<Vector3ic> position = new AtomicReference<>();
170170
Map<Vector3ic, Future<Chunk>> futures = Maps.newHashMap();
171171
Map<Vector3ic, Chunk> chunkCache = Maps.newConcurrentMap();
172-
pipeline = new ChunkProcessingPipeline(chunkCache::get, (o1, o2) -> {
172+
pipeline = new ChunkProcessingPipeline(0, chunkCache::get, (o1, o2) -> {
173173
if (position.get() != null) {
174174
Vector3ic entityPos = position.get();
175175
return (int) (entityPos.distance(((PositionFuture<?>) o1).getPosition())

engine/src/main/java/org/terasology/engine/config/RenderingConfig.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class RenderingConfig extends AbstractSubscribable {
2626
public static final String ANIMATED_MENU = "AnimatedMenu";
2727
public static final String VIEW_DISTANCE = "viewDistance";
2828
public static final String CHUNK_LODS = "chunkLods";
29+
public static final String CHUNK_THREADS = "chunkThreads";
2930
public static final String BILLBOARD_LIMIT = "billboardLimit";
3031
public static final String FLICKERING_LIGHT = "FlickeringLight";
3132
public static final String ANIMATE_GRASS = "AnimateGrass";
@@ -76,6 +77,7 @@ public class RenderingConfig extends AbstractSubscribable {
7677
private boolean animatedMenu;
7778
private ViewDistance viewDistance;
7879
private float chunkLods;
80+
private int chunkThreads;
7981
private float billboardLimit;
8082
private boolean flickeringLight;
8183
private boolean animateGrass;
@@ -270,6 +272,16 @@ public void setChunkLods(float chunkLods) {
270272
propertyChangeSupport.firePropertyChange(CHUNK_LODS, oldLods, chunkLods);
271273
}
272274

275+
public int getChunkThreads() {
276+
return chunkThreads;
277+
}
278+
279+
public void setChunkThreads(int chunkThreads) {
280+
float oldChunkThreads = this.chunkThreads;
281+
this.chunkThreads = chunkThreads;
282+
propertyChangeSupport.firePropertyChange(CHUNK_THREADS, oldChunkThreads, chunkThreads);
283+
}
284+
273285
public float getBillboardLimit() {
274286
return billboardLimit;
275287
}

engine/src/main/java/org/terasology/engine/core/modes/loadProcesses/InitialiseRemoteWorld.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
package org.terasology.engine.core.modes.loadProcesses;
55

6+
import org.terasology.engine.config.Config;
67
import org.terasology.engine.context.Context;
78
import org.terasology.engine.core.ComponentSystemManager;
89
import org.terasology.engine.core.TerasologyConstants;
@@ -54,7 +55,7 @@ public boolean step() {
5455
BlockManager blockManager = context.get(BlockManager.class);
5556
ExtraBlockDataManager extraDataManager = context.get(ExtraBlockDataManager.class);
5657

57-
RemoteChunkProvider chunkProvider = new RemoteChunkProvider(blockManager, localPlayer);
58+
RemoteChunkProvider chunkProvider = new RemoteChunkProvider(blockManager, localPlayer, context.get(Config.class));
5859

5960
WorldProviderCoreImpl worldProviderCore = new WorldProviderCoreImpl(gameManifest.getWorldInfo(TerasologyConstants.MAIN_WORLD), chunkProvider,
6061
blockManager.getBlock(BlockManager.UNLOADED_ID), context);

engine/src/main/java/org/terasology/engine/core/modes/loadProcesses/InitialiseWorld.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.google.common.collect.Maps;
77
import org.slf4j.Logger;
88
import org.slf4j.LoggerFactory;
9+
import org.terasology.engine.config.Config;
910
import org.terasology.engine.config.SystemConfig;
1011
import org.terasology.engine.context.Context;
1112
import org.terasology.engine.core.ComponentSystemManager;
@@ -136,6 +137,7 @@ public boolean step() {
136137
worldGenerator,
137138
blockManager,
138139
extraDataManager,
140+
context.get(Config.class),
139141
Maps.newConcurrentMap());
140142
RelevanceSystem relevanceSystem = new RelevanceSystem(chunkProvider);
141143
context.put(RelevanceSystem.class, relevanceSystem);

engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/videoSettings/VideoSettingsScreen.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,33 @@ public void initialise() {
7575
viewDistance.bindSelection(BindHelper.bindBeanProperty("viewDistance", config.getRendering(), ViewDistance.class));
7676
}
7777

78+
UISlider chunkThreads = find("chunkThreads", UISlider.class);
79+
if (chunkThreads != null) {
80+
chunkThreads.setIncrement(1.0f);
81+
chunkThreads.setPrecision(0);
82+
chunkThreads.setMinimum(0);
83+
chunkThreads.setRange(Runtime.getRuntime().availableProcessors());
84+
chunkThreads.setLabelFunction(input -> {
85+
if (input == 0) {
86+
return "Auto";
87+
} else {
88+
return String.valueOf(input.intValue());
89+
}
90+
});
91+
chunkThreads.bindValue(new Binding<Float>() {
92+
@Override
93+
public Float get() {
94+
return (float) config.getRendering().getChunkThreads();
95+
}
96+
97+
@Override
98+
public void set(Float value) {
99+
int chunkThreads = value.intValue();
100+
config.getRendering().setChunkThreads(chunkThreads);
101+
}
102+
});
103+
}
104+
78105
UIDropdown<WaterReflection> waterReflection = find("reflections", UIDropdown.class);
79106
if (waterReflection != null) {
80107
waterReflection.setOptionRenderer(new ToStringTextRenderer<>(translationSystem));

engine/src/main/java/org/terasology/engine/world/chunks/localChunkProvider/LocalChunkProvider.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.joml.Vector3ic;
1515
import org.slf4j.Logger;
1616
import org.slf4j.LoggerFactory;
17+
import org.terasology.engine.config.Config;
1718
import org.terasology.engine.entitySystem.entity.EntityManager;
1819
import org.terasology.engine.entitySystem.entity.EntityRef;
1920
import org.terasology.engine.entitySystem.entity.EntityStore;
@@ -94,6 +95,7 @@ public class LocalChunkProvider implements ChunkProvider {
9495
private final WorldGenerator generator;
9596
private final BlockManager blockManager;
9697
private final ExtraBlockDataManager extraDataManager;
98+
private final Config config;
9799
private ChunkProcessingPipeline loadingPipeline;
98100
private TaskMaster<ChunkUnloadRequest> unloadRequestTaskMaster;
99101
private EntityRef worldEntity = EntityRef.NULL;
@@ -102,13 +104,14 @@ public class LocalChunkProvider implements ChunkProvider {
102104
private RelevanceSystem relevanceSystem;
103105

104106
public LocalChunkProvider(StorageManager storageManager, EntityManager entityManager, WorldGenerator generator,
105-
BlockManager blockManager, ExtraBlockDataManager extraDataManager,
107+
BlockManager blockManager, ExtraBlockDataManager extraDataManager, Config config,
106108
Map<Vector3ic, Chunk> chunkCache) {
107109
this.storageManager = storageManager;
108110
this.entityManager = entityManager;
109111
this.generator = generator;
110112
this.blockManager = blockManager;
111113
this.extraDataManager = extraDataManager;
114+
this.config = config;
112115
this.unloadRequestTaskMaster = TaskMaster.createFIFOTaskMaster("Chunk-Unloader", 4);
113116
this.chunkCache = chunkCache;
114117
ChunkMonitor.fireChunkProviderInitialized(this);
@@ -406,7 +409,8 @@ public void purgeWorld() {
406409
storageManager.deleteWorld();
407410
worldEntity.send(new PurgeWorldEvent());
408411

409-
loadingPipeline = new ChunkProcessingPipeline(this::getChunk, relevanceSystem.createChunkTaskComporator());
412+
loadingPipeline = new ChunkProcessingPipeline(config.getRendering().getChunkThreads(), this::getChunk,
413+
relevanceSystem.createChunkTaskComporator());
410414
loadingPipeline.addStage(
411415
ChunkTaskProvider.create("Chunk generate internal lightning",
412416
(Consumer<Chunk>) InternalLightProcessor::generateInternalLighting))
@@ -441,7 +445,8 @@ private boolean isChunkReady(Chunk chunk) {
441445
// TODO: move loadingPipeline initialization into constructor.
442446
public void setRelevanceSystem(RelevanceSystem relevanceSystem) {
443447
this.relevanceSystem = relevanceSystem;
444-
loadingPipeline = new ChunkProcessingPipeline(this::getChunk, relevanceSystem.createChunkTaskComporator());
448+
loadingPipeline = new ChunkProcessingPipeline(config.getRendering().getChunkThreads(), this::getChunk,
449+
relevanceSystem.createChunkTaskComporator());
445450
loadingPipeline.addStage(
446451
ChunkTaskProvider.create("Chunk generate internal lightning",
447452
(Consumer<Chunk>) InternalLightProcessor::generateInternalLighting))

engine/src/main/java/org/terasology/engine/world/chunks/pipeline/ChunkProcessingPipeline.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
public class ChunkProcessingPipeline {
4040

4141
@SuppressWarnings("UnstableApiUsage")
42-
private static final int NUM_TASK_THREADS = constrainToRange(
43-
Runtime.getRuntime().availableProcessors() - 1, 1, 8);
42+
private static final int DEFAULT_TASK_THREADS = constrainToRange(
43+
Runtime.getRuntime().availableProcessors() - 2, 1, 4);
4444
private static final Logger logger = LoggerFactory.getLogger(ChunkProcessingPipeline.class);
4545

4646
private final List<ChunkTaskProvider> stages = Lists.newArrayList();
@@ -54,17 +54,18 @@ public class ChunkProcessingPipeline {
5454
/**
5555
* Create ChunkProcessingPipeline.
5656
*/
57-
public ChunkProcessingPipeline(Function<Vector3ic, Chunk> chunkProvider, Comparator<Future<Chunk>> comparable) {
57+
public ChunkProcessingPipeline(int chunkThreads, Function<Vector3ic, Chunk> chunkProvider, Comparator<Future<Chunk>> comparable) {
5858
this.chunkProvider = chunkProvider;
5959

60+
int taskThreads = (chunkThreads == 0) ? DEFAULT_TASK_THREADS : chunkThreads;
6061
executor = new ThreadPoolExecutor(
61-
NUM_TASK_THREADS,
62-
NUM_TASK_THREADS, 0L,
62+
taskThreads,
63+
taskThreads, 0L,
6364
TimeUnit.MILLISECONDS,
6465
new PriorityBlockingQueue(800, comparable),
6566
this::threadFactory,
6667
this::rejectQueueHandler);
67-
logger.debug("allocated {} threads", NUM_TASK_THREADS);
68+
logger.debug("allocated {} threads", taskThreads);
6869
chunkProcessor = new ChunkExecutorCompletionService(executor,
6970
new PriorityBlockingQueue<>(800, comparable));
7071
reactor = new Thread(this::chunkTaskHandler);

engine/src/main/java/org/terasology/engine/world/chunks/remoteChunkProvider/RemoteChunkProvider.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.joml.Vector3f;
1010
import org.joml.Vector3i;
1111
import org.joml.Vector3ic;
12+
import org.terasology.engine.config.Config;
1213
import org.terasology.engine.entitySystem.entity.EntityRef;
1314
import org.terasology.engine.logic.players.LocalPlayer;
1415
import org.terasology.engine.monitoring.chunk.ChunkMonitor;
@@ -56,9 +57,9 @@ public class RemoteChunkProvider implements ChunkProvider {
5657
private EntityRef worldEntity = EntityRef.NULL;
5758
private ChunkReadyListener listener;
5859

59-
public RemoteChunkProvider(BlockManager blockManager, LocalPlayer localPlayer) {
60+
public RemoteChunkProvider(BlockManager blockManager, LocalPlayer localPlayer, Config config) {
6061
this.blockManager = blockManager;
61-
loadingPipeline = new ChunkProcessingPipeline(this::getChunk,
62+
loadingPipeline = new ChunkProcessingPipeline(config.getRendering().getChunkThreads(), this::getChunk,
6263
new LocalPlayerRelativeChunkComparator(localPlayer));
6364

6465
loadingPipeline.addStage(

engine/src/main/resources/org/terasology/engine/assets/i18n/menu.lang

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@
426426
"view-distance-moderate": "view-distance-moderate",
427427
"view-distance-near": "view-distance-near",
428428
"view-distance-ultra": "view-distance-ultra",
429+
"chunk-threads": "chunk-threads",
429430
"vignette": "vignette",
430431
"volumetric-fog": "volumetric-fog",
431432
"warn-modules": "warn-modules",

0 commit comments

Comments
 (0)