Search code examples
c#openglopentkmipmaps

OpenTK manual upload of mipmaps from dds texture doesn't work


I have some dds textures will fully generated mipmaps (usually 10 levels). Up to know I was just loading the first base level and I was generating mipmaps using GL.GenerateMipmap(GenerateMipmapTarget.Texture2D), which worked perfectly.

Since I already save the mipmap data, I wanted to load them manually to the rest levels instead of calling the -very expensive- callback.

This is the corresponding code

//Upload to GPU
bufferID = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, bufferID);

//Temp Variables
int w = width;
int h = height;
int mm_count = ddsImage.header.dwMipMapCount;
int temp_size = ddsImage.header.dwPitchOrLinearSize;
int offset = 0;

for (int i=0; i < ddsImage.header.dwMipMapCount; i++)
{
     if (w == 0 || h == 0)
        continue;
     byte[] tex_data = new byte[temp_size];
     Array.Copy(ddsImage.bdata, offset, tex_data, 0, temp_size);
     GL.CompressedTexImage2D(TextureTarget.Texture2D, i, this.pif, w, h, 0, temp_size, tex_data);
     //Console.WriteLine(GL.GetError());
     offset += temp_size;
     temp_size /= 4;
     w = w >> 1;
     h = h >> 1;
}

Console.WriteLine(GL.GetError());
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureLodBias, -0.2f);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int) TextureWrapMode.Repeat);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int) TextureWrapMode.Repeat);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
//GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); //Generate Mipmaps from the base level
//Console.WriteLine(GL.GetError());

int max_level = 0;
GL.GetTexParameter(TextureTarget.Texture2D, GetTextureParameter.TextureMaxLevel, out max_level);
int base_level = 0;
GL.GetTexParameter(TextureTarget.Texture2D, GetTextureParameter.TextureBaseLevel, out base_level);

int maxsize = Math.Max(height, width);
int p = (int) Math.Floor(Math.Log(maxsize, 2)) + base_level;
int q = Math.Min(p, max_level);

#if(DEBUG)
      //Get all mipmaps
      temp_size = ddsImage.header.dwPitchOrLinearSize;
      for (int i = 0; i < q; i++)
      {
          //Get lowest calculated mipmap
          byte[] pixels = new byte[temp_size];

          //Save to disk
          GL.GetCompressedTexImage(TextureTarget.Texture2D, i, pixels);
          File.WriteAllBytes("Temp\\level" + i.ToString(), pixels);
          temp_size = Math.Max(temp_size / 4, 16);
      }
#endif


ddsImage = null;

ddsImage is an object that holds dds image information and all the pixel data.

These textures are rendered completely black. If I uncomment the line for the mipmap generation everything works like a charm. Also, when I dump the mipmap levels data they're not corrupt. I have a feeling that I need to somehow tell opengl how many mipmaps I have defined but the big question here is why not even the base level doesn't work. At least this one should have worked.


Solution

  • Ok so it turns out that indeed the texture I create is probably incomplete according to https://www.khronos.org/opengl/wiki/Common_Mistakes#Legacy_Generation

    I followed the steps and I found out that I need to set the MAX_LEVEL and BASE_LEVEL parameters BEFORE uploading the texture data and this way it worked like a charm.

    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBaseLevel, 0);
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMaxLevel, 8);
    

    The value for the max level is supposed to be mips - 1. For some reason though, the texture is again pitch black when I try to use all the mipmaps of the image. I still haven't tested it thoroughly but I think that I should completely ommit the upload of the 2x2 and 1x1 mipmaps. I'll update the answer if I test this.