package openmods.stencil;
import com.google.common.collect.Sets;
import java.lang.reflect.Field;
import java.util.BitSet;
import java.util.Iterator;
import java.util.TreeSet;
import net.minecraftforge.client.MinecraftForgeClient;
import openmods.Log;
public class StencilPoolManager {
public interface StencilPool {
public StencilBitAllocation acquire();
public void release(StencilBitAllocation bit);
public int getSize();
public String getType();
}
private static final StencilPool DUMMY = new StencilPool() {
@Override
public StencilBitAllocation acquire() {
return null;
}
@Override
public void release(StencilBitAllocation bit) {
throw new IllegalStateException();
}
@Override
public int getSize() {
return 0;
}
@Override
public String getType() {
return "disabled";
}
};
private static class ForgePool implements StencilPool {
private final boolean isForced;
public ForgePool(boolean isForced) {
this.isForced = isForced;
}
@Override
public StencilBitAllocation acquire() {
int bit = MinecraftForgeClient.reserveStencilBit();
return bit != -1? new StencilBitAllocation(bit) : null;
}
@Override
public void release(StencilBitAllocation allocation) {
MinecraftForgeClient.releaseStencilBit(allocation.bit);
}
@Override
public int getSize() {
return MinecraftForgeClient.getStencilBits();
}
@Override
public String getType() {
return isForced? "forge (forced)" : "forge";
}
}
private static class InternalPool implements StencilPool {
private final TreeSet<StencilBitAllocation> bits = Sets.newTreeSet();
private final boolean isForgeHacked;
public InternalPool(boolean isForgeHacked) {
this.isForgeHacked = isForgeHacked;
for (int i = 0; i < 8; i++)
bits.add(new StencilBitAllocation(i));
}
@Override
public synchronized void release(StencilBitAllocation allocation) {
bits.add(allocation);
}
@Override
public synchronized StencilBitAllocation acquire() {
// reversed, so there is smaller chance we don't collide with hacked stencils
Iterator<StencilBitAllocation> it = bits.descendingIterator();
if (it.hasNext()) {
StencilBitAllocation result = it.next();
it.remove();
return result;
}
return null;
}
@Override
public int getSize() {
return 8;
}
@Override
public String getType() {
return isForgeHacked? "internal (hacked forge)" : "internal";
}
}
private static StencilPool pool;
public static StencilPool pool() {
if (pool == null) pool = selectPool();
return pool;
}
private static StencilPool selectPool() {
final boolean forgeHasDeclaredBits = MinecraftForgeClient.getStencilBits() > 0;
final boolean forgeHasActualBits = !isForgePoolEmpty();
if (forgeHasDeclaredBits) {
if (forgeHasActualBits) return new ForgePool(getForgeStencilFlag());
else return new InternalPool(true); // stencil is enabled, but Forge pool is always empty
}
if (FramebufferHooks.STENCIL_BUFFER_INJECTED) return new InternalPool(false);
return DUMMY;
}
private static boolean isForgePoolEmpty() {
try {
Field f = MinecraftForgeClient.class.getDeclaredField("stencilBits");
f.setAccessible(true);
BitSet pool = (BitSet)f.get(null);
return pool.isEmpty();
} catch (Exception e) {
Log.warn(e, "Failed to get field!");
}
return false;
}
private static boolean getForgeStencilFlag() {
return Boolean.parseBoolean(System.getProperty("forge.forceDisplayStencil", "false"));
}
}