PBO for streaming textures

Anyone here done it?



	TextureLoader texture;
	unsigned int pboBuffer = 0;
	unsigned int arrayDepth = filenames.size();
				
	glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, GetId(arrayName));
	glGenBuffers(1, &pboBuffer);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pboBuffer);
	glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_R, GL_REPEAT);
	glTexImage3D(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_RGBA8, 64, 64, 4, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);

	for(int i = 0; i < arrayDepth; ++i)
	{
		texture.Load(filenames[i]);
		glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, texture.imageSize, NULL, GL_STREAM_DRAW);
        void* pboMemory = glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY);
        memcpy(pboMemory, texture.imageData, texture.imageSize);
		glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB);
		glTexSubImage3D(GL_TEXTURE_2D_ARRAY_EXT, 0, 0, 0, i, 64, 64, 1, GL_BGRA, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
	}
	glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
	glDeleteBuffers(1, &pboBuffer);
	return true;

Because its not working for me… ;(

Yep.

Because its not working for me… ;(

Not working, or not working fast. I’d expect the latter, because you’re loading your file from disk in the same loop as you’re doing the upload. Using PBOs is useless here. With that disk load in the loop, just use glTexImage#D with a CPU-side memory pointer and be done with it.

If you meant the performance issue in your code, then I see an unnecessary memory copy in your code:
file -> system memory -> PBO -> GL texture

memcpy() between system memory and a PBO should be avoided to increase the performance. I don’t see any advantage in the above path, and even it is slower than the conventional way without PBO:
file -> system memory -> GL texture

The ideal way to improve the data transfer rate is loading the texture source from file to a PBO directly:
file -> PBO -> GL texture

In this path, it significantly increases the texture upload performance by 2 reasons:

1. DMA (Direct Memory Access)
OpenGL (GPU) can transfer the pixel data from a PBO to a texture object through DMA without wasting CPU cycles. (CPU still involves in the texture loading from the file to a PBO, but, CPU is free between PBO and texture.)

2. Asynchronous
OpenGL may put aside this DMA transfer for later execution, so, OpenGL pixel operation calls such as, glTexSubImage2D(), can return immediately. Therefore, CPU can process other jobs without waiting the actual texture copy.

What I am seeing is the mipmapping isn’t working with it. I use glGenerateMipmapsEXT and I get white textures.

Here is the improved code I am now moving from file to PBO as suggested but still doesn’t work? Anyone see what maybe wrong?

Thanks


GenerateTexture2DArrayPBO(std::vector<std::string>& filenames, int wrapMode, bool flag, unsigned int textureSize,
											   unsigned int textureChannels, std::string& arrayName)
{
	unsigned int pboBuffer = 0;
	unsigned int imageTypeFormat = 0, imageTypeInternal = 0;
	unsigned int arrayDepth = filenames.size();
	void* pboMemory = NULL;

	bool bgr = CheckChannelOrder(filenames.at(0));
	switch(textureChannels * 8)
	{
		case 8:
			imageTypeFormat   = GL_LUMINANCE;
			imageTypeInternal = flag ? GL_COMPRESSED_LUMINANCE : GL_LUMINANCE8;
			break;

		case 16:
			break;

		case 24:
			imageTypeFormat   = bgr ? GL_BGR : GL_RGB;
			imageTypeInternal = flag ? GL_COMPRESSED_RGB_S3TC_DXT1_EXT : GL_RGB8;				
			break;

		case 32:
			imageTypeFormat   = bgr ? GL_BGRA :GL_RGBA;
			imageTypeInternal = flag ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_RGBA8;
			break;

		default:
			break;
	}
			
	glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
	glTexImage3D(GL_TEXTURE_2D_ARRAY_EXT, 0, imageTypeInternal, 64, 64, arrayDepth, 0, imageTypeFormat, GL_UNSIGNED_BYTE, NULL);
	glGenBuffers(1, &pboBuffer);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pboBuffer);
	glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, GetId(arrayName));
	if(gameSetupData.enableMipmaps)
	{
		glGenerateMipmapEXT(GL_TEXTURE_2D_ARRAY_EXT);
		//glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_GENERATE_MIPMAP, GL_TRUE);
		glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	}
	else
	{
		glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	}
	glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_S, wrapMode);
	glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_T, wrapMode);
	glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_R, wrapMode);

	for(int i = 0; i < arrayDepth; ++i)
	{
		glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, 64*64*textureChannels, NULL, GL_STREAM_DRAW);
		pboMemory = glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY);
		LoadTGAFile(filenames[i].c_str(), pboMemory);
		glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB);	
		glTexSubImage3D(GL_TEXTURE_2D_ARRAY_EXT, 0, 0, 0, i, 64, 64, 1, 
						imageTypeFormat, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));		
	}
	glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
	glDeleteBuffers(1, &pboBuffer);
	if(gameSetupData.enableMipmaps)
		glGenerateMipmapEXT(GL_TEXTURE_2D_ARRAY_EXT);
	
	return true;
}

bool LoadTGAFile(const char *filename, void* pboMemory)
{
	unsigned char TGAheader[12] = {0,0,2,0,0,0,0,0,0,0,0,0};	// Uncompressed TGA Header
	unsigned char TGAcompare[12] = {0};                         // Used To Compare TGA Header
	unsigned char header[6] = {0};                              // First 6 Useful Bytes From The Header
	unsigned int bytesPerPixel = 0;                             // Holds Number Of Bytes Per Pixel Used In The TGA File
	unsigned int bpp = 0;										// Hold number of bits per pixel
	unsigned int temp = 0;                                      // Temporary Variable
	unsigned int imageSize = 0;
	unsigned int imageWidth = 0, imageHeight = 0;
	
	FILE *file = fopen(filename, "rb");                         // Open The TGA File
	
	if(file == NULL ||                                                       
			fread(TGAcompare, 1, sizeof(TGAcompare), file) != sizeof(TGAcompare) || 
			memcmp(TGAheader, TGAcompare, sizeof(TGAheader)) != 0 || fread(header, 1, sizeof(header), file) != sizeof(header))                    
	{
		if(file == NULL)
		{
			//kill app here
			return false;
		}
		else
		{
			//kill app here
			fclose(file);                                     
			return false;                                    
		}
	}
	
	imageWidth = header[1] * 256 + header[0];   // Determine The TGA Width      (highbyte*256+lowbyte)
	imageHeight = header[3] * 256 + header[2];  // Determine The TGA Height     (highbyte*256+lowbyte)
    
	if(imageWidth  <= 0 || imageHeight <= 0 || (header[4] != 24 && header[4] != 32))
	{
		//kill app here
		fclose(file);                                     
		return false;                           
	}
	
	bpp = header[4];// Grab The TGA's Bits Per Pixel (24 or 32)
	bytesPerPixel = bpp / 8;// Divide By 8 To Get The Bytes Per Pixel
	imageSize = imageWidth * imageHeight * bytesPerPixel;// Calculate The Memory Required For The TGA Data	
    if(fread(pboMemory, 1, imageSize, file) != imageSize)
    {
		//kill app here
		fclose(file);                                     
		return false;                           
	}	
	fclose(file);
	return true;
}


glTexSubImage2D() will trigger automatic mipmaps generation, if GL_GENERATE_MIPMAP is once set to GL_TRUE.

So, I believe that calling glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE) is better than the manual mipmap generation with glGenerateMipmapEXT().

Please test once again with only the base level texture after disabling all mipmapping settings of your system in order to see if it is mipmapping problem.

I already tried GL_GENERATE_MIPMAP and still doesn’t work. I even tried manually setting the mipmap chain for each level with glGenerateMipmapsEXT and still nothing.

Ok, the problem is its a bug, as of driver 175.19 on Vista 64bit, and Nvidia is working on fixing it.

I am using the exact same driver and I get very performance with PBO texture transfer. (its actually alot better to transfer the texture in the normal way)

Is there any word on when a improved driver will be made public?