2025-11-24 22:52:51 +03:00

356 lines
13 KiB
Java

/*
* 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<InternalVirtualResource<?>> internalResources = new ArrayList();
private final List<ExternalResource<?>> externalResources = new ArrayList();
private final List<Pass> passes = new ArrayList<Pass>();
public FramePass addPass(String name) {
Pass pass = new Pass(this.passes.size(), name);
this.passes.add(pass);
return pass;
}
public <T> ResourceHandle<T> importExternal(String name, T resource) {
ExternalResource<T> holder = new ExternalResource<T>(name, null, resource);
this.externalResources.add(holder);
return holder.handle;
}
public <T> ResourceHandle<T> createInternal(String name, ResourceDescriptor<T> descriptor) {
return this.createInternalResource((String)name, descriptor, null).handle;
}
private <T> InternalVirtualResource<T> createInternalResource(String name, ResourceDescriptor<T> descriptor, @Nullable Pass createdBy) {
int id = this.internalResources.size();
InternalVirtualResource<T> resource = new InternalVirtualResource<T>(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<Pass> passesInOrder = new ArrayList<Pass>(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<Pass> scratchQueue = new ArrayDeque<Pass>(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<Pass> 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<Pass> 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<Pass> 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<Handle<?>> writesFrom = new ArrayList();
private final BitSet requiredResourceIds = new BitSet();
private final BitSet requiredPassIds = new BitSet();
private Runnable task = () -> {};
private final List<InternalVirtualResource<?>> 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 <T> void markResourceRequired(Handle<T> 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 <T> ResourceHandle<T> createsInternal(String name, ResourceDescriptor<T> descriptor) {
InternalVirtualResource<T> resource = FrameGraphBuilder.this.createInternalResource(name, descriptor, this);
this.requiredResourceIds.set(resource.id);
return resource.handle;
}
@Override
public <T> void reads(ResourceHandle<T> handle) {
this._reads((Handle)handle);
}
private <T> void _reads(Handle<T> handle) {
this.markResourceRequired(handle);
if (handle.createdBy != null) {
this.markPassRequired(handle.createdBy);
}
handle.readBy.set(this.id);
}
@Override
public <T> ResourceHandle<T> readsAndWrites(ResourceHandle<T> 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 <T> Handle<T> _readsAndWrites(Handle<T> 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<T>
extends VirtualResource<T> {
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<T>
implements ResourceHandle<T> {
private final VirtualResource<T> holder;
private final int version;
private final @Nullable Pass createdBy;
private final BitSet readBy = new BitSet();
private @Nullable Handle<T> aliasedBy;
private Handle(VirtualResource<T> 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<T> 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<T> newHandle = new Handle<T>(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<T>
extends VirtualResource<T> {
private final int id;
private final ResourceDescriptor<T> descriptor;
private @Nullable T physicalResource;
public InternalVirtualResource(int id, String name, @Nullable Pass createdBy, ResourceDescriptor<T> 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<T> {
public final String name;
public Handle<T> 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;
}
}
}