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, getMaxTextureUnits() - offset);
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, final int count, final int reuseWeight) {
54 final int max = Math.min(getMaxTextureUnits(), MAX_GLES_UNITS - offset);
55 if (offset < 0 || count < 0 || (offset + count) > max || reuseWeight < 1)
56 throw new GdxRuntimeException("Illegal arguments");
60 this.textures = new TextureDescriptor[count];
61 for (int i = 0; i < count; i++)
62 this.textures[i] = new TextureDescriptor();
63 this.reuseWeight = reuseWeight;
64 this.weights = (method == WEIGHTED) ? new int[count] : null;
67 private static int getMaxTextureUnits () {
68 IntBuffer buffer = BufferUtils.newIntBuffer(16);
69 if (Gdx.graphics.isGL20Available())
70 Gdx.gl.glGetIntegerv(GL20.GL_MAX_TEXTURE_IMAGE_UNITS, buffer);
72 Gdx.gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_UNITS, buffer);
77 public void begin () {
78 for(int i = 0; i < count; i++) {
79 textures[i].texture = null;
80 if(weights != null) weights[i] = 0;
86 for(int i = 0; i < count; i++) {
87 if (textures[i].texture != null) {
88 Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0 + i);
89 Gdx.gl.glBindTexture(GL20.GL_TEXTURE_2D, 0);
90 textures[i].texture = null;
93 Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0);
96 /** Binds the texture if needed and sets it active, returns the unit */
98 public final int bind(final TextureDescriptor textureDesc) {
99 return bindTexture(textureDesc, false);
102 private final int bindTexture(final TextureDescriptor textureDesc, final boolean rebind) {
107 case ROUNDROBIN: result = offset + (idx = bindTextureRoundRobin(textureDesc.texture)); break;
108 case WEIGHTED: result = offset + (idx = bindTextureWeighted(textureDesc.texture)); break;
115 textureDesc.texture.bind(result);
117 Gdx.gl.glActiveTexture(GL10.GL_TEXTURE0 + result);
120 if (textureDesc.minFilter != GL10.GL_INVALID_VALUE && textureDesc.minFilter != textures[idx].minFilter)
121 Gdx.gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, textures[idx].minFilter = textureDesc.minFilter);
122 if (textureDesc.magFilter != GL10.GL_INVALID_VALUE && textureDesc.magFilter != textures[idx].magFilter)
123 Gdx.gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, textures[idx].magFilter = textureDesc.magFilter);
124 if (textureDesc.uWrap != GL10.GL_INVALID_VALUE && textureDesc.uWrap != textures[idx].uWrap)
125 Gdx.gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, textures[idx].uWrap = textureDesc.uWrap);
126 if (textureDesc.vWrap != GL10.GL_INVALID_VALUE && textureDesc.vWrap != textures[idx].vWrap)
127 Gdx.gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, textures[idx].vWrap = textureDesc.vWrap);
131 private int currentTexture = 0;
132 private final int bindTextureRoundRobin(final Texture texture) {
133 for (int i = 0; i < count; i++) {
134 final int idx = (currentTexture + i) % count;
135 if (textures[idx].texture == texture) {
140 currentTexture = (currentTexture + 1) % count;
141 textures[currentTexture].texture = texture;
142 texture.bind(offset + currentTexture);
143 return currentTexture;
146 private final int bindTextureWeighted(final Texture texture) {
148 int weight = weights[0];
150 for (int i = 0; i < count; i++) {
151 if (textures[i].texture == texture) {
153 weights[i]+=reuseWeight;
154 } else if (weights[i] < 0 || --weights[i] < weight) {
160 textures[windex].texture = texture;
161 weights[windex] = 100;
162 texture.bind(offset + (result = windex));
169 public final int getBindCount() { return bindCount; }
172 public final int getReuseCount() { return reuseCount; }
175 public final void resetCounts() { bindCount = reuseCount = 0; }