Skip to content

Commit dc54000

Browse files
committed
Implementation of task effort estimation and frame-rate independent task scheduling (CaffeineMC#2887 merge item 1)
1 parent 166d573 commit dc54000

40 files changed

+939
-299
lines changed

common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/staging/FallbackStagingBuffer.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import java.nio.ByteBuffer;
99

1010
public class FallbackStagingBuffer implements StagingBuffer {
11+
private static final float BYTES_PER_NANO_LIMIT = 8_000_000.0f / (1_000_000_000.0f / 60.0f); // MB per frame at 60fps
12+
1113
private final GlMutableBuffer fallbackBufferObject;
1214

1315
public FallbackStagingBuffer(CommandList commandList) {
@@ -39,4 +41,9 @@ public void flip() {
3941
public String toString() {
4042
return "Fallback";
4143
}
44+
45+
@Override
46+
public long getUploadSizeLimit(long frameDuration) {
47+
return (long) (frameDuration * BYTES_PER_NANO_LIMIT);
48+
}
4249
}

common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/staging/MappedStagingBuffer.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import java.util.List;
1616

1717
public class MappedStagingBuffer implements StagingBuffer {
18+
private static final float UPLOAD_LIMIT_MARGIN = 0.8f;
19+
1820
private static final EnumBitField<GlBufferStorageFlags> STORAGE_FLAGS =
1921
EnumBitField.of(GlBufferStorageFlags.PERSISTENT, GlBufferStorageFlags.CLIENT_STORAGE, GlBufferStorageFlags.MAP_WRITE);
2022

@@ -163,6 +165,11 @@ public void flip() {
163165
}
164166
}
165167

168+
@Override
169+
public long getUploadSizeLimit(long frameDuration) {
170+
return (long) (this.capacity * UPLOAD_LIMIT_MARGIN);
171+
}
172+
166173
private static final class CopyCommand {
167174
private final GlBuffer buffer;
168175
private final long readOffset;

common/src/main/java/net/caffeinemc/mods/sodium/client/gl/arena/staging/StagingBuffer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ public interface StagingBuffer {
1313
void delete(CommandList commandList);
1414

1515
void flip();
16+
17+
long getUploadSizeLimit(long frameDuration);
1618
}

common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumGameOptionPages.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import net.caffeinemc.mods.sodium.client.gui.options.control.*;
1616
import net.caffeinemc.mods.sodium.client.gui.options.storage.MinecraftOptionsStorage;
1717
import net.caffeinemc.mods.sodium.client.gui.options.storage.SodiumOptionsStorage;
18+
import net.caffeinemc.mods.sodium.client.render.chunk.DeferMode;
1819
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.QuadSplittingMode;
1920
import net.minecraft.client.*;
2021
import net.minecraft.network.chat.Component;
@@ -282,12 +283,12 @@ public static OptionPage performance() {
282283
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
283284
.build()
284285
)
285-
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
286-
.setName(Component.translatable("sodium.options.always_defer_chunk_updates.name"))
287-
.setTooltip(Component.translatable("sodium.options.always_defer_chunk_updates.tooltip"))
288-
.setControl(TickBoxControl::new)
286+
.add(OptionImpl.createBuilder(DeferMode.class, sodiumOpts)
287+
.setName(Component.translatable("sodium.options.defer_chunk_updates.name"))
288+
.setTooltip(Component.translatable("sodium.options.defer_chunk_updates.tooltip"))
289+
.setControl(option -> new CyclingControl<>(option, DeferMode.class))
289290
.setImpact(OptionImpact.HIGH)
290-
.setBinding((opts, value) -> opts.performance.alwaysDeferChunkUpdates = value, opts -> opts.performance.alwaysDeferChunkUpdates)
291+
.setBinding((opts, value) -> opts.performance.chunkBuildDeferMode = value, opts -> opts.performance.chunkBuildDeferMode)
291292
.setFlags(OptionFlag.REQUIRES_RENDERER_UPDATE)
292293
.build())
293294
.build()

common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumGameOptions.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import com.google.gson.FieldNamingPolicy;
44
import com.google.gson.Gson;
55
import com.google.gson.GsonBuilder;
6-
import com.google.gson.annotations.SerializedName;
76
import net.caffeinemc.mods.sodium.client.gui.options.TextProvider;
7+
import net.caffeinemc.mods.sodium.client.render.chunk.DeferMode;
88
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.QuadSplittingMode;
99
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.SortBehavior;
1010
import net.caffeinemc.mods.sodium.client.services.PlatformRuntimeInformation;
@@ -41,8 +41,7 @@ public static SodiumGameOptions defaults() {
4141

4242
public static class PerformanceSettings {
4343
public int chunkBuilderThreads = 0;
44-
@SerializedName("always_defer_chunk_updates_v2") // this will reset the option in older configs
45-
public boolean alwaysDeferChunkUpdates = true;
44+
public DeferMode chunkBuildDeferMode = DeferMode.ALWAYS;
4645

4746
public boolean animateOnlyVisibleTextures = true;
4847
public boolean useEntityCulling = true;

common/src/main/java/net/caffeinemc/mods/sodium/client/render/SodiumWorldRenderer.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,14 +191,14 @@ public void setupTerrain(Camera camera,
191191
float fogDistance = RenderSystem.getShaderFog().end();
192192

193193
if (this.lastCameraPos == null) {
194-
this.lastCameraPos = new Vector3d(pos);
194+
this.lastCameraPos = pos;
195195
}
196196
if (this.lastProjectionMatrix == null) {
197197
this.lastProjectionMatrix = new Matrix4f(projectionMatrix);
198198
}
199199
boolean cameraLocationChanged = !pos.equals(this.lastCameraPos);
200200
boolean cameraAngleChanged = pitch != this.lastCameraPitch || yaw != this.lastCameraYaw || fogDistance != this.lastFogDistance;
201-
boolean cameraProjectionChanged = !projectionMatrix.equals(this.lastProjectionMatrix);
201+
boolean cameraProjectionChanged = !projectionMatrix.equals(this.lastProjectionMatrix, 0.0001f);
202202

203203
this.lastProjectionMatrix = projectionMatrix;
204204

@@ -211,13 +211,13 @@ public void setupTerrain(Camera camera,
211211

212212
this.lastFogDistance = fogDistance;
213213

214-
this.renderSectionManager.updateCameraState(pos, camera);
214+
this.renderSectionManager.prepareFrame(pos);
215215

216216
if (cameraLocationChanged) {
217217
profiler.popPush("translucent_triggering");
218218

219219
this.renderSectionManager.processGFNIMovement(new CameraMovement(this.lastCameraPos, pos));
220-
this.lastCameraPos = new Vector3d(pos);
220+
this.lastCameraPos = pos;
221221
}
222222

223223
int maxChunkUpdates = updateChunksImmediately ? this.renderDistance : 1;

common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/ChunkUpdateType.java

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package net.caffeinemc.mods.sodium.client.render.chunk;
2+
3+
/**
4+
* Important: Whether the task is scheduled immediately after its creation. Otherwise, they're scheduled through
5+
* asynchronous culling that collects non-important tasks. Defer mode: For important tasks, how fast they are going to
6+
* be executed. One or zero frame deferral only allows one or zero frames to pass before the frame blocks on the task.
7+
* Always deferral allows the task to be deferred indefinitely, but if it's important it will still be put to the front
8+
* of the queue.
9+
*/
10+
public class ChunkUpdateTypes {
11+
public static final int SORT = 0b001;
12+
public static final int REBUILD = 0b010;
13+
public static final int IMPORTANT = 0b100;
14+
public static final int INITIAL_BUILD = 0b1000;
15+
16+
public static int join(int from, int to) {
17+
return from | to;
18+
}
19+
20+
public static boolean isSort(int type) {
21+
return (type & SORT) != 0;
22+
}
23+
24+
public static boolean isRebuild(int type) {
25+
return (type & REBUILD) != 0;
26+
}
27+
28+
public static boolean isImportant(int type) {
29+
return (type & IMPORTANT) != 0;
30+
}
31+
32+
public static boolean isInitialBuild(int type) {
33+
return (type & INITIAL_BUILD) != 0;
34+
}
35+
36+
public static boolean isRebuildWithSort(int type) {
37+
return (isRebuild(type) || isInitialBuild(type)) && isSort(type);
38+
}
39+
40+
public static TaskQueueType getQueueType(int type, TaskQueueType importantRebuildQueueType) {
41+
if (isInitialBuild(type)) {
42+
return TaskQueueType.INITIAL_BUILD;
43+
}
44+
if (isImportant(type)) {
45+
if (isRebuild(type)) {
46+
return importantRebuildQueueType;
47+
} else { // implies important sort task
48+
return TaskQueueType.ZERO_FRAME_DEFER;
49+
}
50+
} else {
51+
return TaskQueueType.ALWAYS_DEFER;
52+
}
53+
}
54+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package net.caffeinemc.mods.sodium.client.render.chunk;
2+
3+
import net.caffeinemc.mods.sodium.client.gui.options.TextProvider;
4+
import net.minecraft.network.chat.Component;
5+
6+
public enum DeferMode implements TextProvider {
7+
ALWAYS("sodium.options.defer_chunk_updates.always", TaskQueueType.ALWAYS_DEFER),
8+
ONE_FRAME("sodium.options.defer_chunk_updates.one_frame", TaskQueueType.ONE_FRAME_DEFER),
9+
ZERO_FRAMES("sodium.options.defer_chunk_updates.zero_frames", TaskQueueType.ZERO_FRAME_DEFER);
10+
11+
private final Component name;
12+
private final TaskQueueType importantRebuildQueueType;
13+
14+
DeferMode(String name, TaskQueueType importantRebuildQueueType) {
15+
this.name = Component.translatable(name);
16+
this.importantRebuildQueueType = importantRebuildQueueType;
17+
}
18+
19+
@Override
20+
public Component getLocalizedName() {
21+
return this.name;
22+
}
23+
24+
public TaskQueueType getImportantRebuildQueueType() {
25+
return this.importantRebuildQueueType;
26+
}
27+
}

common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSection.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package net.caffeinemc.mods.sodium.client.render.chunk;
22

3+
import net.caffeinemc.mods.sodium.client.render.chunk.compile.estimation.MeshResultSize;
34
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionInfo;
45
import net.caffeinemc.mods.sodium.client.render.chunk.occlusion.GraphDirection;
56
import net.caffeinemc.mods.sodium.client.render.chunk.occlusion.GraphDirectionSet;
@@ -54,9 +55,10 @@ public class RenderSection {
5455
// Pending Update State
5556
@Nullable
5657
private CancellationToken taskCancellationToken = null;
58+
private long lastMeshResultSize = MeshResultSize.NO_DATA;
5759

58-
@Nullable
59-
private ChunkUpdateType pendingUpdateType;
60+
private int pendingUpdateType;
61+
private long pendingUpdateSince;
6062

6163
private int lastUploadFrame = -1;
6264
private int lastSubmittedFrame = -1;
@@ -179,6 +181,14 @@ private boolean clearRenderState() {
179181
return wasBuilt;
180182
}
181183

184+
public void setLastMeshResultSize(long size) {
185+
this.lastMeshResultSize = size;
186+
}
187+
188+
public long getLastMeshResultSize() {
189+
return this.lastMeshResultSize;
190+
}
191+
182192
/**
183193
* Returns the chunk section position which this render refers to in the level.
184194
*/
@@ -347,12 +357,21 @@ public void setTaskCancellationToken(@Nullable CancellationToken token) {
347357
this.taskCancellationToken = token;
348358
}
349359

350-
public @Nullable ChunkUpdateType getPendingUpdate() {
360+
public int getPendingUpdate() {
351361
return this.pendingUpdateType;
352362
}
353363

354-
public void setPendingUpdate(@Nullable ChunkUpdateType type) {
364+
public long getPendingUpdateSince() {
365+
return this.pendingUpdateSince;
366+
}
367+
368+
public void setPendingUpdate(int type, long now) {
355369
this.pendingUpdateType = type;
370+
this.pendingUpdateSince = now;
371+
}
372+
373+
public void clearPendingUpdate() {
374+
this.pendingUpdateType = 0;
356375
}
357376

358377
public void prepareTrigger(boolean isDirectTrigger) {

0 commit comments

Comments
 (0)