手动材质(ManualTexture)拼合,RenderTarget宽高在不支持非2幂设备上的解决方案

(一)问题的产生:

部分设备,比如android的老款机型,并不支持非2幂宽高的尺寸。那么当我们申请一个手动材质(ManualTexture)时怎么办呢?

通常的解决方案一般是将申请的材质宽高做扩展让他等于一个大于等于宽高的最小2幂。如宽高为12x7将转化成16x8。这样我们解决了非2幂问题。

然而,我们得到了一个新问题,简单的扩展导致了存储的浪费,在宽高比较小的情况下,这种浪费可能还能接受,但是如果宽高为形如1080x1920将扩为2048x2048,有效利用率为k = (1080x1920) / (2048x2048) = 0.494384765625.而理论上,最低利用率为大于并趋近于0.5

 

(二)思路

我参考了cocos2d、ogre、cegui这三个能看到源码的库,发现作者们似乎并没有对此做特殊处理,而是采用了简单的扩容法。

事实上我们可以很容易想到将手动材质做成材质池,然后在池中分配需要的小材质。这通常可以这样设计:

  1. 能创建一张比较大的材质,通常它的宽高应该大于设备屏幕,而小于底层渲染api的最大材质参数。
  2. 通过一些标记来记录从池中分配小材质块。我们需要一个区域分配算法。
  3. 底层渲染api可以对大材质做部分更新,使小材质看起来行为和普通材质表现一致。

庆幸的是这些前提条件中的1和3都由显卡和渲染api提供了。我们只需要一个区域分配算法,然后把这个系统做出来。

 

(三)算法结构

由于我之前是在做CEGUI和Ogre结合层实现的这个模块,出于便利,在这里我使用Ogre的ManualTexture做例子。

 

先介绍我自己实现的区域分配算法,TexturePacker工具就应该有好些算法,你们也可以自行设计。

姐妹Block,由同一个区分裂而来的小于等于四个的子Block。

 

我的做法比较简单:

  • 有这几个关键模组Block,Page,Series,Manager。
  • 对于新分配的池宽高,我将其缓存在Page中,缓存一个max(w,h),并创建一个可用区为池宽高将其加入可用表。
  • 分配一个(w,h)的流程。
  • 回收一个Block流程。

分配流程:

  1. max(w,h)不包含(w,h) ,则分配失败。
  2. max(w,h)包含(w,h),则在可用表中选一个边缘权重(后面讨论权重选择),取出一个可用区。
  3. 将可用区进行分裂,分裂为四个区,将可用区分别横竖画一条线将矩形分为四个区,这横竖两条线分别为(0,h)(w,0).
  4. 将主的分裂区加入已用表移除可用表,将第一区加入已用表,将其余三个(如果宽高同时非0算一个,否则小于三个)区加入可用区。
  5. 将第一个区作为结果Block返回。

回收流程:

  1. 可用区加入Block,已用区移除Block。
  2. 如果这个Block的父亲存在,则检测这个Block的姐妹Block看看是否都已回收完全。
  3. 如果并未回收完全则回收完成。
  4. 如果姐妹Block已经回收完成,则进行姐妹区合并,将所有姐妹区从可用表移除,将父Block做回收迭代。

 

(四)附加结构

然后我们对于不同的手动材质属性,将拥有不同Page系列Series。

系列属性:

  • 深度(depth)
  • 纹理映射数(numMipmaps)
  • 纹理格式(format)
  • 用途(usage)

 

对于一个系列我们将拥有若干张Page,最后将系列组成最后的管理器Manager。

(五)权重设计

我们有两个权重,

  • 选区权重:一个在Page取可用区,我选的权重顺序是waste(浪费系数),area(区域面积),id(姐妹id编号).
  • 选页权重:一个在Series中取可用Page,我选的权重顺序是page_id(页码自增号)和pixel_idle(像素空余).

 

  1. 选区权重我们选小临界(选最小值)。
  2. waste(浪费系数)  姐妹Block中的3号(右下角Block)面积,我们希望分配以后浪费区更小,并能更加紧凑。
  3. area(区域面积) 长乘宽,我们尽量在小区间分配,腾出大空间。
  4. id(姐妹id编号). 为姐妹从父Block分裂由上到下左到右[0,3],我们希望总是希望使用小的姐妹Block,回收时加大合并概率。

 

(六)实例实现

注意:这个拼接方案在Ogre的“Direct3D11 Rendering Subsystem”下有问题,问题在于Ogre的Direct3D11渲染器在RenderSystem->clearFrameBuffer时,将会把整个RenderTarget清除,RenderSystem->_setViewport并不生效,导致不是本Block的RenderTarget渲染区域被清空,这个问题看起来像是引擎瑕疵。

TextureManager.h

/***********************************************************************
*  	created:    2019.5.26
* 	author:     mm_longcheng@icloud.com
************************************************************************
*/
/***************************************************************************
 *   Copyright (C) 2004 - 2009 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#ifndef __CEGUIOgreTextureManager_h__
#define __CEGUIOgreTextureManager_h__

#include "CEGUIOgreRenderer/Prerequisites.h"

#include <OgrePrerequisites.h>
#include <OgrePixelFormat.h>
#include <OgreTexture.h>

#include <vector>
#include <map>

 // Start of CEGUI namespace section
namespace CEGUI
{
	// Alloc most  at stack. not need special memory allocator.
	class CEGUIOGRE_GUIRENDERER_API CEGUIOgreTextureArea
	{
	public:
		Ogre::uint d_x;
		Ogre::uint d_y;
		Ogre::uint d_w;
		Ogre::uint d_h;
	public:
		CEGUIOgreTextureArea();
		~CEGUIOgreTextureArea();
	public:
		//! reset member.
		void reset();
	};

	class CEGUIOgreTexturePage;

	class CEGUIOGRE_GUIRENDERER_API CEGUIOgreTextureBlock : public Ogre::ArchiveAlloc
	{
	public:
		CEGUIOgreTextureArea d_area;
		Ogre::uint d_split_w;
		Ogre::uint d_split_h;
		Ogre::uint d_used;
		Ogre::uint d_id;
		CEGUIOgreTexturePage* d_page;
		CEGUIOgreTextureBlock* d_parent;
		CEGUIOgreTextureBlock* d_child[4];
	public:
		CEGUIOgreTextureBlock();
		virtual ~CEGUIOgreTextureBlock();
	public:
		//! assign page.
		void assign_page(CEGUIOgreTexturePage* _page);
		//! assign used.
		void assign_used(Ogre::uint _used);
		//! assign id.
		void assign_id(Ogre::uint _id);

		//! reset member.
		void reset();

		//! split sub block.
		void split(CEGUIOgreTextureArea _child[4], Ogre::uint _w, Ogre::uint _h);

		//! calculation texture block coord offset scale.
		void texture_coord_offset_scale(Ogre::Vector2* _scale);

		//! calculation texture block coord offset pixel.
		void texture_coord_offset_pixel(Ogre::Vector2* _pixel);

		//! calculation texture block size pixel.
		void texture_block_size_pixel(Ogre::Vector2* _size);

		//! get ogre texture.
		Ogre::TexturePtr get_ogre_texture();
	};

	// Alloc most  at stack. not need special memory allocator.
	class CEGUIOGRE_GUIRENDERER_API CEGUIOgreTextureBlockWeight
	{
	public:
		Ogre::uint64 d_waste;// dw * dh
		Ogre::uint64 d_area;// w * h
		Ogre::uint64 d_id;// id
	public:
		CEGUIOgreTextureBlockWeight();
		CEGUIOgreTextureBlockWeight(const CEGUIOgreTextureBlockWeight& rhs);

		//! operator=.
		CEGUIOgreTextureBlockWeight& operator=(const CEGUIOgreTextureBlockWeight& rhs);

		//! operator<.
		friend bool operator<(const CEGUIOgreTextureBlockWeight& lhs, const CEGUIOgreTextureBlockWeight& rhs);
		//! operator==.
		friend bool operator==(const CEGUIOgreTextureBlockWeight& lhs, const CEGUIOgreTextureBlockWeight& rhs);
	};

	class CEGUIOgreTextureSeries;

	class CEGUIOGRE_GUIRENDERER_API CEGUIOgreTexturePage : public Ogre::ArchiveAlloc
	{
	public:
		typedef std::set<CEGUIOgreTextureBlock*> block_set_type;
		typedef std::map<CEGUIOgreTextureBlockWeight, CEGUIOgreTextureBlock*> weights_map_type;
	public:
		Ogre::String d_name;

		Ogre::TexturePtr d_texture;

		CEGUIOgreTextureSeries* d_series;

		Ogre::uint32 d_page_id;

		Ogre::uint d_w;
		Ogre::uint d_h;

		// cache value for quick check.
		Ogre::uint d_max_w;
		Ogre::uint d_max_h;

		block_set_type d_area_pool;
		block_set_type d_area_used;
		block_set_type d_area_idle;
		block_set_type d_area_hide;

		// cache value.
		weights_map_type d_weights_map;

		Ogre::uint64 d_pixel_used;
		Ogre::uint64 d_pixel_idle;
	public:
		CEGUIOgreTexturePage();
		virtual ~CEGUIOgreTexturePage();
	public:
		//! assign (w, h) size.
		void assign_size(Ogre::uint w, Ogre::uint h);
		//! assign series.
		void assign_series(CEGUIOgreTextureSeries* _series);
		//! assign page_id.
		void assign_page_id(Ogre::uint32 _page_id);

		//! finish launching event.
		void on_finish_launching();
		//! before erminate event.
		void on_before_terminate();

		//! clear block area.
		void clear_area();

		//! block produce.
		CEGUIOgreTextureBlock* block_produce();
		//! block recycle.
		void block_recycle(CEGUIOgreTextureBlock* _block);

		//! update max (w ,h) cache.
		void update_max_cache();

		//! helper to generate unique texture names
		void generate_texture_name();

		//! block primary election select.
		CEGUIOgreTextureBlock* primary_election(Ogre::uint _w, Ogre::uint _h);

		//! empty return true.
		bool blankness();

		//! produce implement, not logger here.
		CEGUIOgreTextureBlock* produce_impl(Ogre::uint _w, Ogre::uint _h);
		//! recycle implement, not logger here.
		void recycle_impl(CEGUIOgreTextureBlock* _block);

		//! produce block for use.
		CEGUIOgreTextureBlock* produce(Ogre::uint _w, Ogre::uint _h);
		//! recycle block for use.
		void recycle(CEGUIOgreTextureBlock* _block);
	};

	// Alloc most  at stack. not need special memory allocator.
	class CEGUIOGRE_GUIRENDERER_API CEGUIOgreTexturePageWeight
	{
	public:
		Ogre::uint32 d_page_id;
		Ogre::uint64 d_pixel_idle;
	public:
		CEGUIOgreTexturePageWeight();
		CEGUIOgreTexturePageWeight(const CEGUIOgreTexturePageWeight& rhs);

		//! operator=.
		CEGUIOgreTexturePageWeight& operator=(const CEGUIOgreTexturePageWeight& rhs);

		//! operator<.
		friend bool operator<(const CEGUIOgreTexturePageWeight& lhs, const CEGUIOgreTexturePageWeight& rhs);
		//! operator==.
		friend bool operator==(const CEGUIOgreTexturePageWeight& lhs, const CEGUIOgreTexturePageWeight& rhs);
	};

	// Alloc most  at stack. not need special memory allocator.
	class CEGUIOGRE_GUIRENDERER_API CEGUIOgreTextureSeriesAttribute
	{
	public:
		Ogre::uint d_depth;
		int d_numMipmaps;
		Ogre::PixelFormat d_format;
		Ogre::TextureUsage d_usage;
	public:
		CEGUIOgreTextureSeriesAttribute();
		CEGUIOgreTextureSeriesAttribute(const CEGUIOgreTextureSeriesAttribute& rhs);

		//! operator=.
		CEGUIOgreTextureSeriesAttribute& operator=(const CEGUIOgreTextureSeriesAttribute& rhs);

		//! operator<.
		friend bool operator<(const CEGUIOgreTextureSeriesAttribute& lhs, const CEGUIOgreTextureSeriesAttribute& rhs);
		//! operator==.
		friend bool operator==(const CEGUIOgreTextureSeriesAttribute& lhs, const CEGUIOgreTextureSeriesAttribute& rhs);
	};

	class CEGUIOGRE_GUIRENDERER_API CEGUIOgreTextureSeries : public Ogre::ArchiveAlloc
	{
	public:
		typedef std::map<Ogre::uint32, CEGUIOgreTexturePage*> page_map_type;
		typedef std::map<CEGUIOgreTexturePageWeight, CEGUIOgreTexturePage*> weights_map_type;
	public:
		Ogre::String d_prefix_name;

		CEGUIOgreTextureSeriesAttribute d_attribute;

		Ogre::uint d_w;
		Ogre::uint d_h;
		page_map_type d_page_pool;

		// cache value.
		weights_map_type d_weights_map;

		Ogre::uint64 d_pixel_used;
		Ogre::uint64 d_pixel_idle;

		//! used for creating texture names.
		Ogre::uint32 d_page_counter;
	public:
		CEGUIOgreTextureSeries();
		virtual ~CEGUIOgreTextureSeries();
	public:
		//! assign prefix name.
		void assign_prefix_name(const Ogre::String& _prefix_name);
		//! assign (w, h) size.
		void assign_size(Ogre::uint w, Ogre::uint h);
		//! assign attribute.
		void assign_attribute(CEGUIOgreTextureSeriesAttribute* attribute);

		//! finish launching event.
		void on_finish_launching();
		//! before erminate event.
		void on_before_terminate();

		//! block primary election select.
		void primary_election(Ogre::uint _w, Ogre::uint _h);

		//! page produce for memery.
		CEGUIOgreTexturePage* page_produce();
		//! page recycle for memery.
		void page_recycle(CEGUIOgreTexturePage* _page);

		//! add page for size (w, h).
		CEGUIOgreTexturePage* add_page(Ogre::uint w, Ogre::uint h);
		//! rmv page.
		void rmv_page(CEGUIOgreTexturePage* _page);
		//! clear page.
		void clear_page();
		//! shrinkage memory.
		void shrinkage_memory();

		//! produce block for use.
		CEGUIOgreTextureBlock* produce(Ogre::uint _w, Ogre::uint _h);
		//! recycle block for use.
		void recycle(CEGUIOgreTextureBlock* _block);
		//! refresh block for use.
		CEGUIOgreTextureBlock* refresh(CEGUIOgreTextureBlock* _block, Ogre::uint _w, Ogre::uint _h);
	protected:
		//! recycle implement prefix.
		void recycle_impl_prefix(CEGUIOgreTextureBlock* _block, int* _trim);
		//! recycle implement suffix.
		void recycle_impl_suffix(CEGUIOgreTexturePage* _page_parent, int* _trim);
	};
	//! TextureManager
	class CEGUIOGRE_GUIRENDERER_API CEGUIOgreTextureManager : public Ogre::FactoryAlloc
	{
	public:
		typedef std::map<CEGUIOgreTextureSeriesAttribute, CEGUIOgreTextureSeries*> TextureSeriesMapType;
	public:
		Ogre::String d_name;

		Ogre::uint d_w;
		Ogre::uint d_h;

		Ogre::uint d_window_w;
		Ogre::uint d_window_h;

		Ogre::uint d_max_texture_size;
		Ogre::uint d_min_texture_size;

		Ogre::uint64 d_pixel_used;
		Ogre::uint64 d_pixel_idle;

		TextureSeriesMapType d_texture_series_map;
	public:
		// 2048 is default.
		static const Ogre::uint MAX_TEXTURE_SIZE;
		// 2048 is default.
		static const Ogre::uint MIN_TEXTURE_SIZE;
	public:
		CEGUIOgreTextureManager();
		virtual ~CEGUIOgreTextureManager();
	public:
		//! assign manager name.
		void assign_name(const Ogre::String& _name);
		//! assign (w, h) size.
		void assign_size(Ogre::uint w, Ogre::uint h);
		//! assign window (w, h) size.
		void assign_window_size(Ogre::uint w, Ogre::uint h);
		//! assign (w, h) max size.
		void assign_max_texture_size(Ogre::uint max_texture_size);
		//! assign (w, h) min size.
		void assign_min_texture_size(Ogre::uint mix_texture_size);
		//! update size.
		void update_size();

		//! finish launching event.
		void on_finish_launching();
		//! before erminate event.
		void on_before_terminate();

		//! shrinkage memory.
		void shrinkage_memory();

		//! series produce.
		CEGUIOgreTextureSeries* series_produce();
		//! series recycle.
		void series_recycle(CEGUIOgreTextureSeries* _series);

		//! add series.
		CEGUIOgreTextureSeries* add_series(CEGUIOgreTextureSeriesAttribute* attri);
		//! get series.
		CEGUIOgreTextureSeries* get_series(CEGUIOgreTextureSeriesAttribute* attri);
		//! get series instance, will add if not exist.
		CEGUIOgreTextureSeries* get_series_instance(CEGUIOgreTextureSeriesAttribute* attri);
		//! rmv series.
		void rmv_series(CEGUIOgreTextureSeriesAttribute* attri);
		//! clear series.
		void clear_series();

		//! produce underlying Ogre texture for cegui texture.
		CEGUIOgreTextureBlock* produce(
			Ogre::uint width,
			Ogre::uint height,
			Ogre::uint depth,
			int numMipmaps,
			Ogre::PixelFormat format,
			Ogre::TextureUsage usage);

		//! recycle underlying Ogre texture for cegui texture.
		void recycle(CEGUIOgreTextureBlock* block);

		//! refresh block for use.
		CEGUIOgreTextureBlock* refresh(CEGUIOgreTextureBlock* block, Ogre::uint _w, Ogre::uint _h);
	};
} // End of  CEGUI namespace section

#endif//__CEGUIOgreTextureManager_h__

TextureManager.cpp

/***********************************************************************
*  	created:    2019.5.26
* 	author:     mm_longcheng@icloud.com
************************************************************************
*/
/***************************************************************************
 *   Copyright (C) 2004 - 2011 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#include "CEGUIOgreRenderer/TextureManager.h"
#include "CEGUIOgreRenderer/Integer.h"
#include "CEGUIOgreRenderer/Resource.h"

#include <OgreTextureManager.h>
#include <OgreLogManager.h>
#include <OgreVector2.h>

 // Start of CEGUI namespace section
namespace CEGUI
{
	//----------------------------------------------------------------------------//
	CEGUIOgreTextureArea::CEGUIOgreTextureArea()
		: d_x(0)
		, d_y(0)
		, d_w(0)
		, d_h(0)
	{

	}
	CEGUIOgreTextureArea::~CEGUIOgreTextureArea()
	{

	}
	void CEGUIOgreTextureArea::reset()
	{
		this->d_x = 0;
		this->d_y = 0;
		this->d_w = 0;
		this->d_h = 0;
	}
	//----------------------------------------------------------------------------//
	CEGUIOgreTextureBlock::CEGUIOgreTextureBlock()
		: d_area()
		, d_split_w(0)
		, d_split_h(0)
		, d_used(0)
		, d_id(0)
		, d_page(NULL)
		, d_parent(NULL)
	{
		memset(this->d_child, 0, sizeof(CEGUIOgreTextureBlock*) * 4);
	}
	CEGUIOgreTextureBlock::~CEGUIOgreTextureBlock()
	{

	}
	void CEGUIOgreTextureBlock::assign_page(CEGUIOgreTexturePage* _page)
	{
		this->d_page = _page;
	}
	//! assign used.
	void CEGUIOgreTextureBlock::assign_used(Ogre::uint _used)
	{
		this->d_used = _used;
	}
	//! assign id.
	void CEGUIOgreTextureBlock::assign_id(Ogre::uint _id)
	{
		this->d_id = _id;
	}
	void CEGUIOgreTextureBlock::reset()
	{
		this->d_page = NULL;
		this->d_area.reset();
		this->d_split_w = 0;
		this->d_split_h = 0;
		this->d_used = 0;
		this->d_parent = NULL;
		memset(this->d_child, 0, sizeof(CEGUIOgreTextureBlock*) * 4);
	}
	void CEGUIOgreTextureBlock::split(CEGUIOgreTextureArea _child[4], Ogre::uint _w, Ogre::uint _h)
	{
		Ogre::uint p1_w = 0;
		Ogre::uint p1_h = 0;

		Ogre::uint p2_w = 0;
		Ogre::uint p2_h = 0;

		this->d_split_w = _w;
		this->d_split_h = _h;

		p1_w = this->d_area.d_x + this->d_split_w;
		p1_h = this->d_area.d_y + this->d_split_h;

		p2_w = this->d_area.d_w - this->d_split_w;
		p2_h = this->d_area.d_h - this->d_split_h;

		_child[0].d_x = this->d_area.d_x;
		_child[0].d_y = this->d_area.d_y;
		_child[0].d_w = this->d_split_w;
		_child[0].d_h = this->d_split_h;

		_child[1].d_x = p1_w;
		_child[1].d_y = this->d_area.d_y;
		_child[1].d_w = p2_w;
		_child[1].d_h = this->d_split_h;

		_child[2].d_x = this->d_area.d_x;
		_child[2].d_y = p1_h;
		_child[2].d_w = this->d_split_w;
		_child[2].d_h = p2_h;

		_child[3].d_x = p1_w;
		_child[3].d_y = p1_h;
		_child[3].d_w = p2_w;
		_child[3].d_h = p2_h;
	}
	void CEGUIOgreTextureBlock::texture_coord_offset_scale(Ogre::Vector2* _scale)
	{
		CEGUIOgreTexturePage* _page = NULL;

		_page = this->d_page;

		_scale->x = (Ogre::Real)this->d_area.d_x / (Ogre::Real)_page->d_w;
		_scale->y = (Ogre::Real)this->d_area.d_y / (Ogre::Real)_page->d_h;
	}
	void CEGUIOgreTextureBlock::texture_coord_offset_pixel(Ogre::Vector2* _pixel)
	{
		_pixel->x = (Ogre::Real)this->d_area.d_x;
		_pixel->y = (Ogre::Real)this->d_area.d_y;
	}
	void CEGUIOgreTextureBlock::texture_block_size_pixel(Ogre::Vector2* _size)
	{
		_size->x = (Ogre::Real)this->d_area.d_w;
		_size->y = (Ogre::Real)this->d_area.d_h;
	}
	Ogre::TexturePtr CEGUIOgreTextureBlock::get_ogre_texture()
	{
		CEGUIOgreTexturePage* _page = NULL;

		_page = this->d_page;

		return _page->d_texture;
	}
	//----------------------------------------------------------------------------//
	CEGUIOgreTextureBlockWeight::CEGUIOgreTextureBlockWeight()
		: d_waste(0)
		, d_area(0)
		, d_id(0)
	{

	}
	CEGUIOgreTextureBlockWeight::CEGUIOgreTextureBlockWeight(const CEGUIOgreTextureBlockWeight& rhs)
		: d_waste(rhs.d_waste)
		, d_area(rhs.d_area)
		, d_id(rhs.d_id)
	{

	}

	//! operator=.
	CEGUIOgreTextureBlockWeight& CEGUIOgreTextureBlockWeight::operator=(const CEGUIOgreTextureBlockWeight& rhs)
	{
		this->d_waste = rhs.d_waste;
		this->d_area = rhs.d_area;
		this->d_id = rhs.d_id;
		return *this;
	}

	//! operator<.
	bool operator<(const CEGUIOgreTextureBlockWeight& lhs, const CEGUIOgreTextureBlockWeight& rhs)
	{
		if (lhs.d_waste != rhs.d_waste) return lhs.d_waste < rhs.d_waste;
		if (lhs.d_area != rhs.d_area) return lhs.d_area < rhs.d_area;
		if (lhs.d_id != rhs.d_id) return lhs.d_id < rhs.d_id;
		return false;
	}
	//! operator==.
	bool operator==(const CEGUIOgreTextureBlockWeight& lhs, const CEGUIOgreTextureBlockWeight& rhs)
	{
		return
			lhs.d_waste == rhs.d_waste &&
			lhs.d_area == rhs.d_area &&
			lhs.d_id == rhs.d_id;
	}
	//----------------------------------------------------------------------------//
	CEGUIOgreTexturePage::CEGUIOgreTexturePage()
		: d_name("")

		, d_texture()

		, d_series(NULL)

		, d_page_id(0)

		, d_w(0)
		, d_h(0)

		, d_max_w(0)
		, d_max_h(0)

		, d_area_pool()
		, d_area_used()
		, d_area_idle()
		, d_area_hide()

		, d_weights_map()

		, d_pixel_used(0)
		, d_pixel_idle(0)
	{

	}
	CEGUIOgreTexturePage::~CEGUIOgreTexturePage()
	{
		assert(0 == this->d_area_pool.size() && "clear block before destroy.");
	}
	void CEGUIOgreTexturePage::assign_size(Ogre::uint w, Ogre::uint h)
	{
		this->d_w = w;
		this->d_h = h;
	}
	void CEGUIOgreTexturePage::assign_series(CEGUIOgreTextureSeries* _series)
	{
		this->d_series = _series;
	}
	void CEGUIOgreTexturePage::assign_page_id(Ogre::uint32 _page_id)
	{
		this->d_page_id = _page_id;
	}
	void CEGUIOgreTexturePage::on_finish_launching()
	{
		Ogre::TextureManager* _texture_manager = Ogre::TextureManager::getSingletonPtr();

		CEGUIOgreTextureSeriesAttribute* _attribute = &this->d_series->d_attribute;
		
		CEGUIOgreTextureBlock* _block = this->block_produce();
		_block->d_area.d_x = 0;
		_block->d_area.d_y = 0;
		_block->d_area.d_w = this->d_w;
		_block->d_area.d_h = this->d_h;
		this->d_area_pool.insert(_block);
		this->d_area_idle.insert(_block);

		this->d_max_w = this->d_w;
		this->d_max_h = this->d_h;

		this->d_pixel_used = 0;
		this->d_pixel_idle = this->d_w * this->d_h;
		
		this->generate_texture_name();

		this->d_texture = _texture_manager->createManual(
			this->d_name,
			Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
			Ogre::TEX_TYPE_2D,
			this->d_w,
			this->d_h,
			_attribute->d_depth,
			_attribute->d_numMipmaps,
			_attribute->d_format,
			_attribute->d_usage);

		{
			Ogre::LogManager* _LogManager = Ogre::LogManager::getSingletonPtr();
			std::string _message;
			char _string[64] = { 0 };

			sprintf(_string, "%ux%u", this->d_w, this->d_h);
			_message.assign("CEGUIOgreTexturePage::on_finish_launching ");
			_message.append(this->d_name);
			_message.append(" texture: ");
			_message.append(_string);
			_LogManager->logMessage(_message);
		}
	}
	void CEGUIOgreTexturePage::on_before_terminate()
	{
		Ogre::TextureManager* _texture_manager = Ogre::TextureManager::getSingletonPtr();

		CEGUIOgreResource* _CEGUIOgreResource = (CEGUIOgreResource*)this->d_texture.get();

		
		// although we can handling this situation, 
		// but it is mean some mistake at upper layer texture block life cycle.
		assert(0 == this->d_pixel_used && "you must free all texture block before terminate.");
		
		this->clear_area();
		
		this->d_pixel_used = 0;
		this->d_pixel_idle = 0;
		
		// Before we remove the texture, we reset the texture loader to NULL.
		// Sometimes when we remove the texture, but the instance will not dealloc immediately.
		// It's will crash, beause the Ogre::ManualResourceLoader is already dealloc.
		_CEGUIOgreResource->setLoader(NULL);

		_texture_manager->remove(this->d_texture);

		this->d_texture.reset();

		{
			Ogre::LogManager* _LogManager = Ogre::LogManager::getSingletonPtr();
			std::string _message;
			char _string[64] = { 0 };

			sprintf(_string, "%ux%u", this->d_w, this->d_h);
			_message.assign("CEGUIOgreTexturePage::on_before_terminate ");
			_message.append(this->d_name);
			_message.append(" texture: ");
			_message.append(_string);
			_LogManager->logMessage(_message);
		}
	}
	void CEGUIOgreTexturePage::clear_area()
	{
		CEGUIOgreTextureBlock* _block = NULL;

		block_set_type::iterator it;

		it = this->d_area_pool.begin();
		while (it != this->d_area_pool.end())
		{
			_block = (*it);
			this->d_area_pool.erase(it++);
			this->block_recycle(_block);
			_block = NULL;
		}

		this->d_area_used.clear();
		this->d_area_idle.clear();
		this->d_area_hide.clear();
	}
	CEGUIOgreTextureBlock* CEGUIOgreTexturePage::block_produce()
	{
		CEGUIOgreTextureBlock* _block = OGRE_NEW CEGUIOgreTextureBlock();
		_block->reset();
		_block->assign_page(this);
		return _block;
	}
	void CEGUIOgreTexturePage::block_recycle(CEGUIOgreTextureBlock* _block)
	{
		_block->reset();
		OGRE_DELETE _block;
	}
	void CEGUIOgreTexturePage::update_max_cache()
	{
		CEGUIOgreTextureBlock* _block = NULL;
		block_set_type::iterator it;

		this->d_max_w = 0;
		this->d_max_h = 0;

		for (it = this->d_area_idle.begin();
			it != this->d_area_idle.end(); it++)
		{
			_block = (*it);

			this->d_max_w = this->d_max_w > _block->d_area.d_w ? this->d_max_w : _block->d_area.d_w;
			this->d_max_h = this->d_max_h > _block->d_area.d_h ? this->d_max_h : _block->d_area.d_h;
		}
	}
	void CEGUIOgreTexturePage::generate_texture_name()
	{
		CEGUIOgreTextureSeriesAttribute* _attribute = &this->d_series->d_attribute;

		Ogre::uint _w = this->d_w;
		Ogre::uint _h = this->d_h;
		Ogre::uint _d = _attribute->d_depth;
		int _m = _attribute->d_numMipmaps;
		Ogre::uint _u = (Ogre::uint)_attribute->d_usage;

		const Ogre::String& _formatName = Ogre::PixelUtil::getFormatName(_attribute->d_format);

		char _attribute_string[128] = { 0 };
		sprintf(_attribute_string, "(%s,%ux%ux%u,%d %u)_%u", _formatName.c_str(), _w, _h, _d, _m, _u, this->d_page_id);

		this->d_name = "__";
		this->d_name.append(this->d_series->d_prefix_name);
		this->d_name.append("_page_tex_");
		this->d_name.append(_attribute_string);
	}
	CEGUIOgreTextureBlock* CEGUIOgreTexturePage::primary_election(Ogre::uint _w, Ogre::uint _h)
	{
		CEGUIOgreTextureBlock* _block_parent = NULL;
		CEGUIOgreTextureBlock* _block = NULL;

		CEGUIOgreTextureBlockWeight _weights;
		Ogre::uint _dw = 0;
		Ogre::uint _dh = 0;

		block_set_type::iterator it;

		this->d_weights_map.clear();

		for (it = this->d_area_idle.begin();
			it != this->d_area_idle.end(); it++)
		{
			_block = (*it);

			if (_w <= _block->d_area.d_w && _h <= _block->d_area.d_h)
			{
				// We will try to use blocks of similar size as the preferred one.
				_dw = _block->d_area.d_w - _w;
				_dh = _block->d_area.d_h - _h;

				_weights.d_waste = _dw * _dh;
				_weights.d_area = _block->d_area.d_w * _block->d_area.d_h;
				_weights.d_id = _block->d_id;
				this->d_weights_map[_weights] = _block;
			}
		}

		if (!this->d_weights_map.empty())
		{
			weights_map_type::iterator it_w;

			it_w = this->d_weights_map.begin();

			_block_parent = it_w->second;
		}

		this->d_weights_map.clear();

		return _block_parent;
	}
	bool CEGUIOgreTexturePage::blankness()
	{
		return 0 == this->d_pixel_used;
	}
	CEGUIOgreTextureBlock* CEGUIOgreTexturePage::produce_impl(Ogre::uint _w, Ogre::uint _h)
	{
		CEGUIOgreTextureBlock* _block = NULL;
		// quick check.
		if (_w <= this->d_max_w && _h <= this->d_max_h)
		{
			CEGUIOgreTextureBlock* _block_parent = NULL;

			_block_parent = this->primary_election(_w, _h);

			if (NULL != _block_parent)
			{
				CEGUIOgreTextureArea _child[4];
				size_t i = 0;

				Ogre::uint64 pixel = 0;

				memset(_child, 0, sizeof(CEGUIOgreTextureArea) * 4);

				_block_parent->split(_child, _w, _h);
				_block_parent->assign_used(1);

				// child[0] used.
				{
					CEGUIOgreTextureBlock* v = this->block_produce();
					this->d_area_pool.insert(v);
					v->d_area = _child[0];
					this->d_area_used.insert(v);
					//
					v->assign_used(1);
					v->assign_id(0);
					v->d_parent = _block_parent;
					_block_parent->d_child[0] = v;
					//
					_block = v;
				}
				// child[1, 3] idle.
				for (i = 1; i < 4; i++)
				{
					if (0 != _child[i].d_w && 0 != _child[i].d_h)
					{
						CEGUIOgreTextureBlock* v = this->block_produce();
						this->d_area_pool.insert(v);
						v->d_area = _child[i];
						this->d_area_idle.insert(v);
						//
						v->assign_used(0);
						v->assign_id(i);
						v->d_parent = _block_parent;
						_block_parent->d_child[i] = v;
					}
				}

				this->d_area_idle.erase(_block_parent);
				this->d_area_used.insert(_block_parent);
				this->d_area_hide.insert(_block_parent);

				pixel = _w * _h;
				this->d_pixel_used += pixel;
				this->d_pixel_idle -= pixel;

				this->update_max_cache();
			}
		}
		return _block;
	}
	void CEGUIOgreTexturePage::recycle_impl(CEGUIOgreTextureBlock* _block)
	{
		CEGUIOgreTextureBlock* _block_parent = NULL;
		Ogre::uint64 pixel = 0;
		//
		assert(NULL != _block && "_block is a invalid.");
		//
		this->d_area_idle.insert(_block);
		this->d_area_used.erase(_block);

		pixel = _block->d_area.d_w * _block->d_area.d_h;
		this->d_pixel_used -= pixel;
		this->d_pixel_idle += pixel;
		//
		this->update_max_cache();
		//
		_block_parent = _block->d_parent;

		_block->assign_used(0);

		if (NULL != _block_parent)
		{
			CEGUIOgreTextureBlock* _block_sibling = NULL;
			size_t i = 0;
			Ogre::uint _used = 0;

			for (i = 0; i < 4; i++)
			{
				_block_sibling = _block_parent->d_child[i];
				if (NULL != _block_sibling)
				{
					if (1 == _block_sibling->d_used)
					{
						_used = 1;
						break;
					}
				}
			}

			if (0 == _used)
			{
				CEGUIOgreTextureBlock* _block_sibling = NULL;
				size_t i = 0;

				for (i = 0; i < 4; i++)
				{
					_block_sibling = _block_parent->d_child[i];
					if (NULL != _block_sibling)
					{
						_block_sibling->d_parent = NULL;
						_block_parent->d_child[i] = NULL;
						//
						this->d_area_idle.erase(_block_sibling);
						this->d_area_pool.erase(_block_sibling);
						//
						this->block_recycle(_block_sibling);
						_block_sibling = NULL;
					}
				}

				_block_parent->d_split_w = 0;
				_block_parent->d_split_h = 0;

				this->d_area_hide.erase(_block_parent);

				// Avoid overlapping areas, calculated twice.
				pixel = _block_parent->d_area.d_w * _block_parent->d_area.d_h;
				this->d_pixel_used += pixel;
				this->d_pixel_idle -= pixel;

				this->recycle_impl(_block_parent);
			}
		}
	}

	CEGUIOgreTextureBlock* CEGUIOgreTexturePage::produce(Ogre::uint _w, Ogre::uint _h)
	{
		CEGUIOgreTextureBlock* _block = NULL;

		_block = this->produce_impl(_w, _h);

		if (NULL != _block)
		{
			Ogre::LogManager* _LogManager = Ogre::LogManager::getSingletonPtr();
			std::string _message;
			char _string[64] = { 0 };

			CEGUIOgreTextureArea* _area = &_block->d_area;

			sprintf(_string, "((%u,%u),(%u,%u))", _area->d_x, _area->d_y, _area->d_w, _area->d_h);
			_message.assign("CEGUIOgreTexturePage::produce ");
			_message.append(this->d_name);
			_message.append(" ");
			_message.append(_string);
			_LogManager->logMessage(_message);
		}

		return _block;
	}
	void CEGUIOgreTexturePage::recycle(CEGUIOgreTextureBlock* _block)
	{
		if (NULL != _block)
		{
			Ogre::LogManager* _LogManager = Ogre::LogManager::getSingletonPtr();
			std::string _message;
			char _string[64] = { 0 };

			CEGUIOgreTextureArea* _area = &_block->d_area;

			sprintf(_string, "((%u,%u),(%u,%u))", _area->d_x, _area->d_y, _area->d_w, _area->d_h);
			_message.assign("CEGUIOgreTexturePage::recycle ");
			_message.append(this->d_name);
			_message.append(" ");
			_message.append(_string);
			_LogManager->logMessage(_message);
		}

		this->recycle_impl(_block);
	}
	//----------------------------------------------------------------------------//
	CEGUIOgreTexturePageWeight::CEGUIOgreTexturePageWeight()
		: d_page_id(0)
		, d_pixel_idle(0)
	{

	}
	CEGUIOgreTexturePageWeight::CEGUIOgreTexturePageWeight(const CEGUIOgreTexturePageWeight& rhs)
		: d_page_id(rhs.d_page_id)
		, d_pixel_idle(rhs.d_pixel_idle)
	{

	}

	//! operator=.
	CEGUIOgreTexturePageWeight& CEGUIOgreTexturePageWeight::operator=(const CEGUIOgreTexturePageWeight& rhs)
	{
		this->d_page_id = rhs.d_page_id;
		this->d_pixel_idle = rhs.d_pixel_idle;
		return *this;
	}

	//! operator<.
	bool operator<(const CEGUIOgreTexturePageWeight& lhs, const CEGUIOgreTexturePageWeight& rhs)
	{
		if (lhs.d_page_id != rhs.d_page_id) return lhs.d_page_id < rhs.d_page_id;
		if (lhs.d_pixel_idle != rhs.d_pixel_idle) return lhs.d_pixel_idle < rhs.d_pixel_idle;
		return false;
	}
	//! operator==.
	bool operator==(const CEGUIOgreTexturePageWeight& lhs, const CEGUIOgreTexturePageWeight& rhs)
	{
		return
			lhs.d_page_id == rhs.d_page_id &&
			lhs.d_pixel_idle == rhs.d_pixel_idle;
	}
	//----------------------------------------------------------------------------//
	CEGUIOgreTextureSeriesAttribute::CEGUIOgreTextureSeriesAttribute()
		: d_depth(0)
		, d_numMipmaps(0)
		, d_format(Ogre::PF_UNKNOWN)
		, d_usage(Ogre::TU_DEFAULT)
	{

	}
	CEGUIOgreTextureSeriesAttribute::CEGUIOgreTextureSeriesAttribute(const CEGUIOgreTextureSeriesAttribute& rhs)
		: d_depth(rhs.d_depth)
		, d_numMipmaps(rhs.d_numMipmaps)
		, d_format(rhs.d_format)
		, d_usage(rhs.d_usage)
	{

	}
	CEGUIOgreTextureSeriesAttribute& CEGUIOgreTextureSeriesAttribute::operator=(const CEGUIOgreTextureSeriesAttribute& rhs)
	{
		this->d_depth = rhs.d_depth;
		this->d_numMipmaps = rhs.d_numMipmaps;
		this->d_format = rhs.d_format;
		this->d_usage = rhs.d_usage;
		return *this;
	}
	bool operator<(const CEGUIOgreTextureSeriesAttribute& lhs, const CEGUIOgreTextureSeriesAttribute& rhs)
	{
		if (lhs.d_depth != rhs.d_depth) return lhs.d_depth < rhs.d_depth;
		if (lhs.d_numMipmaps != rhs.d_numMipmaps) return lhs.d_numMipmaps < rhs.d_numMipmaps;
		if (lhs.d_format != rhs.d_format) return lhs.d_format < rhs.d_format;
		if (lhs.d_usage != rhs.d_usage) return lhs.d_usage < rhs.d_usage;
		return false;
	}
	bool operator==(const CEGUIOgreTextureSeriesAttribute& lhs, const CEGUIOgreTextureSeriesAttribute& rhs)
	{
		return
			lhs.d_depth == rhs.d_depth &&
			lhs.d_numMipmaps == rhs.d_numMipmaps &&
			lhs.d_format == rhs.d_format &&
			lhs.d_usage == rhs.d_usage;
	}
	//----------------------------------------------------------------------------//
	CEGUIOgreTextureSeries::CEGUIOgreTextureSeries()
		: d_prefix_name("series")
		, d_attribute()
		, d_w(0)
		, d_h(0)

		, d_page_pool()
		, d_weights_map()

		, d_pixel_used()
		, d_pixel_idle()

		, d_page_counter(0)
	{

	}
	CEGUIOgreTextureSeries::~CEGUIOgreTextureSeries()
	{
		assert(0 == this->d_page_pool.size() && "clear page before destroy.");
	}
	void CEGUIOgreTextureSeries::assign_prefix_name(const Ogre::String& _prefix_name)
	{
		this->d_prefix_name = _prefix_name;
	}
	void CEGUIOgreTextureSeries::assign_size(Ogre::uint w, Ogre::uint h)
	{
		this->d_w = w;
		this->d_h = h;
	}
	void CEGUIOgreTextureSeries::assign_attribute(CEGUIOgreTextureSeriesAttribute* attribute)
	{
		this->d_attribute = *attribute;
	}
	void CEGUIOgreTextureSeries::on_finish_launching()
	{

	}
	void CEGUIOgreTextureSeries::on_before_terminate()
	{
		this->clear_page();
	}
	void CEGUIOgreTextureSeries::primary_election(Ogre::uint _w, Ogre::uint _h)
	{
		CEGUIOgreTexturePage* _page = NULL;

		CEGUIOgreTexturePageWeight _weights;

		page_map_type::iterator it;

		this->d_weights_map.clear();

		for (it = this->d_page_pool.begin();
			it != this->d_page_pool.end(); it++)
		{
			_page = it->second;

			if (_page->d_max_w >= _w && _page->d_max_h >= _h)
			{
				// we use the page id for weight.
				// We want the block to go as close as possible to the start page.
				// Because the initial page may have some blocks that will not be released for a long time, 
				// and the new blocks that are added later are more dynamic and fragmented.
				_weights.d_page_id = _page->d_page_id;
				_weights.d_pixel_idle = _page->d_pixel_idle;
				this->d_weights_map[_weights] = _page;
			}
		}
	}
	CEGUIOgreTexturePage* CEGUIOgreTextureSeries::page_produce()
	{
		CEGUIOgreTexturePage* _page = OGRE_NEW CEGUIOgreTexturePage();
		return _page;
	}
	void CEGUIOgreTextureSeries::page_recycle(CEGUIOgreTexturePage* _page)
	{
		OGRE_DELETE _page;
	}
	CEGUIOgreTexturePage* CEGUIOgreTextureSeries::add_page(Ogre::uint w, Ogre::uint h)
	{
		CEGUIOgreTexturePage* _page = NULL;

		Ogre::uint _w = w;
		Ogre::uint _h = h;

		CEGUIOgre_roundup32(_w);
		CEGUIOgre_roundup32(_h);

		_w = _w > this->d_w ? _w : this->d_w;
		_h = _h > this->d_h ? _h : this->d_h;

		_page = this->page_produce();
		_page->assign_size(_w, _h);
		_page->assign_series(this);
		_page->assign_page_id(this->d_page_counter++);
		_page->on_finish_launching();
		this->d_page_pool.insert(page_map_type::value_type(_page->d_page_id, _page));

		this->d_pixel_idle += _page->d_pixel_idle;

		return _page;
	}
	void CEGUIOgreTextureSeries::rmv_page(CEGUIOgreTexturePage* _page)
	{
		this->d_pixel_idle -= _page->d_pixel_idle;

		this->d_page_pool.erase(_page->d_page_id);
		_page->on_before_terminate();
		this->page_recycle(_page);
	}
	void CEGUIOgreTextureSeries::clear_page()
	{
		CEGUIOgreTexturePage* _page = NULL;

		page_map_type::iterator it;

		it = this->d_page_pool.begin();
		while (it != this->d_page_pool.end())
		{
			_page = it->second;
			this->d_pixel_idle -= _page->d_pixel_idle;
			this->d_page_pool.erase(it++);
			_page->on_before_terminate();
			this->page_recycle(_page);
			_page = NULL;
		}
	}
	void CEGUIOgreTextureSeries::shrinkage_memory()
	{
		CEGUIOgreTexturePage* _page_parent = NULL;

		page_map_type::reverse_iterator it;

		it = this->d_page_pool.rbegin();

		// chack the last page.
		if (it!= this->d_page_pool.rend())
		{
			_page_parent = it->second;

			// we will remove the last page at recycle process find the last two page blankness.
			// here we need remove only one page.
			if (_page_parent->blankness())
			{
				this->rmv_page(_page_parent);
			}
		}
	}
	CEGUIOgreTextureBlock* CEGUIOgreTextureSeries::produce(Ogre::uint _w, Ogre::uint _h)
	{
		CEGUIOgreTextureBlock* _block = NULL;
		CEGUIOgreTexturePage* _page_parent = NULL;

		weights_map_type::iterator it;

		Ogre::uint64 pixel = 0;

		this->primary_election(_w, _h);

		for (it = this->d_weights_map.begin();
			it != this->d_weights_map.end(); it++)
		{
			_page_parent = it->second;

			_block = _page_parent->produce(_w, _h);
			if (NULL != _block)
			{
				// ok we produce block success.
				break;
			}
		}

		this->d_weights_map.clear();

		if (NULL == _block)
		{
			// if current page can not produce a block, we just add page for it.
			_page_parent = this->add_page(_w, _h);
			_block = _page_parent->produce(_w, _h);
		}

		if (NULL == _block)
		{
			Ogre::LogManager* _LogManager = Ogre::LogManager::getSingletonPtr();
			std::string _message;
			char _string[64] = { 0 };

			sprintf(_string, "%ux%u", this->d_w, this->d_h);
			_message.assign("CEGUIOgreTextureSeries::produce ");
			_message.append(_string);
			_message.append(" failure. Not enough system memory.");
			_LogManager->logError(_message);
		}
		else
		{
			pixel = _block->d_area.d_w * _block->d_area.d_h;
			this->d_pixel_idle -= pixel;
			this->d_pixel_used += pixel;
		}

		return _block;
	}
	void CEGUIOgreTextureSeries::recycle(CEGUIOgreTextureBlock* _block)
	{
		CEGUIOgreTexturePage* _page_parent = _block->d_page;

		int _trim = 0;

		this->recycle_impl_prefix(_block, &_trim);

		_page_parent->recycle(_block);

		this->recycle_impl_suffix(_page_parent, &_trim);
	}
	CEGUIOgreTextureBlock* CEGUIOgreTextureSeries::refresh(CEGUIOgreTextureBlock* _block, Ogre::uint _w, Ogre::uint _h)
	{
		CEGUIOgreTexturePage* _page_parent = _block->d_page;

		int _trim = 0;

		CEGUIOgreTextureBlock* _new_block = NULL;

		this->recycle_impl_prefix(_block, &_trim);

		_page_parent->recycle(_block);

		_new_block = this->produce(_w, _h);

		this->recycle_impl_suffix(_page_parent, &_trim);

		return _new_block;
	}
	void CEGUIOgreTextureSeries::recycle_impl_prefix(CEGUIOgreTextureBlock* _block, int* _trim)
	{
		CEGUIOgreTexturePage* _page_parent = _block->d_page;
		CEGUIOgreTexturePage* _prev_parent = NULL;

		Ogre::uint32 _page_id = _page_parent->d_page_id;

		Ogre::uint64 pixel = 0;

		// the reverse iterator will pointer the _page_id iterator prev element.
		page_map_type::reverse_iterator rit(this->d_page_pool.find(_page_id));

		pixel = _block->d_area.d_w * _block->d_area.d_h;
		this->d_pixel_idle += pixel;
		this->d_pixel_used -= pixel;

		if (rit != this->d_page_pool.rend())
		{
			_prev_parent = rit->second;

			(*_trim) = _prev_parent->blankness() ? 1 : 0;
		}
		else
		{
			// We keep an empty leaf to ensure that critical allocations 
			// are not allocated and destroyed frequently
			(*_trim) = 0;
		}
	}
	void CEGUIOgreTextureSeries::recycle_impl_suffix(CEGUIOgreTexturePage* _page_parent, int* _trim)
	{
		if (_page_parent->blankness() && 1 == (*_trim))
		{
			// the last two page is blankness.
			// we remove the last one for shrinkage memory.
			this->rmv_page(_page_parent);
		}
	}
	//----------------------------------------------------------------------------//
	const Ogre::uint CEGUIOgreTextureManager::MAX_TEXTURE_SIZE = 2048;
	const Ogre::uint CEGUIOgreTextureManager::MIN_TEXTURE_SIZE = 2048;
	//----------------------------------------------------------------------------//
	CEGUIOgreTextureManager::CEGUIOgreTextureManager()
		: d_name("mgr")

		, d_w(0)
		, d_h(0)

		, d_window_w(1920)
		, d_window_h(1080)

		, d_max_texture_size(MAX_TEXTURE_SIZE)
		, d_min_texture_size(MIN_TEXTURE_SIZE)

		, d_pixel_used(0)
		, d_pixel_idle(0)

		, d_texture_series_map()
	{

	}
	CEGUIOgreTextureManager::~CEGUIOgreTextureManager()
	{
		assert(0 == this->d_texture_series_map.size() && "clear series before destroy.");
	}
	void CEGUIOgreTextureManager::assign_name(const Ogre::String& _name)
	{
		this->d_name = _name;
	}
	void CEGUIOgreTextureManager::assign_size(Ogre::uint w, Ogre::uint h)
	{
		this->d_w = w;
		this->d_h = h;
	}
	void CEGUIOgreTextureManager::assign_window_size(Ogre::uint w, Ogre::uint h)
	{
		this->d_window_w = w;
		this->d_window_h = h;
	}
	void CEGUIOgreTextureManager::assign_max_texture_size(Ogre::uint max_texture_size)
	{
		assert(CEGUIOgre_is_power_of_2(max_texture_size) && "max_texture_size must power of 2.");
		this->d_max_texture_size = max_texture_size;
	}
	void CEGUIOgreTextureManager::assign_min_texture_size(Ogre::uint min_texture_size)
	{
		assert(CEGUIOgre_is_power_of_2(min_texture_size) && "min_texture_size must power of 2.");
		this->d_min_texture_size = min_texture_size;
	}
	void CEGUIOgreTextureManager::update_size()
	{
		Ogre::uint pot_window_w = this->d_window_w;
		Ogre::uint pot_window_h = this->d_window_h;

		Ogre::uint pot_window_sz = 0;

		assert(this->d_min_texture_size <= this->d_max_texture_size && "invalid for limit texture_size.");

		CEGUIOgre_roundup32(pot_window_w);
		CEGUIOgre_roundup32(pot_window_h);

		pot_window_sz = pot_window_w > pot_window_h ? pot_window_w : pot_window_h;

		pot_window_sz = this->d_max_texture_size < pot_window_sz ? this->d_max_texture_size : pot_window_sz;
		pot_window_sz = this->d_min_texture_size > pot_window_sz ? this->d_min_texture_size : pot_window_sz;

		this->d_w = pot_window_sz;
		this->d_h = pot_window_sz;
	}
	//! finish launching event.
	void CEGUIOgreTextureManager::on_finish_launching()
	{
		this->update_size();
	}
	//! before erminate event.
	void CEGUIOgreTextureManager::on_before_terminate()
	{
		this->clear_series();
	}
	void CEGUIOgreTextureManager::shrinkage_memory()
	{
		CEGUIOgreTextureSeries* e = NULL;
		TextureSeriesMapType::iterator it;
		it = this->d_texture_series_map.begin();
		while (it != this->d_texture_series_map.end())
		{
			e = it->second;
			e->shrinkage_memory();
		}
	}
	//! series produce.
	CEGUIOgreTextureSeries* CEGUIOgreTextureManager::series_produce()
	{
		CEGUIOgreTextureSeries* _series = OGRE_NEW CEGUIOgreTextureSeries();
		return _series;
	}
	//! series recycle.
	void CEGUIOgreTextureManager::series_recycle(CEGUIOgreTextureSeries* _series)
	{
		OGRE_DELETE _series;
	}
	//! add series.
	CEGUIOgreTextureSeries* CEGUIOgreTextureManager::add_series(CEGUIOgreTextureSeriesAttribute* attri)
	{
		CEGUIOgreTextureSeries* e = NULL;
		e = this->get_series(attri);
		if (NULL == e)
		{
			e = this->series_produce();
			e->assign_prefix_name(this->d_name);
			e->assign_size(this->d_w, this->d_h);
			e->assign_attribute(attri);
			e->on_finish_launching();
			this->d_texture_series_map.insert(TextureSeriesMapType::value_type(*attri, e));
		}
		return e;
	}
	//! get series.
	CEGUIOgreTextureSeries* CEGUIOgreTextureManager::get_series(CEGUIOgreTextureSeriesAttribute* attri)
	{
		CEGUIOgreTextureSeries* e = NULL;
		TextureSeriesMapType::iterator it;
		it = this->d_texture_series_map.find(*attri);
		if (it != this->d_texture_series_map.end())
		{
			e = it->second;
		}
		return e;
	}
	//! get series instance, will add if not exist.
	CEGUIOgreTextureSeries* CEGUIOgreTextureManager::get_series_instance(CEGUIOgreTextureSeriesAttribute* attri)
	{
		CEGUIOgreTextureSeries* e = NULL;
		e = this->get_series(attri);
		if (NULL == e)
		{
			e = this->add_series(attri);
		}
		return e;
	}
	//! rmv series.
	void CEGUIOgreTextureManager::rmv_series(CEGUIOgreTextureSeriesAttribute* attri)
	{
		CEGUIOgreTextureSeries* e = NULL;
		TextureSeriesMapType::iterator it;
		it = this->d_texture_series_map.find(*attri);
		if (it != this->d_texture_series_map.end())
		{
			e = it->second;
			e->on_before_terminate();
			this->d_texture_series_map.erase(it);
			this->series_recycle(e);
		}
	}
	//! clear series.
	void CEGUIOgreTextureManager::clear_series()
	{
		CEGUIOgreTextureSeries* e = NULL;
		TextureSeriesMapType::iterator it;
		it = this->d_texture_series_map.begin();
		while (it != this->d_texture_series_map.end())
		{
			e = it->second;
			e->on_before_terminate();
			this->d_texture_series_map.erase(it++);
			this->series_recycle(e);
		}
	}
	//----------------------------------------------------------------------------//
	CEGUIOgreTextureBlock* CEGUIOgreTextureManager::produce(
		Ogre::uint width,
		Ogre::uint height,
		Ogre::uint depth,
		int numMipmaps,
		Ogre::PixelFormat format,
		Ogre::TextureUsage usage)
	{
		CEGUIOgreTextureSeriesAttribute attri;

		CEGUIOgreTextureSeries* _series = NULL;

		attri.d_depth = depth;
		attri.d_numMipmaps = numMipmaps;
		attri.d_format = format;
		attri.d_usage = usage;

		_series = this->get_series_instance(&attri);

		return _series->produce(width, height);
	}

	void CEGUIOgreTextureManager::recycle(CEGUIOgreTextureBlock* block)
	{
		CEGUIOgreTextureSeries* _series = NULL;
		CEGUIOgreTexturePage* _page = NULL;

		_page = block->d_page;
		_series = _page->d_series;

		_series->recycle(block);
	}
	CEGUIOgreTextureBlock* CEGUIOgreTextureManager::refresh(CEGUIOgreTextureBlock* block, Ogre::uint _w, Ogre::uint _h)
	{
		CEGUIOgreTextureSeries* _series = NULL;
		CEGUIOgreTexturePage* _page = NULL;

		_page = block->d_page;
		_series = _page->d_series;

		return _series->refresh(block, _w, _h);
	}
	//----------------------------------------------------------------------------//

} // End of  CEGUI namespace section

//----------------------------------------------------------------------------//
// Implementation of template base class

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值