本文讨论优化多个进程在共用gfx嵌入字体的过程.
flash做界面有着天然的优势.随着gfx的发展也看到越来越多的游戏使用gfx做界面工作.
本项目组使用gfx也有几年时间了,这次接了个棋牌项目,很自然的就想使用flash作为界面.
与客户端项目不同的是单个棋牌游戏必须保持"身材瘦小".都能保持在5M以下是比较满意的.而且启动速度越快越好.
gfx使用的字体方案分为两种.
一种是系统字体.Windows字体或FT.
一种是嵌入字体即将整个字体库嵌入到swf文件当中,一个字体大概就在5M左右.粗体,斜体都算另一种字体.
gfx有着自己的字体渲染系统,在使用系统字体时会有明显的锯齿.对于一些应用来说是不可接受的.
但使用嵌入字体一是大小增加了近20M.共用文件又使游戏间产生了依赖关系.二是启动时读取一个大文件会影响启动速度.
于是想到使用共用内存来解决依赖关系并减少IO读取,提高启动速度.
Windows下合适的方案就是文件映射了.由于不存在写操作.所以不需要同步.整个过程非常简单.
fontfilemaping.h
#pragma once
#include <Windows.h>
#define FONTLIBMOVIE "fonts_cn.swf"
//为gfx本地化字库做内存共享
class FontFileMaping
{
public:
FontFileMaping();
~FontFileMaping();
bool CreateFontFileMaping( void );
void OpenFontFileMaping( void );
protected:
unsigned char* m_zData;
int m_iFileSize;
HANDLE hFontMap; //字体文件映射句柄
};
fontfilemaping.cpp
#include "stdafx.h"
#include "FontFileMaping.h"
#define FONTMAPINGNAME "gfxfontfile"
#define FONTSIZEMAPINGNAME "gfxfontfilesize"
FontFileMaping::FontFileMaping()
{
m_zData = NULL;
m_iFileSize = 0;
hFontMap = NULL;
}
FontFileMaping::~FontFileMaping()
{
if ( m_zData )
{
UnmapViewOfFile( m_zData );
m_zData = NULL;
}
if ( hFontMap )
{
CloseHandle( hFontMap );
hFontMap = NULL;
}
}
bool FontFileMaping::CreateFontFileMaping( void )
{
HANDLE fh;
fh = ::CreateFileA( FONTLIBMOVIE, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
m_iFileSize = GetFileSize( fh, NULL );
hFontMap = CreateFileMappingA( fh, NULL, PAGE_READONLY, 0, m_iFileSize, FONTMAPINGNAME );
if ( hFontMap == 0 )
{
int iEr = GetLastError();
printf( "Can not Create File Maping. error Code : %d", iEr );
//assert( 0 );
return false;
}
printf( "Create File Maping Success" );
MapViewOfFile( hFontMap, PAGE_READONLY, 0, 0, 0 );
CloseHandle( fh );
HANDLE hMapSize = CreateFileMappingA( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, FONTSIZEMAPINGNAME ); //用来传递文件大小.
if ( hMapSize == 0 )
{
int iEr = GetLastError();
assert( 0 );
return false;
}
int* lpData = (int*)MapViewOfFile( hMapSize, FILE_MAP_ALL_ACCESS, 0, 0, 1024 );
lpData[0] = m_iFileSize;
return true;
}
void FontFileMaping::OpenFontFileMaping( void )
{
hFontMap = OpenFileMappingA( FILE_MAP_READ, false, FONTMAPINGNAME );
m_zData = (unsigned char*)MapViewOfFile( hFontMap, FILE_MAP_READ, 0, 0, 0 );
HANDLE hMapSize = OpenFileMappingA( FILE_MAP_READ, false, FONTSIZEMAPINGNAME );
int* lpSizeData = (int*)MapViewOfFile( hMapSize, FILE_MAP_READ, 0, 0, 1024 );
m_iFileSize = lpSizeData[0];
}
gfx提供了现成的读写已在内存中的文件的类GMemoryFile.
ShareFontFileOpener.h
#pragma once
#include "..\FontFileMaping.h"
class ShareFontFileOpener : public GFxFileOpener, public FontFileMaping
{
public:
ShareFontFileOpener( void );
~ShareFontFileOpener( void );
virtual GFile* OpenFile(const char* purl,
SInt flags = GFileConstants::Open_Read|GFileConstants::Open_Buffered,
SInt mode = GFileConstants::Mode_ReadWrite);
inline virtual SInt64 GetFileModifyTime(const char* purl)
{
return 0;
}
inline virtual GFile* OpenFileEx(const char* purl, class GFxLog *plog,
SInt flags = GFileConstants::Open_Read|GFileConstants::Open_Buffered,
SInt mode = GFileConstants::Mode_ReadWrite)
{
return OpenFile( purl );
}
};
ShareFontFileOpener.cpp
#include "stdafx.h"
#include "ShareFontFileOpener.h"
ShareFontFileOpener::ShareFontFileOpener( void )
{
OpenFontFileMaping();
}
ShareFontFileOpener::~ShareFontFileOpener()
{
}
GFile* ShareFontFileOpener::OpenFile(const char* purl, SInt flags, SInt mode)
{
if ( strcmp( purl, FONTLIBMOVIE ) == 0 )
{
GMemoryFile* pMemFile = new GMemoryFile( purl, m_zData, m_iFileSize );
return pMemFile;
}
return GFxFileOpener::OpenFile( purl, flags, mode );
}
GPtr<GFxFileOpener> pfileOpener = *new ShareFontFileOpener;
loader.SetFileOpener(pfileOpener);
一切OK.对于项目应用来说.在大厅启动时CreateFontFileMaping.考虑到可能启动多个大厅的情况.则创建失败后仅输出log并返回.
游戏启动时则共用同一个字体文件.