本文讨论优化多个进程在共用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并返回.
游戏启动时则共用同一个字体文件.