I am trying to write my code with extension ARB_direct_state_access (core in 4.5) and a fall-back in pre-ARB_direct_state_access times.
So, I have the texture loading functions, which work.
To render, I must run the old code: (when I run this code, texture appears).
glActiveTexture(GL_TEXTURE0 + unit);
gl.glBindTexture(target, texture);
Or the new code: (when I run this code, texture doesn’t appear).
glBindTextureUnit(GL_TEXTURE0 + unit, texture);
What is wrong, with this? Is n’t glBindTextureUnit() enough for rendering?
I include the full code:
Texture.java
public class Texture implements Release {
public Texture() {
int a[] = new int[1];
gl.glGenTextures(1, a, 0);
texture = a[0];
}
protected Texture(int tex) { texture = tex; }
@Override public void release() { gl.glDeleteTextures(1, new int[] { texture }, 0); }
public final int texture;
/** Bind texture to texture target.
* @param target texture target like GL_TEXTURE_2D. */
public void bindToTarget(int target) { gl.glBindTexture(target, texture); }
/** Bind texture to texture unit.
* @param unit index of the texture unit. */
public void bindToUnit(int unit) { gl.glBindTextureUnit(GL_TEXTURE0 + unit, texture); }
/** Set active texture unit.
* @param unit index of the texture unit. */
static public void setActiveUnit(int unit) { gl.glActiveTexture(GL_TEXTURE0 + unit); }
static public float getFloat(int pname) {
float[] ret = new float[1];
gl.glGetFloatv(pname, ret, 0);
return ret[0];
}
static public int getInteger(int pname) {
int[] ret = new int[1];
gl.glGetIntegerv(pname, ret, 0);
return ret[0];
}
static public class Config {
/** Max supported anisotropy from device. At least 1.f */
private static float max_anisotropy = 0;
/** Get max supported anisotropy from device.
* @return max anisotropy. 1.f means no anisotropy available. */
public static float getMaxAnisotropy() {
if (max_anisotropy == 0) {
max_anisotropy = getFloat(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT);
if (max_anisotropy < 1.0f) max_anisotropy = 1.0f;
}
return max_anisotropy;
}
/** Texture anisotropy for request.
* Must be lower than getMaxAnisotropy(). No anisotropy is 1.f */
public float anisotropy = getMaxAnisotropy();
//public int magnify = GL_LINEAR; // default
//public int minify = GL_LINEAR_MIPMAP_LINEAR; // not default
//public int edgePolicy = GL_REPEAT; //default
/** Skip higher mipmap levels.
* e.g if 3, skip levels 4*4 and higher. Default 0. */
public int skip_last_levels = 0;
/** Edge policy of texture coordinate s. */
public int s_edge = GL_CLAMP_TO_EDGE;
/** Edge policy of texture coordinate t. */
public int t_edge = GL_CLAMP_TO_EDGE;
/** Edge policy of texture coordinate r. */
public int r_edge = GL_CLAMP_TO_EDGE;
/** Border color for texture, if it has border. */
public int border;
public void textureParameters(int texture) {
//default: gl.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
gl.glTextureParameterf(texture, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
gl.glTextureParameteri(texture, GL_TEXTURE_WRAP_S, s_edge);
gl.glTextureParameteri(texture, GL_TEXTURE_WRAP_T, t_edge);
gl.glTextureParameteri(texture, GL_TEXTURE_WRAP_R, r_edge);
gl.glTextureParameterfv(texture, GL_TEXTURE_BORDER_COLOR, getColorFromInt(border), 0);
}
public void texParameters(int target) {
//default: gl.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
gl.glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
gl.glTexParameteri(target, GL_TEXTURE_WRAP_S, s_edge);
gl.glTexParameteri(target, GL_TEXTURE_WRAP_T, t_edge);
gl.glTexParameteri(target, GL_TEXTURE_WRAP_R, r_edge);
gl.glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, getColorFromInt(border), 0);
}
/** Convert an integer RGBA, to a float array of 4 components.
* @param color A 4 byte integer as RGBA color, with lower byte the red.
* @return A float array with 4 color components, where 0-th element is red. */
protected static float[] getColorFromInt(int color) {
float[] c = new float[4];
for (int z = 0; z < 4; ++z, color >>= 8)
c[0] = color & 255;
return c;
}
}
/** How many levels a texture can have.
* @param i Texture image.
* @param cfg Texture loading preferences. Can be null.
* @return Number of mipmap levels for that texture. */
public static int getMipmaps(Image i, Config cfg) {
return max(1, i.getMipmaps() - cfg.skip_last_levels);
}
public static Texture loadTexture2D(InputStream is, Config cfg) throws IOException {
if (is == null) throw new IOException("InputStream parameter is null, on loadTexture2D");
if (cfg == null) cfg = new Config();
Image i = new Image(is);
if (i.width != i.height) throw new IOException("2d textures must be square");
Texture texture = new Texture();
texture.bindToTarget(GL_TEXTURE_2D); // needed to baptize texture
final int levels = getMipmaps(i, cfg);
gl.glTextureStorage2D(texture.texture, levels, i.format, i.width, i.height);
if (i.isCompressed)
for (int z = 0, sz, offs = 0; z < levels; ++z, offs += sz) {
sz = i.getMipmapSize(z);
gl.glCompressedTextureSubImage2D(texture.texture, z/*level*/,
0/*offset_x*/, 0/*offset_y*/, i.width, i.height, i.format,
sz/*image_size*/, ByteBuffer.wrap(i.raw, offs, sz));
}
else {
gl.glTextureSubImage2D(texture.texture, 0/*level*/, 0, 0/*offset_x-y*/,
i.width, i.height, i.extformat, i.type, ByteBuffer.wrap(i.raw));
gl.glGenerateTextureMipmap(texture.texture);
}
cfg.textureParameters(texture.texture);
return texture;
}
public static Texture loadTex2D(InputStream is, Config cfg) throws IOException {
if (is == null) throw new IOException("InputStream parameter is null, on loadTex2D");
if (cfg == null) cfg = new Config();
Image i = new Image(is);
if (i.width != i.height) throw new IOException("2d textures must be square");
Texture texture = new Texture();
texture.bindToTarget(GL_TEXTURE_2D);
final int levels = getMipmaps(i, cfg);
gl.glTexStorage2D(GL_TEXTURE_2D, levels, i.format, i.width, i.height);
if (i.isCompressed)
for (int z = 0, sz, offs = 0; z < levels; ++z, offs += sz) {
sz = i.getMipmapSize(z);
gl.glCompressedTexSubImage2D(GL_TEXTURE_2D, z/*level*/,
0/*offset_x*/, 0/*offset_y*/, i.width, i.height, i.format,
sz/*image_size*/, ByteBuffer.wrap(i.raw, offs, sz));
}
else {
gl.glTexSubImage2D(GL_TEXTURE_2D, 0/*level*/, 0, 0/*offset_x-y*/,
i.width, i.height, i.extformat, i.type, ByteBuffer.wrap(i.raw));
gl.glGenerateMipmap(GL_TEXTURE_2D);
}
cfg.texParameters(GL_TEXTURE_2D);
return texture;
}
public static Texture loadTextureArray2D(InputStream is, Config cfg) throws IOException {
if (is == null) throw new IOException("InputStream parameter is null, on loadTexArray2D");
if (cfg == null) cfg = new Config();
Image i = new Image(is);
if (i.width != i.height) throw new IOException("2d array textures must be square");
Texture texture = new Texture();
texture.bindToTarget(GL_TEXTURE_2D_ARRAY); // needed to baptize texture
final int levels = getMipmaps(i, cfg);
gl.glTextureStorage3D(texture.texture, levels, i.format, i.width, i.height, i.depth);
if (i.isCompressed)
for (int z = 0, sz, offs = 0; z < levels; ++z, offs += sz) {
sz = i.getMipmapSize(z);
gl.glCompressedTextureSubImage3D(texture.texture, z/*level*/,
0, 0, 0/*offset_x-y-z*/, i.width, i.height, i.depth,
i.format, sz/*image_size*/, ByteBuffer.wrap(i.raw, offs, sz));
}
else {
gl.glTextureSubImage3D(texture.texture, 0/*level*/, 0, 0, 0/*offset_x-y-z*/,
i.width, i.height, i.depth, i.extformat, i.type, ByteBuffer.wrap(i.raw));
gl.glGenerateTextureMipmap(texture.texture);
}
cfg.textureParameters(texture.texture);
return texture;
}
public static Texture loadTexArray2D(InputStream is, Config cfg) throws IOException {
if (is == null) throw new IOException("InputStream parameter is null, on loadTextureArray2D");
if (cfg == null) cfg = new Config();
Image i = new Image(is);
if (i.width != i.height) throw new IOException("2d array textures must be square");
Texture texture = new Texture();
texture.bindToTarget(GL_TEXTURE_2D_ARRAY);
final int levels = getMipmaps(i, cfg);
gl.glTexStorage3D(GL_TEXTURE_2D_ARRAY, levels, i.format, i.width, i.height, i.depth);
if (i.isCompressed)
for (int z = 0, sz, offs = 0; z < levels; ++z, offs += sz) {
sz = i.getMipmapSize(z);
gl.glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, z/*level*/,
0, 0, 0/*offset_x-y-z*/, i.width, i.height, i.depth,
i.format, sz/*image_size*/, ByteBuffer.wrap(i.raw, offs, sz));
}
else {
gl.glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0/*level*/, 0, 0, 0/*offset_x-y-z*/,
i.width, i.height, i.depth, i.extformat, i.type, ByteBuffer.wrap(i.raw));
gl.glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
}
cfg.texParameters(GL_TEXTURE_2D_ARRAY);
return texture;
}
}
TextureOld.java
public class TextureOld extends Texture {
/** Target of texture. Like GL_TEXTURE_2D. */
protected final int target;
protected TextureOld(int tex, int target) { super(tex); this.target = target; }
/** Bind texture to texture unit.
* @param unit index of the texture unit. */
@Override
public void bindToUnit(int unit) {
gl.glActiveTexture(GL_TEXTURE0 + unit);
gl.glBindTexture(target, texture);
}
public static Texture loadTexture2D(InputStream is, Config cfg) throws IOException {
return isDirectStateAccessSupported()
? Texture.loadTexture2D(is, cfg)
: new TextureOld(Texture.loadTex2D(is, cfg).texture, GL_TEXTURE_2D);
}
public static Texture loadTextureArray2D(InputStream is, Config cfg) throws IOException {
return isDirectStateAccessSupported()
? Texture.loadTextureArray2D(is, cfg)
: new TextureOld(Texture.loadTexArray2D(is, cfg).texture, GL_TEXTURE_2D_ARRAY);
}
private static int ARB_direct_state_access = -1;
public static boolean isDirectStateAccessSupported() {
if (ARB_direct_state_access == -1) {
// Check if OpenGL core version is at least 4.5
int r = getInteger(GL_MAJOR_VERSION);
if (r > 4 || r == 4 && getInteger(GL_MINOR_VERSION) >= 5)
ARB_direct_state_access = 2;
// Check if extension ARB_direct_state_access is supported
else ARB_direct_state_access = gl.glGetString(GL_EXTENSIONS).contains("ARB_direct_state_access") ? 1 : 0;
}
return ARB_direct_state_access > 0;
}
}