Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 6 of 6

Thread: Loading DDS textures into a cube map array

  1. #1
    Junior Member Newbie
    Join Date
    Oct 2016
    Posts
    17

    Loading DDS textures into a cube map array

    I'm trying to load some DXT1 compressed textures into a cube map array, and running into a couple errors. I'm using opengl 4.5. The code to read in the files was is based off of this tutorial, and I think it's working correctly. The results all have consistent height, width, and size. Also, in case it matters, the images do have mipmaps.

    My first issue is that glCompressedTexImage3D is giving me an INVALID_VALUE. From the documentation, I think that has to mean my imageSize is invalid. But I'm reading it directly from the the image files as they are loaded. I'm not sure how else I'm supposed to get it.

    The second error is INVALID_OPERATION, in glCompressedTexSubImage3D. The docs have a couple things that can cause this, but I'm guessing it's this one:
    GL_INVALID_OPERATION is generated if parameter combinations are not supported by the specific compressed internal format as specified in the specific texture compression extension.
    But I'm not really sure what that means, or how to fix it. My format in this case is GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, but I've tried using more general ones, like GL_COMPRESSED_RGBA. No luck.

    Code :
    void Visual::LoadTextures()
    {
    	std::vector<std::string> faces;
     
    	for (int i = 0; i < imguiStatus.textureFolders.size(); i++) {
    		std::string folder = imguiStatus.textureFolders[i];
    		faces.push_back("../cubemaps/" + folder + "/right_PNG_DXT1_1.dds");
    		faces.push_back("../cubemaps/" + folder + "/left_PNG_DXT1_1.dds");
    		faces.push_back("../cubemaps/" + folder + "/top_PNG_DXT1_1.dds");
    		faces.push_back("../cubemaps/" + folder + "/bottom_PNG_DXT1_1.dds");
    		faces.push_back("../cubemaps/" + folder + "/back_PNG_DXT1_1.dds");
    		faces.push_back("../cubemaps/" + folder + "/front_PNG_DXT1_1.dds");
    	}
    	glGenTextures(1, &cubemap);
    	glActiveTexture(GL_TEXTURE0);
    	glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, cubemap);
     
    	int width, height, mipmapCount, format, imageSize;
    	unsigned char * image;
    	for (GLuint i = 0; i < faces.size(); i++)
    	{
    		image = LoadDDS(faces[i], &format, &mipmapCount, &width, &height, &imageSize);
     
    		// format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT in my case
    		if (i == 0) {
    			glCompressedTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY,
    				0,                           // level
    				format,                      // Internal format
    				width, height, faces.size(), // width,height,depth
    				0,						     //border
    				faces.size() * imageSize,    // imageSize
    				0);                          // pointer to data
     
    			GetGlError();
    		}
     
    		unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
    		unsigned int offset = 0;
     
    		/* load the mipmaps */
    		for (unsigned int level = 0; level < mipmapCount && (width || height); ++level)
    		{
    			int size = ((width + 3) / 4)*((height + 3) / 4)*blockSize;
    			glCompressedTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, level, 0, 0, i, width, height, 1, format, size, image + offset);
     
    			GetGlError();
     
    			offset += size;
    			width /= 2;
    			height /= 2;
    		}
    		free(image);
    	}
     
    	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    }
     
     
    //http://www.opengl-tutorial.org/beginners-tutorials/tutorial-5-a-textured-cube/
    #define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
    #define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
    #define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
    unsigned char * Visual::LoadDDS(std::string imagepath, int * format, int * mipmapCount, int * width, int * height, int * imageSize)
    {
    	unsigned char header[124];
     
    	FILE *fp;
     
    	/* try to open the file */
    	fopen_s(&fp, imagepath.c_str(), "rb");
    	if (fp == NULL)
    		return 0;
     
    	/* verify the type of file */
    	char filecode[4];
    	fread(filecode, 1, 4, fp);
    	if (strncmp(filecode, "DDS ", 4) != 0) {
    		fclose(fp);
    		return 0;
    	}
     
    	/* get the surface desc */
    	fread(&header, 124, 1, fp);
     
    	*height = *(unsigned int*)&(header[8]);
    	*width = *(unsigned int*)&(header[12]);
    	*mipmapCount = *(unsigned int*)&(header[24]);
     
    	unsigned int linearSize = *(unsigned int*)&(header[16]);
    	unsigned int fourCC = *(unsigned int*)&(header[80]);
     
    	unsigned char * buffer;
    	/* how big is it going to be including all mipmaps? */
    	*imageSize = *mipmapCount > 1 ? linearSize * 2 : linearSize;
    	buffer = (unsigned char*)malloc(*imageSize * sizeof(unsigned char));
    	fread(buffer, 1, *imageSize, fp);
    	/* close the file pointer */
    	fclose(fp);
     
    	switch (fourCC)
    	{
    	case FOURCC_DXT1:
    		*format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
    		break;
    	case FOURCC_DXT3:
    		*format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
    		break;
    	case FOURCC_DXT5:
    		*format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
    		break;
    	default:
    		free(buffer);
    		return 0;
    	}
     
    	return buffer;
    }

    Any help is much appreciated!

  2. #2
    Junior Member Newbie
    Join Date
    Oct 2016
    Posts
    17
    I've made some progress, but could still use some help.

    The INVALID_VALUE error was caused because imageSize is the size in bytes of the base compressed image PLUS the size of all the compressed mipmaps. I just needed the size of the base image. I'm still not sure about INVALID_OPERATION unfortunately. However, it seems to be caused by the mipmaps. No errors if I only run glCompressedTexSubImage3D with level == 0 (base image). I can then just use glGenerateMipmap, and everything works. However, there is a noticeable load time doing that, so I'd still really like to figure this out.

    I have not been able to find a single working example of this. I'd have guessed it was very common to load compressed images with mipmaps. Seems like the obvious thing to do if you care about performance. Am I trying to do something unusual here? I can get almost the same code to work using a non-array target (GL_TEXTURE_2D), so is there something special about GL_TEXTURE_CUBE_MAP_ARRAY?

  3. #3
    Advanced Member Frequent Contributor arekkusu's Avatar
    Join Date
    Nov 2003
    Posts
    871
    Quote Originally Posted by Sunny_Lime View Post
    I'm still not sure about INVALID_OPERATION unfortunately.
    Your (simplified) code does:
    Code :
    allocate storage (TexImage) w x h x faces for mipmap level zero
    for each face
        for each mipmap level
            upload image (TexSubImage) w x h x 1

    Of course that doesn't work, because you can't upload an image to a mipmap level you never allocated storage for.
    This should be blatantly obvious if you inspect your texture in a debugger (or just introspect it with glGetTexLevelParameter) at the point the error is thrown.

  4. #4
    Junior Member Newbie
    Join Date
    Oct 2016
    Posts
    17
    I see. So, I'm allocating space for every level now. The errors have gone away, and the textures seem to be loaded correctly if I'm interpreting the results from glGetTexLevelParameteriv correctly. But it seems like the mipmaps aren't actually being used (result is just black instead of textures). I don't think this is a problem in another part of my program, since glGenerateMipmap still works fine.

    Code :
    void Visual::LoadTextures()
    {
    	std::vector<std::string> faces;
     
    	for (int i = 0; i < imguiStatus.textureFolders.size(); i++) {
    		std::string folder = imguiStatus.textureFolders[i];
     
    		//source: http://planetpixelemporium.com/earth.html
    		//converted from equirectangular to cubemap using this (blender) 
    		//https://developers.theta360.com/en/forums/viewtopic.php?f=4&t=1981
    		faces.push_back("../cubemaps/" + folder + "/right_PNG_DXT1_1.dds");
    		faces.push_back("../cubemaps/" + folder + "/left_PNG_DXT1_1.dds");
    		faces.push_back("../cubemaps/" + folder + "/top_PNG_DXT1_1.dds");
    		faces.push_back("../cubemaps/" + folder + "/bottom_PNG_DXT1_1.dds");
    		faces.push_back("../cubemaps/" + folder + "/back_PNG_DXT1_1.dds");
    		faces.push_back("../cubemaps/" + folder + "/front_PNG_DXT1_1.dds");
    	}
     
    	glGenTextures(1, &cubemap);
    	glActiveTexture(GL_TEXTURE0);
    	glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, cubemap);
     
    	int width, height, mipmapCount, format, linearSize;
    	unsigned char * image;
    	for (GLuint i = 0; i < faces.size(); i++)
    	{
    		image = LoadDDS(faces[i], &format, &mipmapCount, &width, &height, &linearSize);
     
    		unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
    		unsigned int offset = 0;
     
    		/* load the mipmaps */
    		for (unsigned int level = 0; level < mipmapCount && (width || height); level++)
    		{
    			int size = ((width + 3) / 4)*((height + 3) / 4)*blockSize;
     
    			if (i == 0) {
    				glCompressedTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY,
    					level,                       // level
    					format,                      // Internal format
    					width, height, faces.size(), // width,height,depth
    					0,						     // border
    					faces.size() * size,         // imageSize
    					0);                          // pointer to data
    			}
     
    			glCompressedTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, level, 0, 0, i, width, height, 1, format, size, image + offset);
     
    			offset += size;
    			width /= 2;
    			height /= 2;
    		}
     
    		free(image);
    	}
    	int test;
    	glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_ARRAY, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &test); //34603008
    	glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_ARRAY, 1, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &test); //8650752
    	glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_ARRAY, 2, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &test); //2162688
    	glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_ARRAY, 3, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &test); //540672
    	glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_ARRAY, 4, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &test); //135168
    	glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_ARRAY, 5, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &test); //33792
     
    	int maxlevel;
    	glGetTexParameteriv(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAX_LEVEL, &maxlevel); //1000
     
    	//glGenerateMipmap(GL_TEXTURE_CUBE_MAP_ARRAY);
    	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    	glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    }

  5. #5
    Advanced Member Frequent Contributor arekkusu's Avatar
    Join Date
    Nov 2003
    Posts
    871
    Quote Originally Posted by Sunny_Lime View Post
    Code :
        glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_ARRAY, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &test); //34603008
        glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_ARRAY, 5, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &test); //33792
        glGetTexParameteriv(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAX_LEVEL, &maxlevel); //1000
    I think you have your answers right in front of you.

    * What width & height do you expect your input images to be?
    * How many pixels does 34603008 DXT1 bytes correspond to? (in a square width x height x faces level 0 image)
    * Does that match your expectation?
    * How big is the smallest mipmap you've specified?
    * And what happens when the 1x1 mipmap level doesn't exist?

  6. #6
    Junior Member Newbie
    Join Date
    Oct 2016
    Posts
    17
    Quote Originally Posted by arekkusu View Post
    * What width & height do you expect your input images to be?
    * How many pixels does 34603008 DXT1 bytes correspond to? (in a square width x height x faces level 0 image)
    * Does that match your expectation?
    the base image is 1024 x 1024 = 1048576 pixels
    according to wikipedia: 1048576px * (64bits / 16px) * (1byte / 8bits) = 524288 bytes.
    And I have 66 faces, so the the total comes out to 34603008 bytes, as expected.
    Quote Originally Posted by arekkusu View Post
    * How big is the smallest mipmap you've specified?
    * And what happens when the 1x1 mipmap level doesn't exist?
    Yeah, this is embarrassing. Should have been
    Code :
    for (unsigned int level = 0; level < mipmapCount + 1 && (width || height); level++)
    I just wasn't generating the smallest mipmap. Everything works now. Thanks so much for your help and patience

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •