描画キャッシュ(3)

思いついた。こんなのはどうだろう。

class CacheObject{
public:
    virtual ~CacheObject(){}
    virtual void Draw() = 0;
    virtual int GetPriority() = 0;
    virtual uint32 GetSize() = 0;
};

class DrawCache{
public:
    static DrawCache* Create( uint32 cacheSize , uint32 cacheNum ){
        DrawCache* cache = new DrawCache( cacheSize , cacheNum );
        if( cache == null ) return null;
        
        if( cache->_cache == null || cache->_pCache == null ){
            Release( cache );
        }
        
        return cache;
    }
    
    static void Release( DrawCache*& cache ){
        if( cache != null ){
            delete cache;
            cache = null;
        }
    }
    
private:
    DrawCache( uint32 cacheSize , uint32 cacheNum ) :
        _maxSize( cacheSize ) , _maxNum( cacheNum ) ,
        _cache( new byte[ cacheSize ] ) , _pObj( new CacheObject*[ cacheNum ] ) ,
        _size( 0 ) , _count( 0 ){
    }
    
    ~DrawCache(){
        if( _cache != null ){
            delete _cache;
        }
        if( _pCache != null ){
            delete _pCache;
        }
    }
    
public:
    void Draw( CacheObject& obj ){
        int size = obj.GetSize();
        if( _size + size <= _maxSize && _count < _maxNum ){
            memcpy( &_cache[ _size ] , &obj , size );
            _pObj[ _count ] = (CacheObject*)&_cache[ _size ];
            
            _size += size;
            _count++;
        }
    }
    
    // プライオリティに基づいてソート
    void sort();
    
    void Flush(){
        sort();
        for( int i = 0 ; i < _count ; i++ ){
            _pObj[ i ]->Draw();
        }
        _size = 0;
        _count = 0;
    }
    
private:
    byte*           _cache;
    uint32          _size;
    uint32          _maxSize;
    CacheObject**   _pObj;
    uint32          _count;
    uint32          _maxNum;
};
class DrawImageCacheObject : public CacheObject{
public:
    DrawImageCacheObject( Image dst , int dx , int dy , Image src , int priority ){
        _dst        = dst;
        _dx         = dx;
        _dy         = dy;
        _src        = src;
        _priority   = priority;
    }
    
    virtual void Draw(){
        Graphics::DrawImage( _dst , _dx , _dy , _src );
    }
    
    virtual int GetPriority(){
        return _priority;
    }
    
    virtual uint32 GetSize(){
        return sizeof( DrawImageCacheObject );
    }
private:
    Image       _dst;
    int         _dx;
    int         _dy;
    Image       _src;
    int         _priority;
};


class FillRectCacheObject : public CacheObject{
public:
    FillRectCacheObject( Image dst , int dx , int dy , int dw , int dh ,
                            uint32 color , int priority ){
        _dst        = dst;
        _dx         = dx;
        _dy         = dy;
        _dw         = dw;
        _dh         = dh;
        _color      = color;
        _priority   = priority;
    }
    
    virtual void Draw(){
        Graphics::FillRect( _dst , _dx , _dy , _dw , _dh , _color );
    }
    
    virtual int GetPriority(){
        return _priority;
    }
    
    virtual uint32 GetSize(){
        return sizeof( FillRectCacheObject );
    }
private:
    Image       _dst;
    int         _dx;
    int         _dy;
    int         _dw;
    int         _dh;
    uint32      _color;
    int         _priority;
};

使い方は、

DrawCache* cache = DrawCache::Create( 5 * 1024 , 100 );
if( cache == null ) return;

キャッシュオブジェクトを生成して、

cache->Draw( DrawImageCacheObject( dst , dx , dy , src , priority ) );
cache->Draw( FillRectCacheObject( dst , dx , dy , dw , dh , color , priority ) );

cache->Flush();

キャッシュと Flush をして、

DrawCache::Release( cache );

最後に解放。


キャッシュオブジェクトを格納するための領域は、DrawCache::Create() で生成するときに指定してやる。
こうすることによって2段階初期化をする必要が無くなるし、_cache や _pCache にアクセスする際の null チェックを省くことが出来るようになる。
で、Create() の内部で new をしているので、解放はこちら側で責任を持って解放する。
まあ、これは今回のキャッシュの問題ではないのでどうやってもいい。


で、キャッシュオブジェクトの実体をライブラリ側に持たせるときに問題になるのが、キャッシュオブジェクトのサイズなのだ。
これがわからなければ、ライブラリ側で格納することが出来ない。
なので、キャッシュオブジェクトのサイズの取得は仮想関数にして、そのサイズ分だけ格納していく。
こうすればキャッシュオブジェクトの実体をライブラリ側に持たせることが可能になる。


まあ、DrawCache を生成するためにキャッシュオブジェクトを格納するための領域と、キャッシュする数の両方を指定しなければならないのが面倒なのと、CacheObject::GetSize() を毎回実装するのが面倒で、しかも返す値を間違えるとかなりやばいことになる。
もう少し何とかならないかなぁ……。




というかこれ、memcpy() でオブジェクトをコピーしてるから、キャッシュオブジェクトを生成するときに、コピーコンストラクタで何らかの動作をしているオブジェクト(スマートポインタとか)を引数に取るとやばい。


まだまだ改善が必要っぽいなぁ……。