Minimalist Complete Texture

So I know in order for a texture to be complete, it needs a mipmap defined. Or so I know.

To load PNG files, I use TWL class.

	public static TextureData loadTexture(String location) throws IOException {
		final int tid = glGenTextures();
		final FileInputStream fis = new FileInputStream(location);
		final PNGDecoder decoder = new PNGDecoder(fis);
		final int width = decoder.getWidth(), height = decoder.getHeight();
		final ByteBuffer bb = BufferUtils.createByteBuffer(width * height * 4);
		decoder.decode(bb, decoder.getWidth() * 4, PNGDecoder.Format.RGBA);
		bb.flip();
		fis.close();
		glBindTexture(GL_TEXTURE_2D, tid);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
		//TODO: Add min and max mipmap stuff for finished texture
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bb);
		glBindTexture(GL_TEXTURE_2D, 0);
		final TextureData t = new TextureData(tid, location, width, height);
		Textures.add(t);
		return t;
	}

I am not asking for code, but I am asking for code. What do I put at the //TODO: to make this a complete texture?

Alongside of that, the original intent of this post was to ask why make a complete texture and is it necessary or beneficial.

Why make a complete texture? Well, you can only use a texture if it’s complete. If it’s an incomplete texture, the texture won’t work and you can’t use it.

You have already set up texture filtering with the glTexParameteri function, and you set the mipmap level to 0 in the glTexImage2D function. Your texture is complete, don’t worry :smiley:

A texture is complete if all used mipmaps are defined.

  • If your minification filtering doesn’t include mipmaps, you only need to define the first level.
  • If your minification filtering DOES include mipmaps, all mipmaps from GL_TEXTURE_BASE_LEVEL to GL_TEXTURE_MAX_LEVEL (inclusive, these are settable with glTexParameteri()). If BASE=0 and MAX=0, then you only need to set level 0 for example.
  • If you create your texture with the glTexStorage*D(), all mipmaps will be allocated immediately and the BASE and MAX levels set automaitcally, so the texture is technically complete (but still filled with garbage/uninitialized memory).

so to be sure. if not using glTexStorage, create texture with

GL11.glTexParameteri(bindTarget,GL12.GL_TEXTURE_BASE_LEVEL,baseLevel);
GL11.glTexParameteri(bindTarget,GL12.GL_TEXTURE_MAX_LEVEL,maxLevel);

and create mipmaps with [icode]EXTDirectStateAccess.glGenerateTextureMipmapEXT(id,target)[/icode] or [icode]GL30.glGenerateMipmap(target)[/icode] to allocate memory (even with garbage if base_level is undefined) to avoid out-of-memory errors mid-air later on. like using a ton of g-buffers with mipmaps.

btw :

[icode]GL_TEXTURE_MAX_LEVEL = 1 + floor(log2(max(width,height,depth)))[/icode]

and

[icode]width_at(mip_level) = max(1,floor(width/pow(2,mip_level)))[/icode]
[icode]height_at(mip_level) = max(1,floor(height/pow(2,mip_level)))[/icode] and so on.

o/

Okay so from what I compiled from these brilliant replies (thanks guys), I understand that this code goes without any garbage data/uninit data (because from what I comprehended, 1000 images will be created??).


		glBindTexture(GL_TEXTURE_2D, tid);
		glTexParameteri(GL_TEXTURE_2D, GL12.GL_TEXTURE_BASE_LEVEL, 0);
		glTexParameteri(GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 0);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bb);
		glBindTexture(GL_TEXTURE_2D, 0);

Because I set the mipmap level to 0 base and max.

If I ever support mipmapping, I should a function to directly alter the mipmaps.
As Basil_ said, [quote]EXTDirectStateAccess.glGenerateTextureMipmapEXT(id,target) or GL30.glGenerateMipmap(target)
[/quote]
I think using EXT and ARB is kindof gross looking, but idk. I am not going to bias it before I understand it fully, which this isn’t the topic to learn about it right now.

But Basil_, what did you mean by:

[quote]GL_TEXTURE_MAX_LEVEL = 1 + floor(log2(max(width,height,depth)))

and

width_at(mip_level) = max(1,floor(width/pow(2,mip_level)))
height_at(mip_level) = max(1,floor(height/pow(2,mip_level)))

and so on.
[/quote]

width, height, depth are texture properties. these methods compute the max-mip-level possible with a texture and compute the dimension at a specific mip level - in case you need them while fiddling with textures.

that EXT is DSA, if you find any method that is DSA but not EXT you’re better of using that one but there are not that many around.

Right, right it was a little ambiguous.

So I assume that what I wrote did what I thought.

Thanks for everything, basil_, ShadedVertex, and theagentd!

Some advanced tips:

  • glGenerateMipmaps() is sRGB correct. It automatically does sRGB correct downsampling (sRGB --> linear, downsample, linear --> sRGB) if the format is an sRGB format.
  • Do not use glGenerateMipmaps() in real-time running code as it doesn’t have optimal performance. Use a simple texture read shader or glBlitFramebuffer() to downsample a texture. It also breaks SLI completely if used in the rendering loop as it forces synchronization between GPUs.
  • Always precompress compressed textures and load the compressed data directly. Although driver support for compressing textures is mandatory, the quality and performance varies a lot. Intel’s driver takes ages to do BPTC (~2 minutes to compress our skybox).
  • The driver always seem to allocate all mipmaps up to the mipmap level allocated, regardless of BASE_LEVEL. Example: If you allocate mipmap level 1 first, 0 will also be (invisibly) allocated and use up texture memory. I once tried to reduce memory usage by not loading mipmap 0 and setting BASE_LEVEL to 1, but memory usage in GPU-Z was the same as if I had loaded mipmap level 1 too. Worst texture quality setting ever. It’s therefore almost never useful to modify BASE_LEVEL unless it is to temporarily limit texture sampling to certain mipmaps.
  • AMD’s driver is unreliable when it comes to texture loading. I strongly recommend using glTexStorage() instead of glTexImage() if it’s available. If not, use glTexImage() with a null buffer to allocate all mipmap levels before you start uploading them. I’ve encountered numerous driver crashes and problems by doing it any other way. Mostly true if you’re doing texture streaming with a separate context and/or using compressed textures.

I can’t find anything online that documents what you were trying to say about glTexStorage and from what I read it is also used with glTexSubImage or whatever. Could you please give an example.

Also, I target OpenGL 3,2 currently. As I read, it’s a OpenGL 4,4 feature. I am not sure though.

glTexStorage() allocates the memory for all mipmaps (= calling glTexImage(…, null) for all mipmaps) and makes the texture immutable, so you can’t change the size or allocate more mipmaps after that (you can’t call glTexStorage() or glTexImage() again on that texture). Hence, the texture has to be complete at this point, since if it wasn’t you would be forced to delete it and start over. The contents of the texture mipmaps still aren’t “initialized”, as in we haven’t specified the colors of each pixel yet, which must be done with glTexSubImage(), which modifies an already allocated texture.

There’s a difference between allocating memory and updating the contents of allocated memory. glTexStorage() only allocates memory without updating it (and prevents you from reallocating it), glTexSubImage() only updates memory without being able to allocate new memory, and glTexImage() both updates and allocates/reallocates memory.

And yes, it is an OpenGL 4.4 feature, but you can always use a 3.2 context and use glTexStorage() is the extension for it is supported, falling back to a glTexImage() loop if it’s not supported. Like I said, glTexStorage() seems a lot more solid for the AMD driver.

glTexStorage() allocates immutable memory
glTexSubImage() writes to allocated memory
glTexImage() allocates mutable memory and writes to it, but no mipmap support

Thats what I got out of this.

How do I check if the extension is supported?

It allocates ONE mipmap level per call, so it does have mipmap support. You just have to call it once per mipmap to manually allocate them.

I guess glGenerateMipmaps() also qualifies for this list:

glGenerateMipmaps() allocates (or reallocates) all mipmaps from BASE_LEVEL+1 to MAX_LEVEL, and replaces their contents with repeatedly downsampled data from BASE_LEVEL.

Hmm… something popped up a bit ago that had me questioning.

I recently was adding multitexture support for… entities in general…

	public void bindTexture(int target, int location, int unit, int tid) {
		GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit);
		GL11.glBindTexture(target, tid);
		bindUniformi(location, unit);
	}

So as you see, I select the active texture unit and bind a texture. I then call bindUniformi(location, unit)… however… that just seems wrong. Would that be mipmap level? This is the part where I bind to a sampler2D.

shader.bindTexture(GL11.GL_TEXTURE_2D, u_texture[i], i, material.getTextureData().getId());

u_texture[i] refers to a getUniformLocation() call. I have a table of these locations in the shader. Would the ‘unit’ part of bindUniformi(location, unit) be correct, or is this mipmap?

	public void bindUniformi(int location, int... uni) {
		switch(uni.length) {
		case 1:
			GL20.glUniform1i(location, uni[0]);
			break;
		case 2:
			GL20.glUniform2i(location, uni[0], uni[1]);
			break;
		case 3:
			GL20.glUniform3i(location, uni[0], uni[1], uni[2]);
			break;
		case 4:
			GL20.glUniform4i(location, uni[0], uni[1], uni[2], uni[3]);
			break;
		}
	}

I’ve used this thread for a lot of reference recently.

I wanted to add that my game engine sports this complete texture business.

Also, it appears that I support opengl 2.0 :point:

The best solution is this: Right after creating your shader, you bind it and set the texture units of each sampler.


//set up shaders with glAttachShader(program, ...)
glLinkProgram(program);

glUseProgram(program);
glUniform1i(glGetUniformLocation(program, "firstSampler"), 0);
glUniform1i(glGetUniformLocation(program, "secondSampler"), 1);
glUniform1i(glGetUniformLocation(program, "thirdSampler"), 2);
//etc

These uniform values will then be permanently stored in the program and persists when switching shaders (each program has its own uniform values; uniform values are not global state). Hence, you won’t ever have to worry about updating the samplers again. Just bind the first texture to texture unit 0 to make it readable by firstSampler, to unit 1 to make it readable by secondSampler, etc.

that one always bites me when doing live-coding. “re-linking” requires the uniforms to be set again.

subroutines are not stored that way btw. they need to be set every time the program is used.

I have a new model in mind, which accepts a number of units to use, which binds all necessary samplers. There is then the portion which actually sets the data to these locations. These are called initially from a list of texture handles. Any post will be done over.