/* * Decompiled with CFR 0.152. * * Could not load the following classes: * org.jspecify.annotations.Nullable */ package com.mojang.blaze3d.framegraph; import com.mojang.blaze3d.framegraph.FramePass; 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 org.jspecify.annotations.Nullable; 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 name) { Pass pass = new Pass(this.passes.size(), name); this.passes.add(pass); return pass; } public ResourceHandle importExternal(String name, T resource) { ExternalResource holder = new ExternalResource(name, null, resource); this.externalResources.add(holder); return holder.handle; } public ResourceHandle createInternal(String name, ResourceDescriptor descriptor) { return this.createInternalResource((String)name, descriptor, null).handle; } private InternalVirtualResource createInternalResource(String name, ResourceDescriptor descriptor, @Nullable Pass createdBy) { int id = this.internalResources.size(); InternalVirtualResource resource = new InternalVirtualResource(id, name, createdBy, descriptor); this.internalResources.add(resource); return resource; } public void execute(GraphicsResourceAllocator resourceAllocator) { this.execute(resourceAllocator, Inspector.NONE); } public void execute(GraphicsResourceAllocator resourceAllocator, Inspector inspector) { BitSet passesToKeep = this.identifyPassesToKeep(); ArrayList passesInOrder = new ArrayList(passesToKeep.cardinality()); BitSet visiting = new BitSet(this.passes.size()); for (Pass pass : this.passes) { this.resolvePassOrder(pass, passesToKeep, visiting, passesInOrder); } this.assignResourceLifetimes(passesInOrder); for (Pass pass : passesInOrder) { for (InternalVirtualResource resource : pass.resourcesToAcquire) { inspector.acquireResource(resource.name); resource.acquire(resourceAllocator); } inspector.beforeExecutePass(pass.name); pass.task.run(); inspector.afterExecutePass(pass.name); int id = pass.resourcesToRelease.nextSetBit(0); while (id >= 0) { InternalVirtualResource resource; resource = this.internalResources.get(id); inspector.releaseResource(resource.name); resource.release(resourceAllocator); id = pass.resourcesToRelease.nextSetBit(id + 1); } } } private BitSet identifyPassesToKeep() { ArrayDeque scratchQueue = new ArrayDeque(this.passes.size()); BitSet passesToKeep = new BitSet(this.passes.size()); for (VirtualResource virtualResource : this.externalResources) { Pass pass = virtualResource.handle.createdBy; if (pass == null) continue; this.discoverAllRequiredPasses(pass, passesToKeep, scratchQueue); } for (Pass pass : this.passes) { if (!pass.disableCulling) continue; this.discoverAllRequiredPasses(pass, passesToKeep, scratchQueue); } return passesToKeep; } private void discoverAllRequiredPasses(Pass sourcePass, BitSet visited, Deque passesToTrace) { passesToTrace.add(sourcePass); while (!passesToTrace.isEmpty()) { Pass pass = passesToTrace.poll(); if (visited.get(pass.id)) continue; visited.set(pass.id); int id = pass.requiredPassIds.nextSetBit(0); while (id >= 0) { passesToTrace.add(this.passes.get(id)); id = pass.requiredPassIds.nextSetBit(id + 1); } } } private void resolvePassOrder(Pass pass, BitSet passesToFind, BitSet visiting, List output) { if (visiting.get(pass.id)) { String involvedPasses = visiting.stream().mapToObj(id -> this.passes.get((int)id).name).collect(Collectors.joining(", ")); throw new IllegalStateException("Frame graph cycle detected between " + involvedPasses); } if (!passesToFind.get(pass.id)) { return; } visiting.set(pass.id); passesToFind.clear(pass.id); int id2 = pass.requiredPassIds.nextSetBit(0); while (id2 >= 0) { this.resolvePassOrder(this.passes.get(id2), passesToFind, visiting, output); id2 = pass.requiredPassIds.nextSetBit(id2 + 1); } for (Handle handle : pass.writesFrom) { int id3 = handle.readBy.nextSetBit(0); while (id3 >= 0) { if (id3 != pass.id) { this.resolvePassOrder(this.passes.get(id3), passesToFind, visiting, output); } id3 = handle.readBy.nextSetBit(id3 + 1); } } output.add(pass); visiting.clear(pass.id); } private void assignResourceLifetimes(Collection passesInOrder) { @Nullable Pass[] lastPassByResource = new Pass[this.internalResources.size()]; for (Pass pass : passesInOrder) { int id = pass.requiredResourceIds.nextSetBit(0); while (id >= 0) { InternalVirtualResource resource = this.internalResources.get(id); Pass lastPass = lastPassByResource[id]; lastPassByResource[id] = pass; if (lastPass == null) { pass.resourcesToAcquire.add(resource); } else { lastPass.resourcesToRelease.clear(id); } pass.resourcesToRelease.set(id); id = pass.requiredResourceIds.nextSetBit(id + 1); } } } private class Pass implements FramePass { private final int id; private final String name; private final List> writesFrom = new ArrayList(); private final BitSet requiredResourceIds = new BitSet(); private final BitSet requiredPassIds = new BitSet(); private Runnable task = () -> {}; private final List> resourcesToAcquire = new ArrayList(); private final BitSet resourcesToRelease = new BitSet(); private boolean disableCulling; public Pass(int id, String name) { this.id = id; this.name = name; } private void markResourceRequired(Handle handle) { VirtualResource virtualResource = handle.holder; if (virtualResource instanceof InternalVirtualResource) { InternalVirtualResource resource = (InternalVirtualResource)virtualResource; this.requiredResourceIds.set(resource.id); } } private void markPassRequired(Pass pass) { this.requiredPassIds.set(pass.id); } @Override public ResourceHandle createsInternal(String name, ResourceDescriptor descriptor) { InternalVirtualResource resource = FrameGraphBuilder.this.createInternalResource(name, descriptor, this); this.requiredResourceIds.set(resource.id); return resource.handle; } @Override public void reads(ResourceHandle handle) { this._reads((Handle)handle); } private void _reads(Handle handle) { this.markResourceRequired(handle); if (handle.createdBy != null) { this.markPassRequired(handle.createdBy); } handle.readBy.set(this.id); } @Override public ResourceHandle readsAndWrites(ResourceHandle handle) { return this._readsAndWrites((Handle)handle); } @Override public void requires(FramePass pass) { this.requiredPassIds.set(((Pass)pass).id); } @Override public void disableCulling() { this.disableCulling = true; } private Handle _readsAndWrites(Handle handle) { this.writesFrom.add(handle); this._reads(handle); return handle.writeAndAlias(this); } @Override public void executes(Runnable task) { this.task = task; } public String toString() { return this.name; } } private static class ExternalResource extends VirtualResource { private final T resource; public ExternalResource(String name, @Nullable Pass createdBy, T resource) { super(name, createdBy); this.resource = resource; } @Override public T get() { return this.resource; } } private static class Handle implements ResourceHandle { private final VirtualResource holder; private final int version; private final @Nullable Pass createdBy; private final BitSet readBy = new BitSet(); private @Nullable Handle aliasedBy; private Handle(VirtualResource holder, int version, @Nullable Pass createdBy) { this.holder = holder; this.version = version; this.createdBy = createdBy; } @Override public T get() { return this.holder.get(); } private Handle writeAndAlias(Pass pass) { if (this.holder.handle != this) { throw new IllegalStateException("Handle " + String.valueOf(this) + " is no longer valid, as its contents were moved into " + String.valueOf(this.aliasedBy)); } Handle newHandle = new Handle(this.holder, this.version + 1, pass); this.holder.handle = newHandle; this.aliasedBy = newHandle; return newHandle; } public String toString() { if (this.createdBy != null) { return String.valueOf(this.holder) + "#" + this.version + " (from " + String.valueOf(this.createdBy) + ")"; } return String.valueOf(this.holder) + "#" + this.version; } } private static class InternalVirtualResource extends VirtualResource { private final int id; private final ResourceDescriptor descriptor; private @Nullable T physicalResource; public InternalVirtualResource(int id, String name, @Nullable Pass createdBy, ResourceDescriptor descriptor) { super(name, createdBy); this.id = id; this.descriptor = descriptor; } @Override public T get() { return Objects.requireNonNull(this.physicalResource, "Resource is not currently available"); } public void acquire(GraphicsResourceAllocator allocator) { if (this.physicalResource != null) { throw new IllegalStateException("Tried to acquire physical resource, but it was already assigned"); } this.physicalResource = allocator.acquire(this.descriptor); } public void release(GraphicsResourceAllocator allocator) { if (this.physicalResource == null) { throw new IllegalStateException("Tried to release physical resource that was not allocated"); } allocator.release(this.descriptor, this.physicalResource); this.physicalResource = null; } } public static interface Inspector { public static final Inspector NONE = new Inspector(){}; default public void acquireResource(String name) { } default public void releaseResource(String name) { } default public void beforeExecutePass(String name) { } default public void afterExecutePass(String name) { } } private static abstract class VirtualResource { public final String name; public Handle handle; public VirtualResource(String name, @Nullable Pass createdBy) { this.name = name; this.handle = new Handle(this, 0, createdBy); } public abstract T get(); public String toString() { return this.name; } } }