最近开始正式写纹理管理,用了差不多2周基本功能已经完成,总结下来纹理管理需要作的事情有
1。根据纹理名字迅速找到纹理。
2。当显存不够使用的时候,而用户又要使用一张目前不在显存的纹理,那么需要选择一张纹理替换
3。将纹理按照长,宽,表面格式分组,相同的属于同一个组,这样做的目的是为了能够更好的实现(2)的功能
4。创建纹理时将图像数据载入纹理
在目前的实现中,功能(1)通过对字符串的哈希来实现,对于字符串的hash网上文章比较多,我使用的是Blizzard所使用的hash方法:
unsigned long GFTextureManger::HashString( const char *String, unsigned long dwHashType )
{
unsigned char *key = (unsigned char *)String;
unsigned long seed1 = 0x7FED7FED;
unsigned long seed2 = 0xEEEEEEEE;
int ch;
while( *key != 0 )
{
ch = toupper(*key++);
seed1 = m_CryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2);
seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
}
return seed1;
}
功能1实现非常简单,但是考虑到即将使用的LRU替换算法,我并没有使用Std的标准hash_map,而是自己做了一个可以同时Hash和双向连接的Double link hash map这样每当一张纹理被使用的时候可以从这个Link hash map中移动最前面,同时又不会影响hash,这样在链表中(double link hash map) 的最后一个节点就是Least Recently Used texture
为了实现功能2,我的初步计划是这样对于 长Width 高Height MipLevel 表面格式为FMT的所有纹理称为一个纹理组,所有在这个纹理组中的纹理都可以互相替换,也就是说如果需要使用一张纹理而这张纹理还没有被载入显存,同时没有足够的显存分配,那么只要找到这个纹理所在的纹理组,其中任何纹理的显存部分都可以被目前这张纹理所使用。为了实现上述功能定义了2个纯虚类:
class GFTexMemPart
{
public:
GFTexMemPart();
virtual ~GFTexMemPart();
GFFORMAT m_Format;
int m_Width;
int m_Height;
int m_MipLevels;
bool m_CreatedSucceded;
};
class GFTexVideoMemPart:public GFTexMemPart
{
public:
virtual ~GFTexVideoMemPart();
virtual unsigned int GetVideoMemSize() = 0;
};
class GFTexSysteMemPart:public GFTexMemPart
{
public:
virtual ~GFTexSysteMemPart();
virtual unsigned int GetSysMemSize() = 0;
};
在目前的D3D中的实现就是2个texture,一张在显存,一张在系统内存中。
/*Implementation Of D3D video Texture part*/
class GFD3DTexVideoMemPart:public GFTexVideoMemPart
{
public:
GFD3DTexVideoMemPart(int Width, int Height, int Levels, GFFORMAT Fmt , unsigned long MemSize);
~GFD3DTexVideoMemPart();
unsigned int GetVideoMemSize(){ return m_VideoMemSize;}
IDirect3DTexture9 * m_VideoMemTex;
protected:
bool CreateTexture( int Width, int Height, int Levels, GFFORMAT Fmt);
unsigned long m_VideoMemSize;
};
/*Implementation Of D3D system Texture part*/
class GFD3DTexSysteMemPart:public GFTexSysteMemPart
{
public:
GFD3DTexSysteMemPart(int Width, int Height, int Levels, GFFORMAT Fmt, unsigned long MemSize);
~GFD3DTexSysteMemPart();
unsigned int GetSysMemSize(){ return m_SysMemSize;}
IDirect3DTexture9 * m_SysMemTex;
protected:
bool CreateTexture( int Width, int Height, int Levels, GFFORMAT Fmt);
unsigned long m_SysMemSize;
};
这样一个Texture应该分别由 video 和system part组成。当显存不够分配的时候只需要从同组中的LRUtexture上取下它的video Memory part 并且挂到但前地Texture就可以了。对于一个组中的所有texture使用双向链表将他们连接起来每当一个Texture被使用以后就简单得将这个Texture方到该组的最前面这样就实现了LRU表,每当需要替换的时候只要取链表中最末尾的节点就是最least Recently Used Texture 了
.class GFTexture:public GFRefObject
{
public:
GFTexture();
virtual ~GFTexture() ;
virtual bool CreateTextureFromGfpFile( const TCHAR * gfpTexfile,GFTEXUSAGE Usage = GFTEXUSG_INVIDMEM) = 0;
virtual bool CreateTexture( int Width, int Height, int Levels, GFTEXUSAGE Usage, GFFORMAT Fmt) =0;
virtual bool LoadTexture( const TCHAR * gfpTexfile) = 0;
virtual void DestoryTexture() = 0;
virtual bool UpLoadToVideoMemory() = 0;
//Below Part is Designed for texureManagement
virtual GFTexVideoMemPart * GetVideoMemPart() =0;
virtual GFTexSysteMemPart * GetSysteMemPart() =0;
virtual GFTexVideoMemPart * DetachVideoPart() =0;
virtual GFTexSysteMemPart * DetachSystemPart() =0;
virtual void AttachVideoPart(GFTexVideoMemPart *) =0;
virtual void AttachSystemPart(GFTexVideoMemPart *) =0;
virtual long GetWidth() =0;
virtual long GetHeight() =0;
virtual int GetMipLevel() =0;
virtual GFFORMAT GetFormat() =0;
virtual unsigned int GetTexVidMemSize() = 0;//in Byte
const std::string & GetName(){ return m_Name;}
void SetName(const char * name){ m_Name = name;}
double GetLastUsedTime(){return m_LastUsedTime;}
void SetLastUsedTime(double Time){m_LastUsedTime = Time;}
void SetTextureGroup(GFTextureGroup * Group);
GFTextureGroup * GetTextureGroup();
protected:
std::string m_Name;
GFTextureGroup * m_TexGroup;
double m_LastUsedTime;//Get Last Time Texture has been used
};
以上基本的数据结构 当制订Texture替换策略时还需要考虑到其他组的Texture是否能够释放以供使用同时策略应该能够更具缺页略动态调整