Can anyone help me? Baffling tech problem.

Discussion in 'Game Development (Technical)' started by Raptisoft, May 22, 2008.

  1. Raptisoft

    Raptisoft
    Expand Collapse
    Indie Author

    Joined:
    Jul 29, 2004
    Messages:
    804
    Likes Received:
    0
    Hi Everyone,

    Years ago when XP came out, Hamsterball manifested a bizarre load error, only on XP. The problem manifested during texture load-- I am using the pure DirectX helper code to load images, and somehow it bombs out.

    After tweaking it a bit, I fixed it so it didn't happen on XP any more and all was well.

    However, now it's happening on Vista. Same problem, same place. My problem is, it only happens on SOME Vista, and, not any copy of Vista I try it on.

    This is a bit of a desperation move, but I hoped if I posted the code here, some DirectX guru could look at it and suddenly say, hey, you're not supposed to use that function in that way.

    So here's the code. If I eliminate the mipmap stuff, it seems to work fine. With mipmaps, it will return DX errors (and give the user a "can't load texture") frequently. And the error is always DX_ERR_UNKNOWN.

    Any help gets you a free copy of your choice of Raptisoft game, including the next one. :)

    Here's the code:


    Code:
    Texture::Texture(Graphics *theGraphics, char *theFilename)
    {
    	mGraphics=theGraphics;
    	mHasAlpha=false;
    	mHasMip=false;
    	mPointFilter=false;
    
    	mTexture=NULL;
    	mFilename=NULL;
    
    	char aFilename[MAX_PATH];
    	char aPath[3][MAX_PATH];
    
    	sprintf(aPath[0],"textures\\");
    	sprintf(aPath[1],"");
    	sprintf(aPath[2],"");
    
    	if (mGraphics->mSubstituteDirectory)
    	{
    		sprintf(aPath[0],"%s\\",mGraphics->mSubstituteDirectory);
    		sprintf(aPath[1],"textures\\");
    	}
    
    	int aStartPath=0;
    
    	//
    	// Look for a mipmap version of the texture...
    	// if no -mip1 file exists, then it's just 'alone'
    	//
    
    	bool aFound=false;
    	bool aFoundMip=false;
    
    	char aMipFilename[MAX_PATH];
    	char aMipExtension[MAX_PATH];
    	strcpy(aMipFilename,theFilename);
    	for (int aCount=0;aCount<strlen(aMipFilename);aCount++)
    	{
    		if (aMipFilename[aCount]=='.')
    		{
    			strcpy(aMipExtension,&aMipFilename[aCount+1]);
    			aMipFilename[aCount]=0;
    		}
    	}
    
    	for (int aCount=0;aCount<3;aCount++)
    	{
    		sprintf(aFilename,"%s%s-mip1.%s",aPath[aStartPath],aMipFilename,aMipExtension);
    		if (access(aFilename,0)==0) aFoundMip=true;
    
    		sprintf(aFilename,"%s%s",aPath[aStartPath],theFilename);
    		if (access(aFilename,0)==0)
    		{
    			aFound=true;
    			break;
    		}
    
    		aStartPath++;
    		if (aStartPath>3) aStartPath=0;
    	}
    
    
    	//
    	// Okay: We crash here.  We crash on CreateTextureFromFileA, when we have a widescreen
    	// monitor.  Why is this?  This is BULLSHIT.
    	//
    
    	HRESULT aResult=D3DXCreateTextureFromFileA(mGraphics->mDevice,aFilename,&mTexture);
    	if (FAILED(aResult)) 
    	{
    		aResult=D3DXCreateTextureFromFileA(mGraphics->mDevice,aFilename,&mTexture);
    		if (FAILED(aResult)) 
    		{
    			switch (aResult)
    			{
    			case D3DERR_NOTAVAILABLE: gOut.Out("D3DERR_NOTAVAILABLE");
    			case D3DERR_OUTOFVIDEOMEMORY: gOut.Out("D3DERR_OUTOFVIDEOMEMORY");
    			case D3DERR_INVALIDCALL: gOut.Out("D3DERR_OUTOFVIDEOMEMORY");
    			case D3DXERR_INVALIDDATA: gOut.Out("D3DXERR_INVALIDDATA");
    			case E_OUTOFMEMORY: gOut.Out("E_OUTOFMEMORY");
    			}
    
    			MessageBox(0,theFilename,"TEXTURE LOAD FAILED!",MB_OK);
    			mTexture=NULL;
    		}
    	}
    
    	D3DFORMAT aFormat;
    	if (mTexture!=NULL)
    	{
    		D3DSURFACE_DESC aDesc;
    		mTexture->GetLevelDesc(0,&aDesc);
    
    		aFormat=aDesc.Format;
    	
    		switch (aDesc.Format)
    		{
    		case D3DFMT_A8R8G8B8:
    		case D3DFMT_A4R4G4B4:
    		case D3DFMT_A8R3G3B2:
    			mHasAlpha=true;
    			break;
    		}
    		mWidth=aDesc.Width;
    		mHeight=aDesc.Height;
    	
    		mFilename=new char[strlen(theFilename)+1];
    		strcpy(mFilename,theFilename);
    		mReference=1;
    	}
    
    	mMipCount=0;
    	if (aFoundMip)
    	{
    		//
    		// Buggy?
    		// We seem to have a load problem that *seems* to be mipmap related.
    		// It's very weird.  Checking into it.  At the moment, it looks like
    		// leaving out the mipmaps just makes things magically work.
    		//
    
    
    
    
    		//
    		// Okay... we found mipmaps!
    		// So, we're gonna make a new texture, first,
    		// just to see if we can...
    		//
    		mTexture->Release();
    		mTexture=NULL;
    
    		D3DXCreateTextureFromFileA(mGraphics->mDevice,aFilename,&mMip[0]);
    		int aMipLevels=1;
    		for (int aCount=1;aCount<20;aCount++)
    		{
    			char aMipFN[MAX_PATH];
    			sprintf(aMipFN,"%s%s-mip%d.%s",aPath[aStartPath],aMipFilename,aCount,aMipExtension);
    			if (access(aMipFN,0)==0)
    			{
    				HRESULT aResult=D3DXCreateTextureFromFileA(mGraphics->mDevice,aMipFN,&mMip[aCount]);
    				if (FAILED(aResult))
    				{
    					HRESULT aResult=D3DXCreateTextureFromFileA(mGraphics->mDevice,aMipFN,&mMip[aCount]);
    					if (FAILED(aResult))
    					{
    						switch (aResult)
    						{
    						case D3DERR_NOTAVAILABLE: gOut.Out("D3DERR_NOTAVAILABLE");
    						case D3DERR_OUTOFVIDEOMEMORY: gOut.Out("D3DERR_OUTOFVIDEOMEMORY");
    						case D3DERR_INVALIDCALL: gOut.Out("D3DERR_OUTOFVIDEOMEMORY");
    						case D3DXERR_INVALIDDATA: gOut.Out("D3DXERR_INVALIDDATA");
    						case E_OUTOFMEMORY: gOut.Out("E_OUTOFMEMORY");
    						}
    			
    						MessageBox(0,theFilename,"TEXTURE LOAD FAILED!",MB_OK);
    						mTexture=NULL;
    						return;
    					}
    				}
    				aMipLevels++;
    			}
    			else break;
    		}
    
    		HRESULT aResult=theGraphics->mDevice->CreateTexture(mWidth,mHeight,aMipLevels, 0,aFormat, D3DPOOL_MANAGED, &mTexture);
    		if (FAILED(aResult))
    		{
    			aResult=theGraphics->mDevice->CreateTexture(mWidth,mHeight,aMipLevels, 0,aFormat, D3DPOOL_MANAGED, &mTexture);
    			if (FAILED(aResult)) 
    			{
    				switch (aResult)
    				{
    				case D3DERR_NOTAVAILABLE: gOut.Out("D3DERR_NOTAVAILABLE");
    				case D3DERR_OUTOFVIDEOMEMORY: gOut.Out("D3DERR_OUTOFVIDEOMEMORY");
    				case D3DERR_INVALIDCALL: gOut.Out("D3DERR_OUTOFVIDEOMEMORY");
    				case D3DXERR_INVALIDDATA: gOut.Out("D3DXERR_INVALIDDATA");
    				case E_OUTOFMEMORY: gOut.Out("E_OUTOFMEMORY");
    				}
    	
    				MessageBox(0,theFilename,"TEXTURE LOAD FAILED!",MB_OK);
    				mTexture=NULL;
    				return;
    			}
    		}
    
    		IDirect3DSurface8 *aDest[20];
    		IDirect3DSurface8 *aSrc[20];
    
    		for (int aCount=0;aCount<aMipLevels;aCount++)
    		{
    			aDest[aCount]=NULL;
    			aSrc[aCount]=NULL;
    
    			mTexture->GetSurfaceLevel(aCount,&aDest[aCount]);
    			mMip[aCount]->GetSurfaceLevel(0,&aSrc[aCount]);
    
    			D3DSURFACE_DESC aDesc;
    			mTexture->GetLevelDesc(aCount,&aDesc);
    
    			//
    			// A note to myself:
    			// Putting a rect into CopyRects, instead of just using the
    			// null pointer that's supposed to indicate 'copy the whole surface'
    			// seems to have cleaned up the vista crash.  We'll see how it pans out.
    			//
    
    			RECT aSrcRc={0,0,aDesc.Width,aDesc.Height};
    			POINT aDestPt;
    			aDestPt.x=0;
    			aDestPt.y=0;
    
    			HRESULT aResult;
    			if (aSrc[aCount] && aDest[aCount]) 
    			{
    				aResult=theGraphics->mDevice->CopyRects(aSrc[aCount],&aSrcRc,1,aDest[aCount],&aDestPt);
    				if (!SUCCEEDED(aResult)) 
    				{
    					char eString[256];
    					D3DXGetErrorString(aResult,eString,255);
    					gOut.Out("Error %s [%d,%d]*******************************************************",eString,aDesc.Width,aDesc.Height);
    
    				}
    			}
    		}
    
    		mHasMip=true;
    		mMipCount=aMipLevels;
    	}
    
    }
    
    As you might be able to tell from the comments, I have made a couple 'fixes' that seemed to fix the problem (passing NULL pointers to D3DX code seemed to irritate Vista, for instance)... but none of them fix it completely.

    Thanks again, anyone who can help.
     
  2. cliffski

    cliffski
    Expand Collapse
    Moderator Original Member

    Joined:
    Jul 27, 2004
    Messages:
    3,898
    Likes Received:
    0
    Intel video cards maybe? I trace most of my DX_ERR_UNKNOWN returns to intel video card drivers coded by blind monkeys.
     
  3. chillypacman

    chillypacman
    Expand Collapse
    Guest

    Intel cards have a habit of misreporting their capabilities to directx so it might be there.

    It's bullshit until you realize what's going wrong :p
     
  4. Desktop Gaming

    Desktop Gaming
    Expand Collapse
    Moderator Original Member Indie Author

    Joined:
    Feb 24, 2005
    Messages:
    2,279
    Likes Received:
    0
    I have a Vista laptop with Intel GMA X3100 graphics. PM me if you need anything testing.
     
  5. Backov

    Backov
    Expand Collapse
    Original Member

    Joined:
    Oct 23, 2005
    Messages:
    812
    Likes Received:
    0
    Possibly the driver (or the card, or both) isn't able to create mipmaps and is bombing out somewhere in the process. It's probably the blind monkey thing, as it should be able to create them but probably has a bug.
     
  6. Applewood

    Applewood
    Expand Collapse
    Moderator Original Member Indie Author

    Joined:
    Jul 29, 2004
    Messages:
    3,859
    Likes Received:
    0
    A wild guess. It's crashing because internally the driver is reporting the back display buffer format has an alpha channel, which is not and never has been allowed.

    Note that this is not the same thing as the back buffer format. They added all that checkdeviceformat stuff to protect against this.

    It might be worth adding some debug code just to check this, but I'm not sure if you'll be able to do much about it if it is this.
     
  7. paulble

    paulble
    Expand Collapse
    Original Member

    Joined:
    Feb 17, 2006
    Messages:
    6
    Likes Received:
    0
    A couple questions.

    1) What file format are you loading?

    2) If you can repro this (on someone else's machine), can you turn on d3d debug runtimes and capture the debug spew during texture creation. You might also try running the game under Pix for Windows on the repro machine to see the api calls that d3dx is making (and their result codes).

    3) Can you get the dxdiag output on the machine(s) that fail? Is it possibly a case of d3d devices that cannot create non-power-of-two mipmap chains?

    4) Which mip code do you remove to make it work?


    pb
     
  8. petei

    petei
    Expand Collapse
    New Member

    Joined:
    May 23, 2008
    Messages:
    2
    Likes Received:
    0
    Shot in the dark this....

    From the D3D docs on D3DXCreateTextureFromFile it mentions:

    "When loading images into mipmapped textures, some devices are unable to go to a 1x1 image and this function will fail. If this happens, the images need to be loaded manually."

    Calling D3DXCreateTextureFromFile is equivalent to calling:

    D3DXCreateTextureFromFileEx(pDevice, pSrcFile, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, ppTexture);

    I you look at the documentation for this function you'll notice that it passes in D3DX_DEFAULT to the miplevels parameter. This results in D3D trying to create a texture with full mip-chain down to 1x1. Which as the docs suggest can fail on some devices.

    So from your code, my guess would be either:

    1) You've run into a driver/device that are unable to go to a 1x1 image (as the docs suggests). This would imply you shouldn't attempt to create a full-mip chain and stop at 2x2, 4x4 etc instead.

    2) You run out of resource memory because each and every mip-image you are currently attempting to load is also telling D3D to allocate and create a full mip chain down to 1x1.

    If you are going to do manual mip-level loading yourself, then you're probably better off calling D3DXCreateTextureFromFileEx instead and pass in a 1 to the miplevels parameter, to ensure you're only allocating and creating a single mip-level.

    Cheers
    -Pete
     
  9. Raptisoft

    Raptisoft
    Expand Collapse
    Indie Author

    Joined:
    Jul 29, 2004
    Messages:
    804
    Likes Received:
    0
    Hm,

    I am intrigued by this mipmap thing... as far as I knew, I was creating them manually, and thus they'd never get down to 1x1... are you telling me that code is automatically making its own mipmaps? It doesn't SEEM to be-- if I delete the mipmap files from the texture folder, then the game's visuals are quite degraded and pixellized at a distance.


    --John
     
  10. Applewood

    Applewood
    Expand Collapse
    Moderator Original Member Indie Author

    Joined:
    Jul 29, 2004
    Messages:
    3,859
    Likes Received:
    0
    If you pass a zero in for the number of levels, it will create them even if they're empty waiting your data. It depends on how you're creating em.

    I would suggest working out the number of levels you really need based on their dimensions, then subtract 5 from that to stop them going below 32x32 which seems to be a magic numbers. Even X360 can't deal with smaller than that for some reason.
     
  11. petei

    petei
    Expand Collapse
    New Member

    Joined:
    May 23, 2008
    Messages:
    2
    Likes Received:
    0
    When you call D3DXCreateTextureFromFile d3d is loading the image file and creating a texture of its size with a full mip-chain down to 1x1.

    You can verify this by calling GetLevelCount() on the resulting texture to tell you how many mip-levels have been created.

    Just for kicks I tried this out on the DXSDK/Samples/C++/Direct3D/Tutorials/Tut05_Textures sample. For the 256x256 banana texture you get 9 mip levels, i.e: 256x256, 128x128, 64x64, 32x32, 16x16, 8x8, 4x4, 2x2 and 1x1.

    If I replace the D3DXCreateTextureFromFile with a call to

    D3DXCreateTextureFromFileEx(
    g_pd3dDevice, "banana.bmp",
    D3DX_DEFAULT, D3DX_DEFAULT,
    1, // only 1 mip level please... if it's 0 or D3DX_DEFAULT - you get full mipchain
    0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &g_pTexture);

    you only get 1 mip level.

    -Pete
     
  12. Raptisoft

    Raptisoft
    Expand Collapse
    Indie Author

    Joined:
    Jul 29, 2004
    Messages:
    804
    Likes Received:
    0
    Mips down to 1x1 was the problem.

    Petei, PM me if you want an unlock code. :)
     
  13. Teq

    Teq
    Expand Collapse
    Original Member

    Joined:
    Sep 16, 2004
    Messages:
    228
    Likes Received:
    0
    Nice to see a new poster helping an oldie out :) I stay clear of Directx for the most part, but an interesting read nevertheless
     

Share This Page