1 package com.badlogic.gdx.graphics.g3d.utils;
3 import java.nio.IntBuffer;
5 import com.badlogic.gdx.Gdx;
6 import com.badlogic.gdx.graphics.GL10;
7 import com.badlogic.gdx.graphics.GL20;
8 import com.badlogic.gdx.graphics.Texture;
9 import com.badlogic.gdx.utils.BufferUtils;
10 import com.badlogic.gdx.utils.GdxRuntimeException;
12 /** Class that you assign a range of texture units and binds textures for you within that range.
13 * It does some basic usage tracking to avoid unnessecary bind calls.
15 public final class DefaultTextureBinder implements TextureBinder {
16 public final static int ROUNDROBIN = 0;
17 public final static int WEIGHTED = 1;
18 /** GLES only supports up to 32 textures */
19 public final static int MAX_GLES_UNITS = 32;
20 /** The index of the first exclusive texture unit */
21 private final int offset;
22 /** The amount of exclusive textures that may be used */
23 private final int count;
24 /** The weight added to a texture when its reused */
25 private final int reuseWeight;
26 /** The textures currently exclusive bound */
27 private final TextureDescriptor[] textures;
28 /** The weight (reuseWeight * reused - discarded) of the textures */
29 private final int[] weights;
30 /** The method of binding to use */
31 private final int method;
32 /** Flag to indicate the current texture is reused */
33 private boolean reused;
35 private int reuseCount = 0; // TODO remove debug code
36 private int bindCount = 0; // TODO remove debug code
38 /** Uses all available texture units and reuse weight of 3 */
39 public DefaultTextureBinder(final int method) {
43 /** Uses all remaining texture units and reuse weight of 3 */
44 public DefaultTextureBinder(final int method, final int offset) {
45 this(method, offset, -1);
48 /** Uses reuse weight of 10 */
49 public DefaultTextureBinder(final int method, final int offset, final int count) {
50 this(method, offset, count, 10);
53 public DefaultTextureBinder(final int method, final int offset, int count, final int reuseWeight) {
54 final int max = Math.min(getMaxTextureUnits(), MAX_GLES_UNITS);
57 if (offset < 0 || count < 0 || (offset + count) > max || reuseWeight < 1)
58 throw new GdxRuntimeException("Illegal arguments");
62 this.textures = new TextureDescriptor[count];
63 for (int i = 0; i < count; i++)
64 this.textures[i] = new TextureDescriptor();
65 this.reuseWeight = reuseWeight;
66 this.weights = (method == WEIGHTED) ? new int[count] : null;
69 private static int getMaxTextureUnits () {
70 IntBuffer buffer = BufferUtils.newIntBuffer(16);
71 if (Gdx.graphics.isGL20Available())
72 Gdx.gl.glGetIntegerv(GL20.GL_MAX_TEXTURE_IMAGE_UNITS, buffer);
74 Gdx.gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_UNITS, buffer);
79 public void begin () {
80 for(int i = 0; i < count; i++) {
82 if(weights != null) weights[i] = 0;
88 for(int i = 0; i < count; i++) {
89 if (textures[i].texture != null) {
90 Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0 + offset + i);
91 Gdx.gl.glBindTexture(GL20.GL_TEXTURE_2D, 0);
92 textures[i].texture = null;
95 Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0);
98 /** Binds the texture if needed and sets it active, returns the unit */
100 public final int bind(final TextureDescriptor textureDesc) {
101 return bindTexture(textureDesc, false);
104 private final int bindTexture(final TextureDescriptor textureDesc, final boolean rebind) {
109 case ROUNDROBIN: result = offset + (idx = bindTextureRoundRobin(textureDesc.texture)); break;
110 case WEIGHTED: result = offset + (idx = bindTextureWeighted(textureDesc.texture)); break;
117 textureDesc.texture.bind(result);
119 Gdx.gl.glActiveTexture(GL10.GL_TEXTURE0 + result);
122 if (textureDesc.minFilter != GL10.GL_INVALID_VALUE && textureDesc.minFilter != textures[idx].minFilter)
123 Gdx.gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, textures[idx].minFilter = textureDesc.minFilter);
124 if (textureDesc.magFilter != GL10.GL_INVALID_VALUE && textureDesc.magFilter != textures[idx].magFilter)
125 Gdx.gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, textures[idx].magFilter = textureDesc.magFilter);
126 if (textureDesc.uWrap != GL10.GL_INVALID_VALUE && textureDesc.uWrap != textures[idx].uWrap)
127 Gdx.gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, textures[idx].uWrap = textureDesc.uWrap);
128 if (textureDesc.vWrap != GL10.GL_INVALID_VALUE && textureDesc.vWrap != textures[idx].vWrap)
129 Gdx.gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, textures[idx].vWrap = textureDesc.vWrap);
133 private int currentTexture = 0;
134 private final int bindTextureRoundRobin(final Texture texture) {
135 for (int i = 0; i < count; i++) {
136 final int idx = (currentTexture + i) % count;
137 if (textures[idx].texture == texture) {
142 currentTexture = (currentTexture + 1) % count;
143 textures[currentTexture].texture = texture;
144 texture.bind(offset + currentTexture);
145 return currentTexture;
148 private final int bindTextureWeighted(final Texture texture) {
150 int weight = weights[0];
152 for (int i = 0; i < count; i++) {
153 if (textures[i].texture == texture) {
155 weights[i]+=reuseWeight;
156 } else if (weights[i] < 0 || --weights[i] < weight) {
162 textures[windex].texture = texture;
163 weights[windex] = 100;
164 texture.bind(offset + (result = windex));
171 public final int getBindCount() { return bindCount; }
174 public final int getReuseCount() { return reuseCount; }
177 public final void resetCounts() { bindCount = reuseCount = 0; }