Hi all,
this is my first post on the GameDev forums, so first of all... nice to meet you !
After quite a long time using the resources and articles on this site, a problem drove me to register on few of the best dev community sites, in my quest for information (which is quite tricky to find when you work on windows mobile).
the problem
In my 3d engine,
each texture creation reserves 3 times the space it should need in memory.
Hypotheses
- 3d engine based on Loulou's YesEngine (here, in French : http://loulou.developpez.com)
- Runs on Windows Mobile 6.1 with Direct 3d Mobile (D3D subset)
- Windows Mobile is much restrictive concerning virtual memory (32MB against desktop's 2GB), so the heap is full pretty fast.
1st step: texture creation
IRscTextureBase* GDirect3DMRenderer::CreateTexture(const TVector2I& Size, TPixelFormat Format, unsigned long Flags) const
{
// mip level detection and auto creation detection
bool HasMipmaps = (Flags & TEX_NOMIPMAP) != 0;
bool AutoMipmaps = false; // no auto mipmaping on D3DM
unsigned long Usage = m_SurfCaps_Texture; // let the texture be locked
// create texture
LPDIRECT3DMOBILETEXTURE Texture = NULL;
HRESULT hr;
if ( FAILED ( hr = D3DMXCreateTexture(m_Device, Size.x, Size.y, 0, Usage, CD3DMEnum::Get(Format), m_SurfCaps_Pool, &Texture) ) )
throw CD3DMException(_T("D3DXCreateTexture"), _T("CreateTexture"));
return new CD3DMTexture(Size, Format, HasMipmaps, AutoMipmaps, Usage, Texture);
}
D3DMXCreateTexture reserves memory space (ok), but
new CD3DMTexture (simple object to encapsulate my D3DM texture) does the same ! We exit the function with 2 spaces reserved in memory instead of one.
reference :
class CD3DMTexture : public IRscTextureBase
{
public :
CD3DMTexture(const TVector2I& Size, TPixelFormat Format, bool HasMipmaps, bool AutoMipmaps, unsigned long Usage, IDirect3DMobileTexture* Texture);
// GET D3D TEXTURE
IDirect3DMobileTexture* GetDxTexture() const;
private :
// UPDATE PIXELS
virtual void Update(const CRectangle& Rect);
// COPY PIXELS TO LOCKED SURFACE
void UpdateSurface(const D3DMLOCKED_RECT& LockedRect, const CRectangle& Rect);
// DATA
CSmartPtr<IDirect3DMobileTexture , CResourceCOM > m_Texture; // D3D texture pointer
unsigned long m_Usage;
};
// constructor implementation
CD3DMTexture::CD3DMTexture(const TVector2I& Size, TPixelFormat Format, bool HasMipmaps, bool AutoMipmaps, unsigned long Usage, IDirect3DMobileTexture* Texture) :
IRscTextureBase(Size, Format, HasMipmaps, AutoMipmaps),
m_Usage (Usage),
m_Texture (Texture)
{
}
2d step : texture update
A lot of devices (let's say Omnia) don't allow texture locking, thus we're forced to use surface locking, and then copy pixels from one surface to another... as follows :
void CD3DMTexture::Update(const CRectangle& Rect)
{
Assert(CRectangle(0, 0, m_Size.x, m_Size.y).Intersects(Rect) == INT_IN);
if (m_Format == m_Data.GetFormat() && (m_Usage & D3DMUSAGE_LOCKABLE) ) // if pixel format is the same, simple copy (if texture lockable)
{
// lock texture
D3DMLOCKED_RECT LockedRect;
RECT Lock = {Rect.Left(), Rect.Top(), Rect.Right(), Rect.Bottom()};
m_Texture->LockRect(0, &LockedRect, &Lock, 0);
// copy pixels
UpdateSurface(LockedRect, Rect);
// unlock texture
m_Texture->UnlockRect(0);
}
else
{
// get device
CSmartPtr<IDirect3DMobileDevice, CResourceCOM> Device;
m_Texture->GetDevice(&GetPtr(Device));
// create texture in memory
CSmartPtr<IDirect3DMobileSurface, CResourceCOM> Src;
HRESULT res = Device->CreateImageSurface(Rect.Width(), Rect.Height(), CD3DMEnum::Get(m_Data.GetFormat()), &GetPtr(Src));
if (FAILED(res)) // CreateOffscreenPlainSurface not available in D3DM
{
throw CD3DMException(_T("CreateImageSurface"), _T("CD3DMTexture::Update"));
}
// lock temp texture
D3DMLOCKED_RECT LockedRect;
Src->LockRect(&LockedRect, NULL, 0);
// copy pixels
UpdateSurface(LockedRect, Rect);
// unlock texture
Src->UnlockRect();
// get 0 level surface of the texture
CSmartPtr<IDirect3DMobileSurface, CResourceCOM> Dest;
m_Texture->GetSurfaceLevel(0, &GetPtr(Dest));
// copy to destination surface
RECT DestRect = {Rect.Left(), Rect.Top(), Rect.Right(), Rect.Bottom()};
if (FAILED(D3DMXLoadSurfaceFromSurface(Dest, NULL, &DestRect, Src, NULL, NULL, D3DMX_DEFAULT, 0)))
throw CD3DMException(_T("D3DXLoadSurfaceFromSurface"), _T("CD3DMTexture::Update"));
}
}
Src surface reserves the space needed during
CreateImageSurface, then free it when we exit the brackets where it's defined (thanks smart pointer).
However, Dest does also a reservation about the same size during
D3DMXLoadSurfaceFromSurface... but doesn't release it when the smart pointer is freed !
Conclusion
We have 3 times the needed space reserved by a texture, that other components of the program won't be able to use. With 32MB of virtual memory, I guess you already figured how painful it may be !
There shall be some stuff I messed up, or maybe is it due to the implementation of the YesEngine (encapsulating the D3D texture for instance ?) creating some conflict with D3DM data types... anyway...
Help !
Thanks for your help, may you be WM developer or not (it's after all only a subset of Windows API and DirectX ;) )
ps: see here for more information about the well named "WM virtual memory monster" (and thanks a lot to it's author)
http://www.codeproject.com/KB/mobile/VirtualMemory.aspx
edit : did I messed up with the "code" markup ? :(
[Edited by - MMoi on August 18, 2009 7:00:56 PM]