package com.mojang.blaze3d.framegraph; import com.mojang.blaze3d.resource.GraphicsResourceAllocator; import com.mojang.blaze3d.resource.ResourceDescriptor; import com.mojang.blaze3d.resource.ResourceHandle; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.Deque; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import javax.annotation.Nullable; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) public class FrameGraphBuilder { private final List> internalResources = new ArrayList<>(); private final List> externalResources = new ArrayList<>(); private final List passes = new ArrayList<>(); public FramePass addPass(String p_362791_) { FrameGraphBuilder.Pass framegraphbuilder$pass = new FrameGraphBuilder.Pass(this.passes.size(), p_362791_); this.passes.add(framegraphbuilder$pass); return framegraphbuilder$pass; } public ResourceHandle importExternal(String p_363754_, T p_369377_) { FrameGraphBuilder.ExternalResource externalresource = new FrameGraphBuilder.ExternalResource<>(p_363754_, null, p_369377_); this.externalResources.add(externalresource); return externalresource.handle; } public ResourceHandle createInternal(String p_360750_, ResourceDescriptor p_363637_) { return this.createInternalResource(p_360750_, p_363637_, null).handle; } FrameGraphBuilder.InternalVirtualResource createInternalResource(String p_368654_, ResourceDescriptor p_360922_, @Nullable FrameGraphBuilder.Pass p_362192_) { int i = this.internalResources.size(); FrameGraphBuilder.InternalVirtualResource internalvirtualresource = new FrameGraphBuilder.InternalVirtualResource<>( i, p_368654_, p_362192_, p_360922_ ); this.internalResources.add(internalvirtualresource); return internalvirtualresource; } public void execute(GraphicsResourceAllocator p_368856_) { this.execute(p_368856_, FrameGraphBuilder.Inspector.NONE); } public void execute(GraphicsResourceAllocator p_367017_, FrameGraphBuilder.Inspector p_366308_) { BitSet bitset = this.identifyPassesToKeep(); List list = new ArrayList<>(bitset.cardinality()); BitSet bitset1 = new BitSet(this.passes.size()); for (FrameGraphBuilder.Pass framegraphbuilder$pass : this.passes) { this.resolvePassOrder(framegraphbuilder$pass, bitset, bitset1, list); } this.assignResourceLifetimes(list); for (FrameGraphBuilder.Pass framegraphbuilder$pass1 : list) { for (FrameGraphBuilder.InternalVirtualResource internalvirtualresource : framegraphbuilder$pass1.resourcesToAcquire) { p_366308_.acquireResource(internalvirtualresource.name); internalvirtualresource.acquire(p_367017_); } p_366308_.beforeExecutePass(framegraphbuilder$pass1.name); framegraphbuilder$pass1.task.run(); p_366308_.afterExecutePass(framegraphbuilder$pass1.name); for (int i = framegraphbuilder$pass1.resourcesToRelease.nextSetBit(0); i >= 0; i = framegraphbuilder$pass1.resourcesToRelease.nextSetBit(i + 1)) { FrameGraphBuilder.InternalVirtualResource internalvirtualresource1 = this.internalResources.get(i); p_366308_.releaseResource(internalvirtualresource1.name); internalvirtualresource1.release(p_367017_); } } } private BitSet identifyPassesToKeep() { Deque deque = new ArrayDeque<>(this.passes.size()); BitSet bitset = new BitSet(this.passes.size()); for (FrameGraphBuilder.VirtualResource virtualresource : this.externalResources) { FrameGraphBuilder.Pass framegraphbuilder$pass = virtualresource.handle.createdBy; if (framegraphbuilder$pass != null) { this.discoverAllRequiredPasses(framegraphbuilder$pass, bitset, deque); } } for (FrameGraphBuilder.Pass framegraphbuilder$pass1 : this.passes) { if (framegraphbuilder$pass1.disableCulling) { this.discoverAllRequiredPasses(framegraphbuilder$pass1, bitset, deque); } } return bitset; } private void discoverAllRequiredPasses(FrameGraphBuilder.Pass p_362979_, BitSet p_368956_, Deque p_369416_) { p_369416_.add(p_362979_); while (!p_369416_.isEmpty()) { FrameGraphBuilder.Pass framegraphbuilder$pass = p_369416_.poll(); if (!p_368956_.get(framegraphbuilder$pass.id)) { p_368956_.set(framegraphbuilder$pass.id); for (int i = framegraphbuilder$pass.requiredPassIds.nextSetBit(0); i >= 0; i = framegraphbuilder$pass.requiredPassIds.nextSetBit(i + 1)) { p_369416_.add(this.passes.get(i)); } } } } private void resolvePassOrder(FrameGraphBuilder.Pass p_364839_, BitSet p_370123_, BitSet p_365587_, List p_366834_) { if (p_365587_.get(p_364839_.id)) { String s = p_365587_.stream().mapToObj(p_368248_ -> this.passes.get(p_368248_).name).collect(Collectors.joining(", ")); throw new IllegalStateException("Frame graph cycle detected between " + s); } else if (p_370123_.get(p_364839_.id)) { p_365587_.set(p_364839_.id); p_370123_.clear(p_364839_.id); for (int i = p_364839_.requiredPassIds.nextSetBit(0); i >= 0; i = p_364839_.requiredPassIds.nextSetBit(i + 1)) { this.resolvePassOrder(this.passes.get(i), p_370123_, p_365587_, p_366834_); } for (FrameGraphBuilder.Handle handle : p_364839_.writesFrom) { for (int j = handle.readBy.nextSetBit(0); j >= 0; j = handle.readBy.nextSetBit(j + 1)) { if (j != p_364839_.id) { this.resolvePassOrder(this.passes.get(j), p_370123_, p_365587_, p_366834_); } } } p_366834_.add(p_364839_); p_365587_.clear(p_364839_.id); } } private void assignResourceLifetimes(Collection p_366752_) { FrameGraphBuilder.Pass[] aframegraphbuilder$pass = new FrameGraphBuilder.Pass[this.internalResources.size()]; for (FrameGraphBuilder.Pass framegraphbuilder$pass : p_366752_) { for (int i = framegraphbuilder$pass.requiredResourceIds.nextSetBit(0); i >= 0; i = framegraphbuilder$pass.requiredResourceIds.nextSetBit(i + 1)) { FrameGraphBuilder.InternalVirtualResource internalvirtualresource = this.internalResources.get(i); FrameGraphBuilder.Pass framegraphbuilder$pass1 = aframegraphbuilder$pass[i]; aframegraphbuilder$pass[i] = framegraphbuilder$pass; if (framegraphbuilder$pass1 == null) { framegraphbuilder$pass.resourcesToAcquire.add(internalvirtualresource); } else { framegraphbuilder$pass1.resourcesToRelease.clear(i); } framegraphbuilder$pass.resourcesToRelease.set(i); } } } @OnlyIn(Dist.CLIENT) static class ExternalResource extends FrameGraphBuilder.VirtualResource { private final T resource; public ExternalResource(String p_361702_, @Nullable FrameGraphBuilder.Pass p_362591_, T p_364944_) { super(p_361702_, p_362591_); this.resource = p_364944_; } @Override public T get() { return this.resource; } } @OnlyIn(Dist.CLIENT) static class Handle implements ResourceHandle { final FrameGraphBuilder.VirtualResource holder; private final int version; @Nullable final FrameGraphBuilder.Pass createdBy; final BitSet readBy = new BitSet(); @Nullable private FrameGraphBuilder.Handle aliasedBy; Handle(FrameGraphBuilder.VirtualResource p_368119_, int p_368761_, @Nullable FrameGraphBuilder.Pass p_368110_) { this.holder = p_368119_; this.version = p_368761_; this.createdBy = p_368110_; } @Override public T get() { return this.holder.get(); } FrameGraphBuilder.Handle writeAndAlias(FrameGraphBuilder.Pass p_368239_) { if (this.holder.handle != this) { throw new IllegalStateException("Handle " + this + " is no longer valid, as its contents were moved into " + this.aliasedBy); } else { FrameGraphBuilder.Handle handle = new FrameGraphBuilder.Handle<>(this.holder, this.version + 1, p_368239_); this.holder.handle = handle; this.aliasedBy = handle; return handle; } } @Override public String toString() { return this.createdBy != null ? this.holder + "#" + this.version + " (from " + this.createdBy + ")" : this.holder + "#" + this.version; } } @OnlyIn(Dist.CLIENT) public interface Inspector { FrameGraphBuilder.Inspector NONE = new FrameGraphBuilder.Inspector() { }; default void acquireResource(String p_362031_) { } default void releaseResource(String p_370224_) { } default void beforeExecutePass(String p_363319_) { } default void afterExecutePass(String p_368799_) { } } @OnlyIn(Dist.CLIENT) static class InternalVirtualResource extends FrameGraphBuilder.VirtualResource { final int id; private final ResourceDescriptor descriptor; @Nullable private T physicalResource; public InternalVirtualResource(int p_369414_, String p_364574_, @Nullable FrameGraphBuilder.Pass p_361395_, ResourceDescriptor p_364455_) { super(p_364574_, p_361395_); this.id = p_369414_; this.descriptor = p_364455_; } @Override public T get() { return Objects.requireNonNull(this.physicalResource, "Resource is not currently available"); } public void acquire(GraphicsResourceAllocator p_367587_) { if (this.physicalResource != null) { throw new IllegalStateException("Tried to acquire physical resource, but it was already assigned"); } else { this.physicalResource = p_367587_.acquire(this.descriptor); } } public void release(GraphicsResourceAllocator p_363217_) { if (this.physicalResource == null) { throw new IllegalStateException("Tried to release physical resource that was not allocated"); } else { p_363217_.release(this.descriptor, this.physicalResource); this.physicalResource = null; } } } @OnlyIn(Dist.CLIENT) class Pass implements FramePass { final int id; final String name; final List> writesFrom = new ArrayList<>(); final BitSet requiredResourceIds = new BitSet(); final BitSet requiredPassIds = new BitSet(); Runnable task = () -> { }; final List> resourcesToAcquire = new ArrayList<>(); final BitSet resourcesToRelease = new BitSet(); boolean disableCulling; public Pass(final int p_367383_, final String p_361566_) { this.id = p_367383_; this.name = p_361566_; } private void markResourceRequired(FrameGraphBuilder.Handle p_364152_) { if (p_364152_.holder instanceof FrameGraphBuilder.InternalVirtualResource internalvirtualresource) { this.requiredResourceIds.set(internalvirtualresource.id); } } private void markPassRequired(FrameGraphBuilder.Pass p_365988_) { this.requiredPassIds.set(p_365988_.id); } @Override public ResourceHandle createsInternal(String p_362510_, ResourceDescriptor p_365149_) { FrameGraphBuilder.InternalVirtualResource internalvirtualresource = FrameGraphBuilder.this.createInternalResource(p_362510_, p_365149_, this); this.requiredResourceIds.set(internalvirtualresource.id); return internalvirtualresource.handle; } @Override public void reads(ResourceHandle p_366230_) { this._reads((FrameGraphBuilder.Handle)p_366230_); } private void _reads(FrameGraphBuilder.Handle p_362181_) { this.markResourceRequired(p_362181_); if (p_362181_.createdBy != null) { this.markPassRequired(p_362181_.createdBy); } p_362181_.readBy.set(this.id); } @Override public ResourceHandle readsAndWrites(ResourceHandle p_366708_) { return this._readsAndWrites((FrameGraphBuilder.Handle)p_366708_); } @Override public void requires(FramePass p_367159_) { this.requiredPassIds.set(((FrameGraphBuilder.Pass)p_367159_).id); } @Override public void disableCulling() { this.disableCulling = true; } private FrameGraphBuilder.Handle _readsAndWrites(FrameGraphBuilder.Handle p_361681_) { this.writesFrom.add(p_361681_); this._reads(p_361681_); return p_361681_.writeAndAlias(this); } @Override public void executes(Runnable p_366784_) { this.task = p_366784_; } @Override public String toString() { return this.name; } } @OnlyIn(Dist.CLIENT) abstract static class VirtualResource { public final String name; public FrameGraphBuilder.Handle handle; public VirtualResource(String p_364878_, @Nullable FrameGraphBuilder.Pass p_363310_) { this.name = p_364878_; this.handle = new FrameGraphBuilder.Handle<>(this, 0, p_363310_); } public abstract T get(); @Override public String toString() { return this.name; } } }