Skip to main content
gamedev.net
🔒 Locked

Copy Texture

Started by Antonym May 25, 2009 at 4:28 AM 13 replies 2.2k views
Original Post
Antonym
Antonym
Hello I am trying to copy/clone a texture but I get an error with my code. Is this not the right way to do it?

	IDirect3DTexture9 *textureCopy;
	*textureCopy = *mTexture; //mTexture is a LPDIRECT3DTEXTURE9

Thanks.
Evil Steve
Evil Steve
Quote:
Original post by Antonym
Hello I am trying to copy/clone a texture but I get an error with my code. Is this not the right way to do it?

*** Source Snippet Removed ***

Thanks.
Nope, that's not correct. Your choices are:
  • D3DXLoadSurfaceFromSurface()
  • LockRect() both surfaces and copy the bits
  • IDirect3DDevice9::StretchRect()
  • IDirect3DDevice9::UpdateSurface()
    Each of those operate on surfaces, so you'll have to do each one for each surface level in the texture.

    However, this isn't usually something you'd want to do (Which is why there's no Duplicate() function or anything). Why do you need two copies of the same texture?
  • Antonym
    Antonym
    Because if I have two or more objects that use the same texture and I edit one I don't want the other(s) to exhibit the edition too :S.
    Evil Steve
    Evil Steve
    Quote:
    Original post by Antonym
    Because if I have two or more objects that use the same texture and I edit one I don't want the other(s) to exhibit the edition too :S.
    Ok, that's fair enough (What you're trying to do is commonly called Copy On Write, or COW).
    What memory pool are your textures in? Assuming they're in D3DPOOL_MANAGED (They should be unless they're dynamic really), D3DPOOL_SYSTEMMEM or D3DPOOL_SCRATCH, you'd be best using the LockRect() method.

    Untested code:
    DWORD GetBytesPerPixelFromFormat(D3DFORMAT fmt){   switch(fmt)   {   case D3DFMT_R8G8B8:      return 3;   case D3DFMT_A8R8G8B8:   case D3DFMT_X8R8G8B8:   case D3DFMT_A2B10G10R10:   case D3DFMT_A8B8G8R8:   case D3DFMT_X8B8G8R8:   case D3DFMT_G16R16:   case D3DFMT_A2R10G10B10:   case D3DFMT_X8L8V8U8:   case D3DFMT_Q8W8V8U8:   case D3DFMT_V16U16:   case D3DFMT_A2W10V10U10:   case D3DFMT_G16R16F:   case D3DFMT_R32F:      return 4;   case D3DFMT_R5G6B5:   case D3DFMT_X1R5G5B5:   case D3DFMT_A1R5G5B5:   case D3DFMT_A4R4G4B4:   case D3DFMT_A8R3G3B2:   case D3DFMT_X4R4G4B4:   case D3DFMT_A8P8:   case D3DFMT_A8L8:   case D3DFMT_V8U8:   case D3DFMT_L6V5U5:   case D3DFMT_L16:   case D3DFMT_R16F:   case D3DFMT_CxV8U8:      return 2;   case D3DFMT_R3G3B2:   case D3DFMT_A8:   case D3DFMT_P8:   case D3DFMT_L8:   case D3DFMT_A4L4:      return 1;   case D3DFMT_A16B16G16R16:   case D3DFMT_Q16W16V16U16:   case D3DFMT_A16B16G16R16F:   case D3DFMT_G32R32F:      return 8;   case D3DFMT_A32B32G32R32F:      return 16;   }   // Unknown format   return 0;}LPDIRECT3DTEXTURE9 DuplicateTexture(LPDIRECT3DTEXTURE9 pSrc, LPDIRECT3DDEVICE9 pDevice){   // Get number of mip levels and size of top level   D3DSURFACE_DESC desc;   DWORD dwLevels = pSrc->GetLevelCount();   HRESULT hResult = pSrc->GetLevelDesc(0, &desc);   if(FAILED(hResult))      return NULL;   DWORD dwBytesPerPixel = GetBytesPerPixelFromFormat(desc.Format);   if(dwBytesPerPixel == 0)      return NULL;   // Create a texture that is the same size   LPDIRECT3DTEXTURE9 pDest = NULL;   hResult = pDevice->CreateTexture(desc.Width, desc.Height, dwLevels, desc.Usage,      desc.Format, desc.Pool, &pDest, NULL);   if(FAILED(hResult))      return NULL;   // Lock and copy each surface level in turn   for(DWORD i=0; i<dwLevels; ++i)   {      // Lock source texture      D3DLOCKED_RECT rectSrc, rectDest;      hResult = pSrc->LockRect(i, &rectSrc, NULL, D3DLOCK_READONLY);      if(FAILED(hResult))      {         pDest->Release();         return NULL;      }      // Lock destination texture      D3DLOCKED_RECT rectDest;      hResult = pDest->LockRect(i, &rectDest, NULL, 0);      if(FAILED(hResult))      {         pSrc->UnlockRect(i);         pDest->Release();         return NULL;      }      // If the pitch is the same as bytes per pixel * width, then we can copy the      // entire surface in one go. Otherwise, there's reserved data at the end of      // each scanline, and we have to copy a row at a time.      if(rectSrc.Pitch == desc.Width*dwBytesPerPixel && rectSrc.Pitch == rectDest.Pitch)      {         memcpy(rectDest.pBits, rectSrc.pBits, rectSrc.Pitch*desc.Height);      }      else      {         DWORD dwBytes = desc.Width*dwBytesPerPixel;         for(UINT y=0; y<desc.Height; ++y)         {            BYTE* pBitsSrc = (BYTE*)rectSrc.pBits + rectSrc.Pitch*y;            BYTE* pBitsDest = (BYTE*)rectDest.pBits + rectDest.Pitch*y;            memcpy(pBitsDest, pBitsSrc, dwBytes);         }      }      // Copy complete, unlock surfaces      pDest->UnlockRect(i);      pSrc->UnlockRect(i);   }   // All levels copied, return newly copied texture   return pDest;}

    Hmm, that took longer to write than I thought...

    EDIT: Removed dwLockFlags stuff, that's only valid for default pool textures, which aren't lockable anyway...

    [Edited by - Evil Steve on May 25, 2009 8:49:10 AM]
    ET3D
    ET3D
    If you want them to use the same texture, just keep a pointer to the same texture ("textureCopy = mTexture").
    Antonym
    Antonym
    Wow that was fast. Thanks a lot!

    Edit: the hResult fails for some reason :S.

          // Lock destination texture      hResult = pSrc->LockRect(i, &rectDest, NULL, 0);      if(FAILED(hResult))      {         pSrc->UnlockRect(i);         pDest->Release();         return NULL;      }


    This is the code I use to load a texture.
    Sprite* Direct3D::LoadSprite(LPCTSTR fileName, 							 float textureWidth, 							 float textureHeight, 							 float mFrameWidth, 							 float mFrameHeight){	LPDIRECT3DTEXTURE9 texture;	D3DXCreateTextureFromFileEx(d3d_device, 		fileName, 		D3DX_DEFAULT_NONPOW2, 		D3DX_DEFAULT_NONPOW2,		D3DX_DEFAULT, 		0, //D3DUSAGE_RENDERTARGET? //0		D3DFMT_A8R8G8B8, 		D3DPOOL_MANAGED, //DEFAULT? //MANAGED		D3DX_DEFAULT, 							D3DX_DEFAULT, 		D3DCOLOR_XRGB(255, 0, 255),		NULL, 		NULL, 		&texture);	Sprite *sprite = new Sprite(texture, 		textureWidth, textureHeight,		mFrameWidth, mFrameHeight);	return sprite;}
    Evil Steve
    Evil Steve
    Quote:
    Original post by Antonym
    Wow that was fast. Thanks a lot!

    Edit: the hResult fails for some reason :S.
    What do the Debug Runtimes say the problem is?

    Also, that was a typo which I corrected; it should be:
    hResult = pDest->Lock(...

    EDIT: In fact, that's probably the problem - you can't have two locks on one surface (Or they can't be overlapping regions, I don't recall offhand)
    Antonym
    Antonym
    Yeah that was the problem though another one popped up.

    The problem occurs at.
             DWORD dwBytes = desc.Width*dwBytesPerPixel;         for(UINT y=0; y<desc.Height; ++y)         {            BYTE* pBitsSrc = (BYTE*)rectSrc.pBits + rectSrc.Pitch*y;            BYTE* pBitsDest = (BYTE*)rectDest.pBits + rectDest.Pitch*y;            memcpy(pBitsDest, pBitsSrc, dwBytes);         }


    The debugger tells me.

    Direct3D9: :DoneExclusiveMode'Dungeon.exe': Loaded 'C:\WINDOWS\system32\usp10.dll'First-chance exception at 0x1023fe24 (msvcr90d.dll) in Dungeon.exe: 0xC0000005: Access violation writing location 0x01899000.Unhandled exception at 0x1023fe24 (msvcr90d.dll) in Dungeon.exe: 0xC0000005: Access violation writing location 0x01899000.
    Evil Steve
    Evil Steve
    Quote:
    Original post by Antonym
    Yeah that was the problem though another one popped up.

    The problem occurs at.
    *** Source Snippet Removed ***

    The debugger tells me.

    *** Source Snippet Removed ***
    What are the values of y and dwBytes when the access violation occurs?
    Antonym
    Antonym
    y is 83
    dwBytes is 384.
    Evil Steve
    Evil Steve
    Quote:
    Original post by Antonym
    y is 83
    dwBytes is 384.
    Interesting. What is desc.Width and desc.Height?
    Antonym
    Antonym
    96 and 96
    Evil Steve
    Evil Steve
    Oh, I see the problem. Change the relevant section of code to the following:
       // Lock and copy each surface level in turn   for(DWORD i=0; i<dwLevels; ++i)   {      // Get size of this level      HRESULT hResult = pSrc->GetLevelDesc(i, &desc);      if(FAILED(hResult))      {         pDest->Release();         return NULL;      }      // Lock source texture      D3DLOCKED_RECT rectSrc;
    The code was incorrectly assuming all mip levels are the same. if that doesn't fix the problem, what are the values of i and dwLevels?
    Antonym
    Antonym
    Yes! It works now ^_^ thanks a lot evil steve.
    XVincentX
    XVincentX
    Quote:
    Original post by ET3D
    If you want them to use the same texture, just keep a pointer to the same texture ("textureCopy = mTexture").


    Generally you should also make a call to Texture->AddRef() in order to make a correct Release on both interfaces.

    Topic Locked

    This topic has been locked by a moderator. New replies are not allowed.

    Sign in to reply to this topic.