package loon.opengl.d3d; import java.nio.IntBuffer; import loon.LSystem; import loon.LTexture; import loon.opengl.GL20; import loon.opengl.d3d.materials.TextureDescriptor; public final class DefaultTextureBinder implements TextureBinder { public final static int ROUNDROBIN = 0; public final static int WEIGHTED = 1; public final static int MAX_GLES_UNITS = 32; private final int offset; private final int count; private final int reuseWeight; private final TextureDescriptor[] textures; private final int[] weights; private final int method; private boolean reused; private int reuseCount = 0; private int bindCount = 0; public DefaultTextureBinder(final int method) { this(method, 0); } public DefaultTextureBinder(final int method, final int offset) { this(method, offset, Math.min(getMaxTextureUnits(), MAX_GLES_UNITS) - offset); } public DefaultTextureBinder(final int method, final int offset, final int count) { this(method, offset, count, 10); } public DefaultTextureBinder(final int method, final int offset, final int count, final int reuseWeight) { final int max = Math.min(getMaxTextureUnits(), MAX_GLES_UNITS); if (offset < 0 || count < 0 || (offset + count) > max || reuseWeight < 1) throw new RuntimeException("Illegal arguments"); this.method = method; this.offset = offset; this.count = count; this.textures = new TextureDescriptor[count]; for (int i = 0; i < count; i++) this.textures[i] = new TextureDescriptor(); this.reuseWeight = reuseWeight; this.weights = (method == WEIGHTED) ? new int[count] : null; } private static int getMaxTextureUnits() { IntBuffer buffer = LSystem.base().support().newIntBuffer(16); LSystem.base().graphics().gl.glGetIntegerv( GL20.GL_MAX_TEXTURE_IMAGE_UNITS, buffer); return buffer.get(0); } @Override public void begin() { for (int i = 0; i < count; i++) { textures[i].reset(); if (weights != null) weights[i] = 0; } } @Override public void end() { for (int i = 0; i < count; i++) { if (textures[i].texture != null) { LSystem.base().graphics().gl.glActiveTexture(GL20.GL_TEXTURE0 + offset + i); LSystem.base().graphics().gl.glBindTexture(GL20.GL_TEXTURE_2D, 0); textures[i].texture = null; } } LSystem.base().graphics().gl.glActiveTexture(GL20.GL_TEXTURE0); } /** Binds the texture if needed and sets it active, returns the unit */ @Override public final int bind(final TextureDescriptor textureDesc) { return bindTexture(textureDesc, false); } private final int bindTexture(final TextureDescriptor textureDesc, final boolean rebind) { int idx, result; reused = false; switch (method) { case ROUNDROBIN: result = offset + (idx = bindTextureRoundRobin(textureDesc.texture)); break; case WEIGHTED: result = offset + (idx = bindTextureWeighted(textureDesc.texture)); break; default: return -1; } if (reused) { reuseCount++; if (rebind) textureDesc.texture.bind(result); else LSystem.base().graphics().gl.glActiveTexture(GL20.GL_TEXTURE0 + result); } else bindCount++; if (textureDesc.minFilter != GL20.GL_INVALID_VALUE && textureDesc.minFilter != textures[idx].minFilter) LSystem.base().graphics().gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, textures[idx].minFilter = textureDesc.minFilter); if (textureDesc.magFilter != GL20.GL_INVALID_VALUE && textureDesc.magFilter != textures[idx].magFilter) LSystem.base().graphics().gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, textures[idx].magFilter = textureDesc.magFilter); return result; } private int currentTexture = 0; private final int bindTextureRoundRobin(final LTexture texture) { for (int i = 0; i < count; i++) { final int idx = (currentTexture + i) % count; if (textures[idx].texture == texture) { reused = true; return idx; } } currentTexture = (currentTexture + 1) % count; textures[currentTexture].texture = texture; texture.bind(offset + currentTexture); return currentTexture; } private final int bindTextureWeighted(final LTexture texture) { int result = -1; int weight = weights[0]; int windex = 0; for (int i = 0; i < count; i++) { if (textures[i].texture == texture) { result = i; weights[i] += reuseWeight; } else if (weights[i] < 0 || --weights[i] < weight) { weight = weights[i]; windex = i; } } if (result < 0) { textures[windex].texture = texture; weights[windex] = 100; texture.bind(offset + (result = windex)); } else reused = true; return result; } @Override public final int getBindCount() { return bindCount; } @Override public final int getReuseCount() { return reuseCount; } @Override public final void resetCounts() { bindCount = reuseCount = 0; } }