Skip to content

Commit

Permalink
Lazy as can be
Browse files Browse the repository at this point in the history
- Only push light sections to the engine when the set of sections
  requested by visuals changes
- Clean up light storage plan and comment code
- Remove LIGHT_VOLUME debug mode as it's no longer used
  • Loading branch information
Jozufozu committed Jul 13, 2024
1 parent 4b04d74 commit d63e592
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ public interface Engine {
/**
* Assign the set of sections that visuals have requested GPU light for.
*
* <p> This will be called at most once per frame, and not necessarily every frame.
*
* @param sections The set of sections.
*/
void lightSections(LongSet sections);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@

import it.unimi.dsi.fastutil.longs.LongSet;

/**
* An interface allowing visuals to request light data on the GPU for a set of sections.
*
* <p> Sections passed into {@link SectionProperty#lightSections} will have their light data handed to the
* backend and queryable by {@code flw_light*} functions in shaders.
* <br>
* Note that the queryable light data is shared across all visuals, so even if one specific visual does not
* request a given section, the data will be available if another visual does.
*/
public interface SmoothLitVisual extends Visual {
/**
* Set the section property object.
*
* <p>This method is only called once, upon visual creation,
* <p>This method is only called once, upon visual creation.
*
* @param property The property.
*/
Expand All @@ -17,7 +26,7 @@ public interface SmoothLitVisual extends Visual {
@ApiStatus.NonExtendable
interface SectionProperty {
/**
* Invoke this to indicate to the impl that your visual has moved to a different set of sections.
* Assign the set of sections this visual wants to have light data for.
*/
void lightSections(LongSet sections);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.Long2IntMap;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
Expand Down Expand Up @@ -59,53 +60,77 @@ public LightStorage(LevelAccessor level) {
arena = new Arena(SECTION_SIZE_BYTES, DEFAULT_ARENA_CAPACITY_SECTIONS);
}

/**
* Set the set of requested sections.
* <p> When set, this will be processed in the next frame plan. It may not be set every frame.
*
* @param sections The set of sections requested by the impl.
*/
public void sections(LongSet sections) {
requestedSections = sections;
}

public Plan<RenderContext> createFramePlan() {
return SimplePlan.of(() -> {
if (requestedSections == null) {
var updatedSections = LightUpdateHolder.get(level)
.getAndClearUpdatedSections();

if (updatedSections.isEmpty() && requestedSections == null) {
return;
}

removeUnusedSections(requestedSections);

var knownSections = section2ArenaIndex.keySet();

var updatedSections = LightUpdateHolder.get(level)
.getUpdatedSections();
removeUnusedSections();

// Only add the new sections.
requestedSections.removeAll(knownSections);
// Start building the set of sections we need to collect this frame.
LongSet sectionsToCollect;
if (requestedSections == null) {
// If none were requested, then we need to collect all sections that received updates.
sectionsToCollect = new LongArraySet();
} else {
// If we did receive a new set of requested sections, we only
// need to collect the sections that weren't yet tracked.
sectionsToCollect = requestedSections;
sectionsToCollect.removeAll(section2ArenaIndex.keySet());
}

// updatedSections contains all sections than received light updates,
// but we only care about its intersection with our tracked sections.
for (long updatedSection : updatedSections) {
// Since sections contain the border light of their neighbors, we need to collect the neighbors as well.
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
for (int z = -1; z <= 1; z++) {
long section = SectionPos.offset(updatedSection, x, y, z);
if (knownSections.contains(section)) {
requestedSections.add(section);
if (section2ArenaIndex.containsKey(section)) {
sectionsToCollect.add(section);
}
}
}
}
}

for (long section : requestedSections) {
addSection(section);
// Now actually do the collection.
// TODO: Can this be done in parallel?
for (long section : sectionsToCollect) {
collectSection(section);
}

requestedSections = null;
});
}

private void removeUnusedSections(LongSet allLightSections) {
private void removeUnusedSections() {
if (requestedSections == null) {
return;
}

var entries = section2ArenaIndex.long2IntEntrySet();
var it = entries.iterator();
while (it.hasNext()) {
var entry = it.next();
var section = entry.getLongKey();

if (!allLightSections.contains(section)) {
if (!this.requestedSections.contains(section)) {
arena.free(entry.getIntValue());
needsLutRebuild = true;
it.remove();
Expand All @@ -117,7 +142,7 @@ public int capacity() {
return arena.capacity();
}

public void addSection(long section) {
public void collectSection(long section) {
var lightEngine = level.getLightEngine();

var blockLight = lightEngine.getLayerListener(LightLayer.BLOCK);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ public static LightUpdateHolder get(LevelAccessor level) {

private final LongSet updatedSections = new LongArraySet();

public LongSet getUpdatedSections() {
public LongSet getAndClearUpdatedSections() {
if (updatedSections.isEmpty()) {
return LongSet.of();
}

var out = new LongArraySet(updatedSections);
updatedSections.clear();
return out;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ public enum DebugMode implements StringRepresentable {
LIGHT_COLOR,
OVERLAY,
DIFFUSE,
LIGHT_VOLUME,
;

public static final Codec<DebugMode> CODEC = StringRepresentable.fromEnum(DebugMode::values);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ vec2 _flw_lightForDirection(in vec2[27] lights, in vec3 interpolant, in uvec3 c0
vec2 light1 = mix(light10, light11, interpolant.y);

// Divide by 60 (15 * 4) to normalize.
return mix(light0, light1, interpolant.x) / 60.;
return mix(light0, light1, interpolant.x) / 63.;
}

bool flw_light(vec3 worldPos, vec3 normal, out vec2 lightCoord) {
Expand Down Expand Up @@ -227,7 +227,8 @@ bool flw_light(vec3 worldPos, vec3 normal, out vec2 lightCoord) {
lightY = vec2(0.);
}

lightCoord = (lightX * abs(normal.x) + lightY * abs(normal.y) + lightZ * abs(normal.z));
vec3 n2 = normal * normal;
lightCoord = lightX * n2.x + lightY * n2.y + lightZ * n2.z;

return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,13 @@ private VisualizationManagerImpl(LevelAccessor level) {
.ifFalse(update)
.plan()
.then(SimplePlan.of(() -> {
// TODO: Lazily re-evaluate the union'd set
var out = new LongOpenHashSet();
out.addAll(blockEntities.lightSections());
out.addAll(entities.lightSections());
out.addAll(effects.lightSections());
engine.lightSections(out);
if (blockEntities.lightSectionsDirty() || entities.lightSectionsDirty() || effects.lightSectionsDirty()) {
var out = new LongOpenHashSet();
out.addAll(blockEntities.lightSections());
out.addAll(entities.lightSections());
out.addAll(effects.lightSections());
engine.lightSections(out);
}
}))
.then(RaisePlan.raise(frameVisualsFlag))
.then(engine.createFramePlan())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,13 @@ public Plan<TickableVisual.Context> tickPlan() {
.then(storage.tickPlan());
}

public boolean lightSectionsDirty() {
return getStorage().smoothLitStorage()
.sectionsDirty();
}

public LongSet lightSections() {
return getStorage().lightSections();
return getStorage().smoothLitStorage()
.sections();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.Map;

import org.jetbrains.annotations.Nullable;

import dev.engine_room.flywheel.api.visual.SmoothLitVisual;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
Expand All @@ -11,12 +13,19 @@
public class SmoothLitVisualStorage {
private final Map<SmoothLitVisual, SectionProperty> visuals = new Reference2ObjectOpenHashMap<>();

@Nullable
private LongSet cachedSections;

public boolean sectionsDirty() {
return cachedSections == null;
}

public LongSet sections() {
var out = new LongOpenHashSet();
cachedSections = new LongOpenHashSet();
for (SectionProperty value : visuals.values()) {
out.addAll(value.sections);
cachedSections.addAll(value.sections);
}
return out;
return cachedSections;
}

public void remove(SmoothLitVisual smoothLit) {
Expand All @@ -33,13 +42,15 @@ public void clear() {
visuals.clear();
}

private static final class SectionProperty implements SmoothLitVisual.SectionProperty {
private final class SectionProperty implements SmoothLitVisual.SectionProperty {
private final LongSet sections = new LongArraySet();

@Override
public void lightSections(LongSet sections) {
this.sections.clear();
this.sections.addAll(sections);

SmoothLitVisualStorage.this.cachedSections = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import dev.engine_room.flywheel.lib.task.PlanMap;
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
import dev.engine_room.flywheel.lib.visual.SimpleTickableVisual;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;

public abstract class Storage<T> {
Expand Down Expand Up @@ -176,7 +175,7 @@ private void setup(Visual visual) {
*/
public abstract boolean willAccept(T obj);

public LongSet lightSections() {
return smoothLitVisuals.sections();
public SmoothLitVisualStorage smoothLitStorage() {
return smoothLitVisuals;
}
}

0 comments on commit d63e592

Please sign in to comment.