Load texture from other mipmap not working

I just upgraded my project to the latest SDK and it seems that





PVRTTextureLoadFromPointer is failing when the nLoadFromLevel parameter is anything other than 0.





Textures seem to appear completely black when passed a 1.





This seems to be true for various formats including:





PVRTC 4 bit


RGBA 4444


IA 88








I’m pretty much relying on this to handle memory issues on older iOS devices so will have to move back to an older version unless it’s fixed or somebody can tell me what I’m doing wrong.





Thanks


Glenn.


Hi Glenn,

Which API are you using? OGLES1 or OGLES2? Also is this the GDC release or has this come from our website?

Thanks,

Tobias



GLES2, it’s the latest build from the website. Downloaded two days ago. If i’m not doing anything stupid and i’m right about the error it should be possible to make anything that uses PVRTTextureLoadFromPointer fail by changing loadFrom to 1.


Hi Glenn,

Your usage appears to be correct, there are a couple of missing bits of code in the loading function. It seems that none of our demos actually use this functionality which is why it didn't get caught before.

I'll push a fix to the website later, but for now you can fix it yourself it you open "<sdk>/Tools/OGLES2/PVRTTextureAPI.cpp", and on lines 485,486,592,593,745,746,797 and 798, if you add '>>nLoadFromLevel' to the end of these lines, it should fix your problem. Basically the dimensions that the texture is being loaded with are incorrect when loading from a different level.

Thanks,

Tobias



Tobias2012-03-15 11:57:02

Maybe i’m only hitting it because the texture system is the only part of the SDK i’m currently using. I was rolling my own but I love the way yours has support for A8, I8, A8I8, 4444 etc. I have a tiny wrapper over the PVR system. I drop the top level on most textures which means my iPad2 targeted app fits comfortably in 3GS. I also selectively drop the top level on a few textures for iPhone4/iPad1 for memory and performance issues. I’m surprised this method isn’t used all the time as it handles almost all of my memory worries on older devices.





I’ll try that fix as soon ASAP. Thanks very much for looking into it so quickly





My Blog

I just tried updating to version 3.0 and it seems that this problem still exists even though the changes you describe have been made.



I managed a partial fix by inserting this:



for (PVRTuint32 uiMIPLevel=0; uiMIPLevel<nLoadFromLevel; ++uiMIPLevel)

{

uiCurrentMIPSize=PVRTGetTextureDataSize(*psTempHeader,uiMIPLevel,false,false);

pTempData+=uiCurrentMIPSize;

}



at line 811, it seems that although the sizes were correct, the source data wasn’t being stepped into enough.

I think I need to make this change in a few more places for legacy/other formats to fix completely.

Pasting that for loop in twice seems to fix it in all cases (that i’ve tried) at 772 & 817



Glad I fixed this as I may well be about to start using more than just the texture system and I wouldn’t want to be stuck on an old version

Somehow that loop got corrupted, maybe this’ll work

for (PVRTuint32 uiMIPLevel=0; uiMIPLevel<nLoadFromLevel; ++uiMIPLevel)

{

uiCurrentMIPSize=PVRTGetTextureDataSize(*psTempHeader,uiMIPLevel,false,false);

pTempData+=uiCurrentMIPSize;

}

Hi Glenn,



Yep you’re right, not sure how I missed this since I tested that this worked before giving you the fix… but simple code inspection shows that there’s no way it worked correctly. I’m just going to assume that I went temporarily insane :slight_smile:



The easiest fix I can see is to just loop through all levels, and have an if statement for the levels that you actually load (beyond nLoadFromLevel). So in other words, from lines 744 to 851, replace with:



//Initialise the width/height

PVRTuint32 u32MIPWidth = sTextureHeader.u32Width;

PVRTuint32 u32MIPHeight = sTextureHeader.u32Height;



//Temporary data to save on if statements within the load loops.

PVRTuint8* pTempData=NULL;

PVRTextureHeaderV3 psTempHeader=NULL;

if (bIsCompressedFormat && !bIsCompressedFormatSupported)

{

pTempData=(PVRTuint8
)pDecompressedData;

psTempHeader=&sTextureHeaderDecomp;

}

else

{

pTempData=pTextureData;

psTempHeader=&sTextureHeader;

}



//Loop through all MIP levels.

if (bIsLegacyPVR)

{

//Temporary texture target.

GLint eTextureTarget=eTarget;



//Loop through all the faces.

for (PVRTuint32 uiFace=0; uiFaceu32NumFaces; ++uiFace)

{

//Loop through all the mip levels.

for (PVRTuint32 uiMIPLevel=0; uiMIPLevelu32MIPMapCount; ++uiMIPLevel)

{

//Get the current MIP size.

uiCurrentMIPSize=PVRTGetTextureDataSize(*psTempHeader,uiMIPLevel,false,false);



if (uiMIPLevel>=nLoadFromLevel)

{

//Upload the texture

if (bIsCompressedFormat && bIsCompressedFormatSupported)

{

glCompressedTexImage2D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat,u32MIPWidth, u32MIPHeight, 0, uiCurrentMIPSize, pTempData);

}

else

{

glTexImage2D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat, u32MIPWidth, u32MIPHeight, 0, eTextureFormat, eTextureType, pTempData);

}

}

pTempData+=uiCurrentMIPSize;



//Reduce the MIP Size.

u32MIPWidth=PVRT_MAX(1,u32MIPWidth>>1);

u32MIPHeight=PVRT_MAX(1,u32MIPHeight>>1);

}



//Increase the texture target.

eTextureTarget++;



//Reset the current MIP dimensions.

u32MIPWidth=psTempHeader->u32Width;

u32MIPHeight=psTempHeader->u32Height;



//Error check

if(glGetError())

{

FREE(pDecompressedData);

PVRTErrorOutputDebug(“PVRTTextureLoadFromPointer failed: glTexImage2D() failed.n”);

return PVR_FAIL;

}

}

}

else

{

for (PVRTuint32 uiMIPLevel=0; uiMIPLevelu32MIPMapCount; ++uiMIPLevel)

{

//Get the current MIP size.

uiCurrentMIPSize=PVRTGetTextureDataSize(*psTempHeader,uiMIPLevel,false,false);



GLint eTextureTarget=eTarget;



for (PVRTuint32 uiFace=0; uiFaceu32NumFaces; ++uiFace)

{

if (uiMIPLevel>=nLoadFromLevel)

{

//Upload the texture

if (bIsCompressedFormat && bIsCompressedFormatSupported)

{

glCompressedTexImage2D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat,u32MIPWidth, u32MIPHeight, 0, uiCurrentMIPSize, pTempData);

}

else

{

glTexImage2D(eTextureTarget,uiMIPLevel-nLoadFromLevel,eTextureInternalFormat, u32MIPWidth, u32MIPHeight, 0, eTextureFormat, eTextureType, pTempData);

}

}

pTempData+=uiCurrentMIPSize;

eTextureTarget++;

}



//Reduce the MIP Size.

u32MIPWidth=PVRT_MAX(1,u32MIPWidth>>1);

u32MIPHeight=PVRT_MAX(1,u32MIPHeight>>1);



//Error check

if(glGetError())

{

FREE(pDecompressedData);

PVRTErrorOutputDebug(“PVRTTextureLoadFromPointer failed: glTexImage2D() failed.n”);

return PVR_FAIL;

}

}

}



That should work - I did a quick test to verify (twice).



Thanks,

Tobias

Thanks Tobias but my few lines pasted in two places effectively skips through the source data correctly, for my apps at least.



I’m really surprised that more people don’t make use of this, I have some textures that load from level 0, 1 or 2 depending on the detected device, this not only saves memory and is the only thing that lets my game fit in 3gs, smaller textures also make the iPad1 version run measurably faster.



I’ll stick with my own fix as it’s much smaller but if I run into any problems i’ll use yours instead.

No problem, I basically posted my fix just in case you ran into any problems :slight_smile: The code I’ve posted should be in our 3.1 release next year.



I hadn’t actually considered this use case (I’m guessing many other developers are in the same boat?), I’m looking into redoing the texture loading to be a little neater and more reliable, so I’ll keep this in mind.