Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asynchronous Render List Generation and Frame-Independent Task Scheduling #2887

Open
wants to merge 81 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
caca14a
documentation cleanup
douira Aug 29, 2024
9393180
do frustum cull using a linear octree, WIP (BFS isn't actually decoup…
douira Aug 29, 2024
5ced0f9
use faster deinterleave with one fewer steps
douira Aug 29, 2024
6fd8bd0
implement front-to-back ordering during tree traversal
douira Aug 29, 2024
83aca13
refactor plumbing between RSM and the chunk renderer to avoid touchin…
douira Nov 25, 2024
24eadd6
filter out sections that don't render anything before adding them to …
douira Sep 4, 2024
12be3dd
change tree read timing to average 100 samples
douira Sep 4, 2024
805864f
doesn't need to be public anymore
douira Sep 4, 2024
27e3f37
more reasonable flag mask ordering
douira Sep 4, 2024
0a2d2e1
take 2000 samples for measurement
douira Sep 4, 2024
f8cecd0
fix entity culling
douira Sep 4, 2024
a4c2595
split node iteration into separate loops, no change in performance, c…
douira Sep 4, 2024
3bcc1ee
use section coordinates for tree compatibility test
douira Sep 5, 2024
86b1c92
remove unnecessary frustum test from entity culling
douira Sep 5, 2024
bec6a5e
refactor update state management in RSM, add option to fall back to s…
douira Sep 5, 2024
cb7e306
add better async bfs and tree management. It now calculates multiple …
douira Sep 6, 2024
3ffa22e
prevent some NPEs that happen because of concurrency with loading and…
douira Sep 6, 2024
453c9bb
comment
douira Sep 6, 2024
f376ca7
increase tasks per thread per frame to 3, this seems to usually be ok
douira Sep 6, 2024
1b1f453
do render distance and fog culling in the tree traversal to prevent i…
douira Sep 6, 2024
c296bd7
don't do frustum bfs if the frame was dirty to prevent unnecessary work
douira Sep 7, 2024
1fd9d23
write a new async culling task and result system
douira Sep 13, 2024
5948984
remove debug changes
douira Sep 13, 2024
91b8c43
fix issues with occlusion culling by making the graph search generate…
douira Sep 13, 2024
0d5f7fe
introduce adaptive scheduling of cull tasks to reduce oscillation of …
douira Sep 13, 2024
69a081a
formatting
douira Sep 13, 2024
3521f3c
improve documentation
douira Sep 14, 2024
6459db2
ideas
douira Sep 14, 2024
78ca4bf
move occlusion culler's parameter gymnastics into fields for code sim…
douira Sep 15, 2024
d461547
added efficient approximative ray cast backed by a bitmap to improve …
douira Sep 17, 2024
4f3eb0e
further improve speed of tree building by writing the reduced tree da…
douira Sep 17, 2024
15e1b2b
restore some info on the debug screen
douira Sep 17, 2024
1811b98
fix section ordering
douira Oct 2, 2024
1ddc2eb
improve how coordinate parameters are passed into section tree traversal
douira Oct 2, 2024
6645a44
fix issues when camera is outside of world
douira Oct 2, 2024
04ce0cd
notes
douira Oct 2, 2024
7efe0ba
fix crash when loading under water
douira Oct 9, 2024
92f0308
fix missing world when fog distance changes, and flickering of render…
douira Oct 10, 2024
d3e5faa
remove unused best type variable
douira Oct 10, 2024
2c22c93
fix broken cancellation behavior on sync bfs
douira Oct 10, 2024
d9e0e83
implement frame rate independent task scheduling with effort-based ta…
douira Oct 11, 2024
e6f6ac0
add angle-based section visibility path occlusion, cherry picked from…
douira Oct 11, 2024
0444b0f
make inner tree in section tree static
douira Nov 7, 2024
cdccfa6
refactor tree structures to be more self-contained and easier to make…
douira Nov 10, 2024
abbdea4
fix rebase changes
douira Nov 10, 2024
83e2a33
make traversable trees abstract so that they can be subclassed with a…
douira Nov 10, 2024
e1cda93
ordering consistency
douira Nov 10, 2024
a3eaecd
fix incorrect multi forest size calculation
douira Nov 10, 2024
2215691
cleanup forest build distance handling
douira Nov 14, 2024
2f82fc6
fix section sorting by correcting the camera offset
douira Nov 16, 2024
3c7ebb2
update todos
douira Nov 21, 2024
ccb9321
update debug info to be relevant and useful again
douira Nov 22, 2024
18b3c21
refactor job effort estimation, add category-based meshing task size …
douira Nov 22, 2024
1075f4b
sort render lists separately when doing sync rendering, since they're…
douira Dec 7, 2024
d47b9d1
remove completed todos
douira Nov 27, 2024
339eb59
fix some issues with very delayed section sorting. There are still ot…
douira Dec 2, 2024
36ecf8e
fix sorting by using correct section pos calculation and improve frus…
douira Dec 7, 2024
38b3aff
add documentation and rename some methods
douira Dec 21, 2024
4c4c0b2
add removable tree and forest system for supporting the out-of-graph …
douira Dec 27, 2024
e9992c4
cleanup some todos
douira Dec 27, 2024
5c5ec7d
count tasks using ints instead of longs
douira Dec 30, 2024
f205f0b
put task list collection into its own file
douira Dec 30, 2024
8874df1
move exponential moving average to the math util file
douira Dec 30, 2024
fa3bb7d
fix synchronous sort tasks, change important task scheduling to be se…
douira Dec 31, 2024
f489747
add region sorting to fix translucency sorting issues,
douira Dec 31, 2024
652c09c
use small delta instead of strict equality when comparing projection …
douira Jan 2, 2025
ba79734
prevent estimator from corrupting its data with Infinity
douira Jan 3, 2025
bf79c48
fix forgotten chunk updates stemming from concurrently building a tas…
douira Jan 3, 2025
4df703a
split important and defer mode in the chunk update type to allow for …
douira Jan 3, 2025
068332e
rename updateCameraState to something more descriptive of its actual …
douira Jan 3, 2025
2864a17
adjust pending task scheduling time priority factor
douira Jan 3, 2025
fc9ee4f
consolidate tests that check whether a render section has anything to…
douira Jan 3, 2025
9c96385
add option for changing the tri-state defer mode, rename "important" …
douira Jan 4, 2025
6bb25a6
update job duration factor faster to keep up with JVM warmup at first…
douira Jan 4, 2025
0a8cc9e
rewrite estimation code to split into 1d average and 2d linear regres…
douira Jan 5, 2025
29e8ab7
add correct debug presentation of the estimator models
douira Jan 5, 2025
fafede3
improve naming of defer chunk updates option
douira Jan 5, 2025
7f046e1
fix max section bound for ray occlusion tree after Minecraft changed …
douira Jan 13, 2025
76c8eec
make tree selection for rendering more robust and avoid waiting for a…
douira Jan 14, 2025
9f96fd6
fix concurrent modification of tree hashmap
douira Jan 14, 2025
99bd356
fix tree index calculation and out of bounds cases with multi-forests
douira Jan 14, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import java.nio.ByteBuffer;

public class FallbackStagingBuffer implements StagingBuffer {
private static final float BYTES_PER_NANO_LIMIT = 8_000_000.0f / (1_000_000_000.0f / 60.0f); // MB per frame at 60fps

private final GlMutableBuffer fallbackBufferObject;

public FallbackStagingBuffer(CommandList commandList) {
Expand Down Expand Up @@ -39,4 +41,9 @@ public void flip() {
public String toString() {
return "Fallback";
}

@Override
public long getUploadSizeLimit(long frameDuration) {
return (long) (frameDuration * BYTES_PER_NANO_LIMIT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import java.util.List;

public class MappedStagingBuffer implements StagingBuffer {
private static final float UPLOAD_LIMIT_MARGIN = 0.8f;

private static final EnumBitField<GlBufferStorageFlags> STORAGE_FLAGS =
EnumBitField.of(GlBufferStorageFlags.PERSISTENT, GlBufferStorageFlags.CLIENT_STORAGE, GlBufferStorageFlags.MAP_WRITE);

Expand Down Expand Up @@ -156,6 +158,11 @@ public void flip() {
}
}

@Override
public long getUploadSizeLimit(long frameDuration) {
return (long) (this.capacity * UPLOAD_LIMIT_MARGIN);
}

private static final class CopyCommand {
private final GlBuffer buffer;
private final long readOffset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public interface StagingBuffer {
void delete(CommandList commandList);

void flip();

long getUploadSizeLimit(long frameDuration);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import net.caffeinemc.mods.sodium.client.gui.options.storage.MinecraftOptionsStorage;
import net.caffeinemc.mods.sodium.client.gui.options.storage.SodiumOptionsStorage;
import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds;
import net.caffeinemc.mods.sodium.client.render.chunk.DeferMode;
import net.caffeinemc.mods.sodium.client.services.PlatformRuntimeInformation;
import net.minecraft.client.AttackIndicatorStatus;
import net.minecraft.client.InactivityFpsLimit;
Expand Down Expand Up @@ -279,12 +280,12 @@ public static OptionPage performance() {
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.build()
)
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
.setName(Component.translatable("sodium.options.always_defer_chunk_updates.name"))
.setTooltip(Component.translatable("sodium.options.always_defer_chunk_updates.tooltip"))
.setControl(TickBoxControl::new)
.add(OptionImpl.createBuilder(DeferMode.class, sodiumOpts)
.setName(Component.translatable("sodium.options.defer_chunk_updates.name"))
.setTooltip(Component.translatable("sodium.options.defer_chunk_updates.tooltip"))
.setControl(option -> new CyclingControl<>(option, DeferMode.class))
.setImpact(OptionImpact.HIGH)
.setBinding((opts, value) -> opts.performance.alwaysDeferChunkUpdates = value, opts -> opts.performance.alwaysDeferChunkUpdates)
.setBinding((opts, value) -> opts.performance.chunkBuildDeferMode = value, opts -> opts.performance.chunkBuildDeferMode)
.setFlags(OptionFlag.REQUIRES_RENDERER_UPDATE)
.build())
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import net.caffeinemc.mods.sodium.client.gui.options.TextProvider;
import net.caffeinemc.mods.sodium.client.render.chunk.DeferMode;
import net.caffeinemc.mods.sodium.client.services.PlatformRuntimeInformation;
import net.caffeinemc.mods.sodium.client.util.FileUtil;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.SortBehavior;
Expand Down Expand Up @@ -37,8 +38,7 @@ public static SodiumGameOptions defaults() {

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

public boolean animateOnlyVisibleTextures = true;
public boolean useEntityCulling = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.BlockDestructionProgress;
import net.minecraft.util.Mth;
import net.minecraft.util.profiling.Profiler;
Expand Down Expand Up @@ -193,48 +192,45 @@ public void setupTerrain(Camera camera,
float fogDistance = RenderSystem.getShaderFog().end();

if (this.lastCameraPos == null) {
this.lastCameraPos = new Vector3d(pos);
this.lastCameraPos = pos;
}
if (this.lastProjectionMatrix == null) {
this.lastProjectionMatrix = new Matrix4f(projectionMatrix);
}
boolean cameraLocationChanged = !pos.equals(this.lastCameraPos);
boolean cameraAngleChanged = pitch != this.lastCameraPitch || yaw != this.lastCameraYaw || fogDistance != this.lastFogDistance;
boolean cameraProjectionChanged = !projectionMatrix.equals(this.lastProjectionMatrix);
boolean cameraProjectionChanged = !projectionMatrix.equals(this.lastProjectionMatrix, 0.0001f);

this.lastProjectionMatrix = projectionMatrix;

this.lastCameraPitch = pitch;
this.lastCameraYaw = yaw;

if (cameraLocationChanged || cameraAngleChanged || cameraProjectionChanged) {
this.renderSectionManager.markGraphDirty();
this.renderSectionManager.notifyChangedCamera();
}

this.lastFogDistance = fogDistance;

this.renderSectionManager.updateCameraState(pos, camera);
this.renderSectionManager.prepareFrame(pos);

if (cameraLocationChanged) {
profiler.popPush("translucent_triggering");

this.renderSectionManager.processGFNIMovement(new CameraMovement(this.lastCameraPos, pos));
this.lastCameraPos = new Vector3d(pos);
this.lastCameraPos = pos;
}

int maxChunkUpdates = updateChunksImmediately ? this.renderDistance : 1;

for (int i = 0; i < maxChunkUpdates; i++) {
if (this.renderSectionManager.needsUpdate()) {
profiler.popPush("chunk_render_lists");
profiler.popPush("chunk_render_lists");

this.renderSectionManager.update(camera, viewport, spectator);
}
this.renderSectionManager.updateRenderLists(camera, viewport, spectator, updateChunksImmediately);

profiler.popPush("chunk_update");

this.renderSectionManager.cleanupAndFlip();
this.renderSectionManager.updateChunks(updateChunksImmediately);
this.renderSectionManager.updateChunks(viewport, updateChunksImmediately);

profiler.popPush("chunk_upload");

Expand All @@ -255,6 +251,7 @@ public void setupTerrain(Camera camera,
}

private void processChunkEvents() {
this.renderSectionManager.beforeSectionUpdates();
var tracker = ChunkTrackerHolder.get(this.level);
tracker.forEachEvent(this.renderSectionManager::onChunkAdded, this.renderSectionManager::onChunkRemoved);
}
Expand Down Expand Up @@ -345,9 +342,8 @@ private void renderBlockEntities(PoseStack matrices,

while (renderSectionIterator.hasNext()) {
var renderSectionId = renderSectionIterator.nextByteAsInt();
var renderSection = renderRegion.getSection(renderSectionId);

var blockEntities = renderSection.getCulledBlockEntities();
var blockEntities = renderRegion.getCulledBlockEntities(renderSectionId);

if (blockEntities == null) {
continue;
Expand All @@ -372,7 +368,7 @@ private void renderGlobalBlockEntities(PoseStack matrices,
LocalPlayer player,
LocalBooleanRef isGlowing) {
for (var renderSection : this.renderSectionManager.getSectionsWithGlobalEntities()) {
var blockEntities = renderSection.getGlobalBlockEntities();
var blockEntities = renderSection.getRegion().getGlobalBlockEntities(renderSection.getSectionIndex());

if (blockEntities == null) {
continue;
Expand Down Expand Up @@ -446,9 +442,7 @@ public void iterateVisibleBlockEntities(Consumer<BlockEntity> blockEntityConsume

while (renderSectionIterator.hasNext()) {
var renderSectionId = renderSectionIterator.nextByteAsInt();
var renderSection = renderRegion.getSection(renderSectionId);

var blockEntities = renderSection.getCulledBlockEntities();
var blockEntities = renderRegion.getCulledBlockEntities(renderSectionId);

if (blockEntities == null) {
continue;
Expand All @@ -461,7 +455,7 @@ public void iterateVisibleBlockEntities(Consumer<BlockEntity> blockEntityConsume
}

for (var renderSection : this.renderSectionManager.getSectionsWithGlobalEntities()) {
var blockEntities = renderSection.getGlobalBlockEntities();
var blockEntities = renderSection.getRegion().getGlobalBlockEntities(renderSection.getSectionIndex());

if (blockEntities == null) {
continue;
Expand All @@ -474,10 +468,13 @@ public void iterateVisibleBlockEntities(Consumer<BlockEntity> blockEntityConsume
}

// the volume of a section multiplied by the number of sections to be checked at most
private static final double MAX_ENTITY_CHECK_VOLUME = 16 * 16 * 16 * 15;
private static final double MAX_ENTITY_CHECK_VOLUME = 16 * 16 * 16 * 50;

/**
* Returns whether or not the entity intersects with any visible chunks in the graph.
* Returns whether the entity intersects with any visible chunks in the graph.
*
* Note that this method assumes the entity is within the frustum. It does not perform a frustum check.
*
* @return True if the entity is visible, otherwise false
*/
public <T extends Entity, S extends EntityRenderState> boolean isEntityVisible(EntityRenderer<T, S> renderer, T entity) {
Expand All @@ -495,7 +492,7 @@ public <T extends Entity, S extends EntityRenderState> boolean isEntityVisible(E
// bail on very large entities to avoid checking many sections
double entityVolume = (bb.maxX - bb.minX) * (bb.maxY - bb.minY) * (bb.maxZ - bb.minZ);
if (entityVolume > MAX_ENTITY_CHECK_VOLUME) {
// TODO: do a frustum check instead, even large entities aren't visible if they're outside the frustum
// large entities are only frustum tested, their sections are not checked for visibility
return true;
}

Expand All @@ -509,48 +506,28 @@ public boolean isBoxVisible(double x1, double y1, double z1, double x2, double y
return true;
}

int minX = SectionPos.posToSectionCoord(x1 - 0.5D);
int minY = SectionPos.posToSectionCoord(y1 - 0.5D);
int minZ = SectionPos.posToSectionCoord(z1 - 0.5D);

int maxX = SectionPos.posToSectionCoord(x2 + 0.5D);
int maxY = SectionPos.posToSectionCoord(y2 + 0.5D);
int maxZ = SectionPos.posToSectionCoord(z2 + 0.5D);

for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
for (int y = minY; y <= maxY; y++) {
if (this.renderSectionManager.isSectionVisible(x, y, z)) {
return true;
}
}
}
}

return false;
return this.renderSectionManager.isBoxVisible(x1, y1, z1, x2, y2, z2);
}

public String getChunksDebugString() {
// C: visible/total D: distance
// TODO: add dirty and queued counts
return String.format("C: %d/%d D: %d", this.renderSectionManager.getVisibleChunkCount(), this.renderSectionManager.getTotalSections(), this.renderDistance);
return this.renderSectionManager.getChunksDebugString();
}

/**
* Schedules chunk rebuilds for all chunks in the specified block region.
*/
public void scheduleRebuildForBlockArea(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, boolean important) {
this.scheduleRebuildForChunks(minX >> 4, minY >> 4, minZ >> 4, maxX >> 4, maxY >> 4, maxZ >> 4, important);
public void scheduleRebuildForBlockArea(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, boolean playerChanged) {
this.scheduleRebuildForChunks(minX >> 4, minY >> 4, minZ >> 4, maxX >> 4, maxY >> 4, maxZ >> 4, playerChanged);
}

/**
* Schedules chunk rebuilds for all chunks in the specified chunk region.
*/
public void scheduleRebuildForChunks(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, boolean important) {
public void scheduleRebuildForChunks(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, boolean playerChanged) {
for (int chunkX = minX; chunkX <= maxX; chunkX++) {
for (int chunkY = minY; chunkY <= maxY; chunkY++) {
for (int chunkZ = minZ; chunkZ <= maxZ; chunkZ++) {
this.scheduleRebuildForChunk(chunkX, chunkY, chunkZ, important);
this.scheduleRebuildForChunk(chunkX, chunkY, chunkZ, playerChanged);
}
}
}
Expand All @@ -559,8 +536,8 @@ public void scheduleRebuildForChunks(int minX, int minY, int minZ, int maxX, int
/**
* Schedules a chunk rebuild for the render belonging to the given chunk section position.
*/
public void scheduleRebuildForChunk(int x, int y, int z, boolean important) {
this.renderSectionManager.scheduleRebuild(x, y, z, important);
public void scheduleRebuildForChunk(int x, int y, int z, boolean playerChanged) {
this.renderSectionManager.scheduleRebuild(x, y, z, playerChanged);
}

public Collection<String> getDebugStrings() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,46 @@
package net.caffeinemc.mods.sodium.client.render.chunk;

import net.caffeinemc.mods.sodium.client.render.chunk.compile.executor.ChunkBuilder;
import org.jetbrains.annotations.NotNull;

/**
* Important: Whether the task is scheduled immediately after its creation. Otherwise, they're scheduled through asynchronous culling that collects non-important tasks.
* Defer mode: For important tasks, how fast they are going to be executed. One or zero frame deferral only allows one or zero frames to pass before the frame blocks on the task. Always deferral allows the task to be deferred indefinitely, but if it's important it will still be put to the front of the queue.
*/
public enum ChunkUpdateType {
SORT(Integer.MAX_VALUE, ChunkBuilder.LOW_EFFORT),
INITIAL_BUILD(128, ChunkBuilder.HIGH_EFFORT),
REBUILD(Integer.MAX_VALUE, ChunkBuilder.HIGH_EFFORT),
IMPORTANT_REBUILD(Integer.MAX_VALUE, ChunkBuilder.HIGH_EFFORT),
IMPORTANT_SORT(Integer.MAX_VALUE, ChunkBuilder.LOW_EFFORT);

private final int maximumQueueSize;
private final int taskEffort;

ChunkUpdateType(int maximumQueueSize, int taskEffort) {
this.maximumQueueSize = maximumQueueSize;
this.taskEffort = taskEffort;
SORT(2),
INITIAL_BUILD(0),
REBUILD(1),
IMPORTANT_REBUILD(DeferMode.ZERO_FRAMES, 1),
IMPORTANT_SORT(DeferMode.ZERO_FRAMES, 2);

private final DeferMode deferMode;
private final boolean important;
private final float priorityValue;

ChunkUpdateType(float priorityValue) {
this.deferMode = DeferMode.ALWAYS;
this.important = false;
this.priorityValue = priorityValue;
}

ChunkUpdateType(@NotNull DeferMode deferMode, float priorityValue) {
this.deferMode = deferMode;
this.important = true;
this.priorityValue = priorityValue;
}

public static ChunkUpdateType getPromotionUpdateType(ChunkUpdateType prev, ChunkUpdateType next) {
if (prev == null || prev == SORT || prev == next) {
/**
* Returns a promoted update type if the new update type is more important than the previous one. Nothing is returned if the update type is the same or less important.
*
* @param prev Previous update type
* @param next New update type
* @return Promoted update type or {@code null} if the update type is the same or less important
*/
public static ChunkUpdateType getPromotedTypeChange(ChunkUpdateType prev, ChunkUpdateType next) {
if (prev == next) {
return null;
}
if (prev == null || prev == SORT || prev == INITIAL_BUILD) {
return next;
}
if (next == IMPORTANT_REBUILD
Expand All @@ -29,15 +51,15 @@ public static ChunkUpdateType getPromotionUpdateType(ChunkUpdateType prev, Chunk
return null;
}

public int getMaximumQueueSize() {
return this.maximumQueueSize;
public boolean isImportant() {
return this.important;
}

public boolean isImportant() {
return this == IMPORTANT_REBUILD || this == IMPORTANT_SORT;
public DeferMode getDeferMode(DeferMode importantRebuildDeferMode) {
return this == IMPORTANT_REBUILD ? importantRebuildDeferMode : this.deferMode;
}

public int getTaskEffort() {
return this.taskEffort;
public float getPriorityValue() {
return this.priorityValue;
}
}
Loading
Loading