package com.mojang.blaze3d.resource; import com.google.common.annotations.VisibleForTesting; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; import java.util.Iterator; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @Environment(EnvType.CLIENT) public class CrossFrameResourcePool implements GraphicsResourceAllocator, AutoCloseable { private final int framesToKeepResource; private final Deque> pool = new ArrayDeque(); public CrossFrameResourcePool(final int framesToKeepResource) { this.framesToKeepResource = framesToKeepResource; } public void endFrame() { Iterator> iterator = this.pool.iterator(); while (iterator.hasNext()) { CrossFrameResourcePool.ResourceEntry entry = (CrossFrameResourcePool.ResourceEntry)iterator.next(); if (entry.framesToLive-- == 0) { entry.close(); iterator.remove(); } } } @Override public T acquire(final ResourceDescriptor descriptor) { T resource = this.acquireWithoutPreparing(descriptor); descriptor.prepare(resource); return resource; } private T acquireWithoutPreparing(final ResourceDescriptor descriptor) { Iterator> iterator = this.pool.iterator(); while (iterator.hasNext()) { CrossFrameResourcePool.ResourceEntry entry = (CrossFrameResourcePool.ResourceEntry)iterator.next(); if (descriptor.canUsePhysicalResource(entry.descriptor)) { iterator.remove(); return (T)entry.value; } } return descriptor.allocate(); } @Override public void release(final ResourceDescriptor descriptor, final T resource) { this.pool.addFirst(new CrossFrameResourcePool.ResourceEntry<>(descriptor, resource, this.framesToKeepResource)); } public void clear() { this.pool.forEach(CrossFrameResourcePool.ResourceEntry::close); this.pool.clear(); } public void close() { this.clear(); } @VisibleForTesting protected Collection> entries() { return this.pool; } @Environment(EnvType.CLIENT) @VisibleForTesting protected static final class ResourceEntry implements AutoCloseable { private final ResourceDescriptor descriptor; private final T value; private int framesToLive; private ResourceEntry(final ResourceDescriptor descriptor, final T value, final int framesToLive) { this.descriptor = descriptor; this.value = value; this.framesToLive = framesToLive; } public void close() { this.descriptor.free(this.value); } } }