//	Darkstar Engine  (c) UnderWorld Interactive Studio (all coding by Svarog    2007-2008)
//
//      Данный файл является частью Darkstar Engine. Любое использование кода файла
//      без согласия компании, является нарушением закона об авторском праве (в слу-
//      чае если не оговорено обратное).
//
//      Имя файла: render_state_manager_d3d10.cpp
//      Краткое описание: manager for render state block for directx 10
//

#ifdef GD_D3D10

#include <Darkstar/Graphic System/Render State/render_state_manager_d3d10.hpp>
#include <Darkstar/Graphic System/Direct3D/graphicDriver.hpp>
#include <Darkstar/math.hpp>
#include <Darkstar/Kernel/logh.hpp>

using gs::GraphicDriver;
using gs::RenderStateManager;
using gs::DRSCMPFUNC;
using gs::DBLEND;
using gs::DBLENDOP;
using gs::DCULL;
using gs::DFILLMODE;
using gs::DTEXADDRESS;
using gs::DTEXFILTER;
using ker::Log;
using gs::FillModeRenderState;
using gs::CullModeRenderState;
using gs::DepthRenderState;
using gs::SamplerRenderState;
using gs::ColorWriteRenderState;
using gs::BlendRenderState;
using gs::SAMPLER_TYPE;

RenderStateManager::RenderStateManager()
{
	for(unsigned int i = 0; i < d3d_state_blocks_num; ++i)
	{
		rasterizer_units[i].state = NULL;
		blend_units[i].state = NULL;
		depth_stencil_units[i].state = NULL;
		sampler_units[i].state = NULL;
	};

	ID3D10Device* device = GraphicDriver::ptr()->getDevice();
	// set default rasterizer
	new_cull_state.reset(new CullModeRenderState());
	new_fill_mode_state.reset(new FillModeRenderState());
	shared_ptr<D3D10_RASTERIZER_DESC> def_rasterizer = _getD3D10RasterizerStateFromCur();
	rasterizer_cur_index = _getHashCode(def_rasterizer);
	if(FAILED(device->CreateRasterizerState(def_rasterizer.get(), &rasterizer_units[rasterizer_cur_index].state)))
		lwar << "Cannot create default rasterizer state";
	device->RSSetState(rasterizer_units[rasterizer_cur_index].state);
	rasterizer_units[rasterizer_cur_index].desc = def_rasterizer;

	// set default blender
	new_blend_state.reset(new BlendRenderState());
	new_color_write_state.reset(new ColorWriteRenderState());
	shared_ptr<D3D10_BLEND_DESC> def_blend = _getD3D10BlendStateFromCur();
	blend_cur_index = _getHashCode(def_blend);
	if(FAILED(device->CreateBlendState(def_blend.get(), &blend_units[blend_cur_index].state)))
		lwar << "Cannot create default blend state block";
	device->OMSetBlendState(blend_units[blend_cur_index].state, 0, 0xffffffff);
	blend_units[blend_cur_index].desc = def_blend;

	// set default depth-stencil
	new_depth_state.reset(new DepthRenderState());
	shared_ptr<D3D10_DEPTH_STENCIL_DESC> def_depth_stencil_desc = _getD3D10DepthStencilStateFromCur();
	depth_stencil_cur_index = _getHashCode(def_depth_stencil_desc);
	if(FAILED(device->CreateDepthStencilState(def_depth_stencil_desc.get(), &depth_stencil_units[depth_stencil_cur_index].state)))
		lwar << "Cannot create default depth-stencil state";
	device->OMSetDepthStencilState(depth_stencil_units[depth_stencil_cur_index].state, 0);
	depth_stencil_units[depth_stencil_cur_index].desc = def_depth_stencil_desc;

	// set default sampler_state
	for(unsigned int i = 0; i < sampler_num; ++i)
	{
		new_vs_sampler_state[i].reset(new SamplerRenderState());
		new_ps_sampler_state[i].reset(new SamplerRenderState());
		new_gs_sampler_state[i].reset(new SamplerRenderState());
	};
	// all samplers in all slots are the same, so for example we can take VS_SAMPLER in slot 0
	shared_ptr<D3D10_SAMPLER_DESC> def_sampler_desc = _getD3D10SamplerStateFromCur(0, VS_SAMPLER);
	unsigned int cur_sampler_index = _getHashCode(def_sampler_desc);
	if(FAILED(device->CreateSamplerState(def_sampler_desc.get(), &sampler_units[cur_sampler_index].state)))
		lwar << "Cannot create default sampler state";
	sampler_units[cur_sampler_index].desc = def_sampler_desc;
	ID3D10SamplerState* samplers[sampler_num];
	for(unsigned int i = 0; i < sampler_num; ++i)
		samplers[i] = sampler_units[cur_sampler_index].state;
	for(unsigned int i = 0; i < sampler_num; ++i)
	{
		sampler_cur_gs_index[i] = cur_sampler_index;
		sampler_cur_vs_index[i] = cur_sampler_index;
		sampler_cur_ps_index[i] = cur_sampler_index;
	};
	device->VSSetSamplers(0, sampler_num, samplers);
	device->PSSetSamplers(0, sampler_num, samplers);
	device->GSSetSamplers(0, sampler_num, samplers);
};

RenderStateManager::~RenderStateManager()
{
	ID3D10Device* device = GraphicDriver::ptr()->getDevice();
	device->RSSetState(NULL);
	device->OMSetBlendState(NULL, 0, 0xffffffff);
	device->OMSetDepthStencilState(NULL, 0);
	ID3D10SamplerState* samplers[sampler_num];
	for(unsigned int i = 0; i < sampler_num; ++i)
		samplers[i] = NULL;
	device->VSSetSamplers(0, sampler_num, samplers);
	device->PSSetSamplers(0, sampler_num, samplers);
	device->GSSetSamplers(0, sampler_num, samplers);
	for(unsigned int i = 0; i < d3d_state_blocks_num; ++i)
	{
		if(rasterizer_units[i].state)
		{
			if(FAILED(rasterizer_units[i].state->Release()))
			{
				lwar << "Cannot release rasterizer state block with index: " << i;
			};
			rasterizer_units[i].state = NULL;
		};
		if(blend_units[i].state)
		{
			if(FAILED(blend_units[i].state->Release()))
			{
				lwar << "Cannot release blender state block with index: " << i;
			};
			blend_units[i].state = NULL;
		};
		if(depth_stencil_units[i].state)
		{
			if(FAILED(depth_stencil_units[i].state->Release()))
			{
				lwar << "Cannot release depth-stencil state block with index: " << i;
			};
			depth_stencil_units[i].state = NULL;
		};
		if(sampler_units[i].state)
		{
			if(FAILED(sampler_units[i].state->Release()))
			{
				lwar << "Cannot release sampler state block with index: " << i;
			};
			sampler_units[i].state = NULL;
		};
	};
};

// private functions

shared_ptr<D3D10_BLEND_DESC> RenderStateManager::_getD3D10BlendStateFromCur()
{
	shared_ptr<D3D10_BLEND_DESC> def_blend(new D3D10_BLEND_DESC);
	def_blend->AlphaToCoverageEnable = false;
	for(unsigned int i = 0; i < render_target_num; ++i)
	{
		def_blend->BlendEnable[i] = new_blend_state->getBlendEnable(i);
		def_blend->RenderTargetWriteMask[i] = new_color_write_state->getColorWriteFlag(i);
	};
	def_blend->BlendOp = (D3D10_BLEND_OP)new_blend_state->getBlendOp();
	def_blend->BlendOpAlpha = (D3D10_BLEND_OP)new_blend_state->getBlendOpAlpha();
	def_blend->DestBlend = (D3D10_BLEND)new_blend_state->getDstBlend();
	def_blend->DestBlendAlpha = (D3D10_BLEND)new_blend_state->getDstBlendAlpha();
	def_blend->SrcBlend = (D3D10_BLEND)new_blend_state->getSrcBlend();
	def_blend->SrcBlendAlpha = (D3D10_BLEND)new_blend_state->getSrcBlendAlpha();
	return def_blend;
};

shared_ptr<D3D10_DEPTH_STENCIL_DESC> RenderStateManager::_getD3D10DepthStencilStateFromCur()
{
	shared_ptr<D3D10_DEPTH_STENCIL_DESC> def_depth_stencil_desc(new D3D10_DEPTH_STENCIL_DESC);
	def_depth_stencil_desc->DepthEnable = new_depth_state->getIsDepthEnable();
	def_depth_stencil_desc->DepthWriteMask = (D3D10_DEPTH_WRITE_MASK)new_depth_state->getDepthWriteMask();
	def_depth_stencil_desc->DepthFunc = (D3D10_COMPARISON_FUNC)new_depth_state->getDepthFunction();
	def_depth_stencil_desc->StencilEnable = false;
	def_depth_stencil_desc->StencilReadMask = D3D10_DEFAULT_STENCIL_READ_MASK;
	def_depth_stencil_desc->StencilWriteMask = D3D10_DEFAULT_STENCIL_WRITE_MASK;
	def_depth_stencil_desc->FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;
	def_depth_stencil_desc->FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_KEEP;
	def_depth_stencil_desc->FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
	def_depth_stencil_desc->FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
	def_depth_stencil_desc->BackFace = def_depth_stencil_desc->FrontFace;
	return def_depth_stencil_desc;
};

shared_ptr<D3D10_RASTERIZER_DESC> RenderStateManager::_getD3D10RasterizerStateFromCur()
{
	shared_ptr<D3D10_RASTERIZER_DESC> def_rasterizer(new D3D10_RASTERIZER_DESC);
	def_rasterizer->AntialiasedLineEnable = false;
	def_rasterizer->CullMode = (D3D10_CULL_MODE)new_cull_state->getCullMode();
	def_rasterizer->DepthBias = 0;
	def_rasterizer->DepthBiasClamp = 0;
	def_rasterizer->DepthClipEnable = true;
	def_rasterizer->FillMode = (D3D10_FILL_MODE)new_fill_mode_state->getFillMode();
	def_rasterizer->FrontCounterClockwise = false;
	def_rasterizer->MultisampleEnable = true;
	def_rasterizer->ScissorEnable = false;
	def_rasterizer->SlopeScaledDepthBias = 0;
	return def_rasterizer;
};

shared_ptr<D3D10_SAMPLER_DESC> RenderStateManager::_getD3D10SamplerStateFromCur(const unsigned int sampler_index, const SAMPLER_TYPE& type)
{
	shared_ptr<SamplerRenderState> cur_sampler_state;
	switch(type)
	{
	case VS_SAMPLER:
		{
			cur_sampler_state = new_vs_sampler_state[sampler_index];
		};break;
	case PS_SAMPLER:
		{
			cur_sampler_state = new_ps_sampler_state[sampler_index];
		};break;
	case GS_SAMPLER:
		{
			cur_sampler_state = new_gs_sampler_state[sampler_index];
		};
	};
	shared_ptr<D3D10_SAMPLER_DESC> def_sampler_desc(new D3D10_SAMPLER_DESC);
	def_sampler_desc->Filter = (D3D10_FILTER)cur_sampler_state->getFilter();
	def_sampler_desc->AddressU = (D3D10_TEXTURE_ADDRESS_MODE)cur_sampler_state->getAddressU();
	def_sampler_desc->AddressV = (D3D10_TEXTURE_ADDRESS_MODE)cur_sampler_state->getAddressV();
	def_sampler_desc->AddressW = (D3D10_TEXTURE_ADDRESS_MODE)cur_sampler_state->getAddressW();
	def_sampler_desc->MipLODBias = cur_sampler_state->getMipLODBias();
	def_sampler_desc->MaxAnisotropy = cur_sampler_state->getMaxAnisotropy();
	def_sampler_desc->ComparisonFunc = D3D10_COMPARISON_NEVER;
	for(unsigned int i = 0; i < 4; ++i)
		def_sampler_desc->BorderColor[i] = 0;
	def_sampler_desc->MinLOD = 0;
	// in d3d 10 default value is D3D10_FLOAT32_MAX but this is bad for my hash-function
	def_sampler_desc->MaxLOD = 4000;
	return def_sampler_desc;
};

unsigned int _getHashCodeFromFloat(const float* values, const unsigned int num)
{
	unsigned int result = 0;
	// classic hash-function for string. We will be use it for structure
	for(unsigned int i = 0; i < num; ++i)
	{
		result = (unsigned int)((float)result*23.0f + values[i]);
		result %= gs::d3d_state_blocks_num;
	};
	return result;
};

unsigned int RenderStateManager::_getHashCode(const shared_ptr<D3D10_RASTERIZER_DESC>& desc)
{
	float values[10] = {(float)desc->AntialiasedLineEnable, (float)desc->CullMode, (float)desc->DepthBias,
		(float)desc->DepthClipEnable, desc->DepthBiasClamp, (float)desc->FillMode, (float)desc->FrontCounterClockwise,
		(float)desc->MultisampleEnable, (float)desc->ScissorEnable, desc->SlopeScaledDepthBias};
	return _getHashCodeFromFloat(values, 10);
};

unsigned int RenderStateManager::_getHashCode(const shared_ptr<D3D10_DEPTH_STENCIL_DESC>& desc)
{
	float values[14] = {(float)desc->BackFace.StencilDepthFailOp, (float)desc->BackFace.StencilFailOp,
		(float)desc->BackFace.StencilFunc, (float)desc->BackFace.StencilPassOp, (float)desc->DepthEnable,
		(float)desc->DepthFunc, (float)desc->DepthWriteMask, (float)desc->FrontFace.StencilDepthFailOp,
		(float)desc->FrontFace.StencilFailOp, (float)desc->FrontFace.StencilFunc, (float)desc->FrontFace.StencilPassOp,
		(float)desc->StencilEnable, (float)desc->StencilReadMask, (float)desc->StencilWriteMask};
	return _getHashCodeFromFloat(values, 14);
};

unsigned int RenderStateManager::_getHashCode(const shared_ptr<D3D10_BLEND_DESC>& desc)
{
	float values[23];
	for(unsigned int i = 0; i < 16; i += 2)
	{
		values[i] = (float)desc->BlendEnable[i];
		values[i+1] = (float)desc->RenderTargetWriteMask[i];
	};
	values[16] = (float)desc->AlphaToCoverageEnable;
	values[17] = (float)desc->BlendOp;
	values[18] = (float)desc->BlendOpAlpha;
	values[19] = (float)desc->DestBlend;
	values[20] = (float)desc->DestBlendAlpha;
	values[21] = (float)desc->SrcBlend;
	values[22] = (float)desc->SrcBlendAlpha;
	return _getHashCodeFromFloat(values, 23);
};

unsigned int RenderStateManager::_getHashCode(const shared_ptr<D3D10_SAMPLER_DESC>& desc)
{
	float values[13] = {(float)desc->AddressU, (float)desc->AddressV, (float)desc->AddressW,
		desc->BorderColor[0], desc->BorderColor[1], desc->BorderColor[2], desc->BorderColor[3],
		(float)desc->ComparisonFunc, (float)desc->Filter, (float)desc->MaxAnisotropy, 
		desc->MaxLOD, desc->MinLOD, desc->MipLODBias};
	return _getHashCodeFromFloat(values, 13);
};

// SAMPLER

void RenderStateManager::setSamplerRenderState(const SamplerRenderState& sampler_render_state, const unsigned int sampler_index, const SAMPLER_TYPE& type)
{
#ifdef _NOTFINAL
	if(sampler_index >= sampler_num)
	{
		lwar << "Try to set sampler with sampler_index greater or equal sampler_num";
		return;
	};
#endif
	_SetSampler new_s;
	switch(type)
	{
	case VS_SAMPLER:
		{
			new_vs_sampler_state[sampler_index].reset(new SamplerRenderState(sampler_render_state));
		};break;
	case PS_SAMPLER:
		{
			new_ps_sampler_state[sampler_index].reset(new SamplerRenderState(sampler_render_state));
		};break;
	case GS_SAMPLER:
		{
			new_gs_sampler_state[sampler_index].reset(new SamplerRenderState(sampler_render_state));
		};break;
	};
	new_s.index = sampler_index;
	new_s.type = type;
	_new_sampler.push_back(new_s);
};

const shared_ptr<SamplerRenderState>& RenderStateManager::getCurSamplerRenderState(const unsigned int sampler_index, const SAMPLER_TYPE& type) const
{
	unsigned int si = sampler_index;
#ifdef _NOTFINAL
	if(sampler_index >= sampler_num)
	{
		lwar << "Try to get sampler with sampler_index greater or equal sampler_num";
		si = 0;
	};
#endif
	switch(type)
	{
	case VS_SAMPLER:
		return new_vs_sampler_state[si];
	case PS_SAMPLER:
		return new_ps_sampler_state[si];
	case GS_SAMPLER:
		return new_gs_sampler_state[si];
	};
	// dummy return
	return new_vs_sampler_state[0];
};

bool compareBools(BOOL val1[8], BOOL val2[8])
{
	for(unsigned int i = 0; i < 8; ++i)
		if(val1[i] != val2[i])
			return false;
	return true;
};

bool compareUINT8(UINT8 val1[8], UINT8 val2[8])
{
	for(unsigned int i = 0; i < 8; ++i)
		if(val1[i] != val2[i])
			return false;
	return true;
};

void printSamplerState(const shared_ptr<D3D10_SAMPLER_DESC>& desc)
{
	lmes << "Filter: " << desc->Filter;
	lmes << "AddressU: " << desc->AddressU;
	lmes << "AddressV: " << desc->AddressV;
	lmes << "AddressW: " << desc->AddressW;
	lmes << "MipLODBias: " << desc->MipLODBias;
	lmes << "MaxAnisotropy: " << desc->MaxAnisotropy;
	lmes << "ComparisonFunc: " << desc->ComparisonFunc;
	lmes << "BorderColor: " << desc->BorderColor[0] << " " << desc->BorderColor[1] << " " << desc->BorderColor[2] << " " << desc->BorderColor[3];
	lmes << "MinLOD: " << desc->MinLOD;
	lmes << "MaxLOD: " << desc->MaxLOD;
};

void RenderStateManager::flush()
{
	// rasterizer set
	shared_ptr<D3D10_RASTERIZER_DESC> new_rasterizer = _getD3D10RasterizerStateFromCur();
	unsigned int hash_code = _getHashCode(new_rasterizer);
	RasterizerUnit old_rasterizer = rasterizer_units[hash_code];
	if(old_rasterizer.state)
	{
		shared_ptr<D3D10_RASTERIZER_DESC> old_rasterizer_desc = old_rasterizer.desc;
		if(new_rasterizer->AntialiasedLineEnable == old_rasterizer_desc->AntialiasedLineEnable &&
			new_rasterizer->CullMode == old_rasterizer_desc->CullMode &&
			new_rasterizer->DepthBias == old_rasterizer_desc->DepthBias &&
			math::float_equal(new_rasterizer->DepthBiasClamp, old_rasterizer_desc->DepthBiasClamp) &&
			new_rasterizer->DepthClipEnable == old_rasterizer_desc->DepthClipEnable &&
			new_rasterizer->FillMode == old_rasterizer_desc->FillMode &&
			new_rasterizer->FrontCounterClockwise == old_rasterizer_desc->FrontCounterClockwise &&
			new_rasterizer->MultisampleEnable == old_rasterizer_desc->MultisampleEnable &&
			new_rasterizer->ScissorEnable == old_rasterizer_desc->ScissorEnable &&
			math::float_equal(new_rasterizer->SlopeScaledDepthBias, old_rasterizer_desc->SlopeScaledDepthBias))
		{
			if(rasterizer_cur_index != hash_code)
				GraphicDriver::ptr()->getDevice()->RSSetState(old_rasterizer.state);
			// else old_rasterizer state has set already
			rasterizer_cur_index = hash_code;
		}
		else
		{
			if(FAILED(old_rasterizer.state->Release()))
			{
				lwar << "Cannot release rasterizer state block";
				return;
			};
			old_rasterizer.state = NULL;
		};
	};
	if(!old_rasterizer.state)
	{
		if(FAILED(GraphicDriver::ptr()->getDevice()->CreateRasterizerState(new_rasterizer.get(), &old_rasterizer.state)))
		{
			lwar << "Cannot create new rasterizer state block";
			return;
		};
		GraphicDriver::ptr()->getDevice()->RSSetState(old_rasterizer.state);
		rasterizer_units[hash_code].desc = new_rasterizer;
		rasterizer_units[hash_code].state = old_rasterizer.state;
		rasterizer_cur_index = hash_code;
	};

	// blender set
	shared_ptr<D3D10_BLEND_DESC> new_blender = _getD3D10BlendStateFromCur();
	hash_code = _getHashCode(new_blender);
	BlendUnit old_blender = blend_units[hash_code];
	if(old_blender.state)
	{
		shared_ptr<D3D10_BLEND_DESC> old_blender_desc = old_blender.desc;
		if(new_blender->AlphaToCoverageEnable == old_blender_desc->AlphaToCoverageEnable &&
			new_blender->BlendOp == old_blender_desc->BlendOp && new_blender->BlendOpAlpha == old_blender_desc->BlendOpAlpha &&
			new_blender->DestBlend == old_blender_desc->DestBlend && new_blender->DestBlendAlpha == old_blender_desc->DestBlendAlpha &&
			new_blender->SrcBlend == old_blender_desc->SrcBlend && new_blender->SrcBlendAlpha == old_blender_desc->SrcBlendAlpha &&
			compareBools(new_blender->BlendEnable, old_blender_desc->BlendEnable) && 
			compareUINT8(new_blender->RenderTargetWriteMask, old_blender_desc->RenderTargetWriteMask))
		{
			if(blend_cur_index != hash_code)
				GraphicDriver::ptr()->getDevice()->OMSetBlendState(old_blender.state, 0, 0xffffffff);
			// else blender cur index = hash_code and cur_desc = new_desc so we don't need to update state
			blend_cur_index = hash_code;
		}
		else
		{
			if(FAILED(old_blender.state->Release()))
			{
				lwar << "Cannot release old blender state";
				return;
			};
			old_blender.state = NULL;
		};
	};
	if(!old_blender.state)
	{
		if(FAILED(GraphicDriver::ptr()->getDevice()->CreateBlendState(new_blender.get(), &old_blender.state)))
		{
			lwar << "Cannot create new blend state block";
			return;
		};
		GraphicDriver::ptr()->getDevice()->OMSetBlendState(old_blender.state, 0, 0xffffffff);
		blend_units[hash_code].state = old_blender.state;
		blend_units[hash_code].desc = new_blender;
		blend_cur_index = hash_code;
	};

	// depth-stencil set
	shared_ptr<D3D10_DEPTH_STENCIL_DESC> new_depth_stencil = _getD3D10DepthStencilStateFromCur();
	hash_code = _getHashCode(new_depth_stencil);
	DepthStencilUnit old_depth_stencil = depth_stencil_units[hash_code];
	if(old_depth_stencil.state)
	{
		shared_ptr<D3D10_DEPTH_STENCIL_DESC> old_depth_stencil_desc = old_depth_stencil.desc;
		if(new_depth_stencil->BackFace.StencilDepthFailOp == old_depth_stencil_desc->BackFace.StencilDepthFailOp &&
			new_depth_stencil->BackFace.StencilFailOp == old_depth_stencil_desc->BackFace.StencilFailOp &&
			new_depth_stencil->BackFace.StencilFunc == old_depth_stencil_desc->BackFace.StencilFunc &&
			new_depth_stencil->BackFace.StencilPassOp == old_depth_stencil_desc->BackFace.StencilPassOp &&
			new_depth_stencil->FrontFace.StencilDepthFailOp == old_depth_stencil_desc->FrontFace.StencilDepthFailOp &&
			new_depth_stencil->FrontFace.StencilFailOp == old_depth_stencil_desc->FrontFace.StencilFailOp &&
			new_depth_stencil->FrontFace.StencilFunc == old_depth_stencil_desc->FrontFace.StencilFunc &&
			new_depth_stencil->FrontFace.StencilPassOp == old_depth_stencil_desc->FrontFace.StencilPassOp &&
			new_depth_stencil->DepthEnable == old_depth_stencil_desc->DepthEnable &&
			new_depth_stencil->DepthFunc == old_depth_stencil_desc->DepthFunc &&
			new_depth_stencil->DepthWriteMask == old_depth_stencil_desc->DepthWriteMask &&
			new_depth_stencil->StencilEnable == old_depth_stencil_desc->StencilEnable &&
			new_depth_stencil->StencilReadMask == old_depth_stencil_desc->StencilReadMask &&
			new_depth_stencil->StencilWriteMask == old_depth_stencil_desc->StencilWriteMask)
		{
			if(depth_stencil_cur_index != hash_code)
				GraphicDriver::ptr()->getDevice()->OMSetDepthStencilState(old_depth_stencil.state, 0);
			// else depth_stencil_cur_index == hash_code and old_depth_stencil_desc == new_depth_stencil_desc
			// so we don't need to update depth stencil state
				depth_stencil_cur_index = hash_code;
		}
		else
		{
			if(FAILED(old_depth_stencil.state->Release()))
			{
				lwar << "Cannot release depth-stencil state";
				return;
			};
			old_depth_stencil.state = NULL;
		};
	};
	if(!old_depth_stencil.state)
	{
		if(FAILED(GraphicDriver::ptr()->getDevice()->CreateDepthStencilState(new_depth_stencil.get(), &old_depth_stencil.state)))
		{
			lwar << "Cannot create new depth-stencil state";
			return;
		};
		GraphicDriver::ptr()->getDevice()->OMSetDepthStencilState(old_depth_stencil.state, 0);
		depth_stencil_units[hash_code].desc = new_depth_stencil;
		depth_stencil_units[hash_code].state = old_depth_stencil.state;
		depth_stencil_cur_index = hash_code;
	};

	// set sampler state
	for(unsigned int i = 0; i < _new_sampler.size(); ++i)
	{
		unsigned int cur_index = _new_sampler[i].index;
		SAMPLER_TYPE type = _new_sampler[i].type;
		shared_ptr<D3D10_SAMPLER_DESC> new_sampler = _getD3D10SamplerStateFromCur(cur_index, type);
		hash_code = _getHashCode(new_sampler);
		SamplerUnit old_sampler = sampler_units[hash_code];
		if(old_sampler.state)
		{
			shared_ptr<D3D10_SAMPLER_DESC> old_sampler_desc = old_sampler.desc;
			if(new_sampler->Filter == old_sampler_desc->Filter &&
				new_sampler->AddressU == old_sampler_desc->AddressU &&
				new_sampler->AddressV == old_sampler_desc->AddressV &&
				new_sampler->AddressW == old_sampler_desc->AddressW &&
				math::float_equal(new_sampler->MipLODBias, old_sampler_desc->MipLODBias) &&
				new_sampler->MaxAnisotropy == old_sampler_desc->MaxAnisotropy &&
				new_sampler->ComparisonFunc == old_sampler_desc->ComparisonFunc &&
				math::float_equal(new_sampler->BorderColor[0], old_sampler_desc->BorderColor[0]) &&
				math::float_equal(new_sampler->BorderColor[1], old_sampler_desc->BorderColor[1]) &&
				math::float_equal(new_sampler->BorderColor[2], old_sampler_desc->BorderColor[2]) &&
				math::float_equal(new_sampler->BorderColor[3], old_sampler_desc->BorderColor[3]) &&
				math::float_equal(new_sampler->MinLOD, old_sampler_desc->MinLOD) &&
				math::float_equal(new_sampler->MaxLOD, old_sampler_desc->MaxLOD))
			{
				switch(type)
				{
				case VS_SAMPLER:
					{
						if(hash_code != sampler_cur_vs_index[cur_index])
						{
							GraphicDriver::ptr()->getDevice()->VSSetSamplers(0, 1, &old_sampler.state);
						};
						sampler_cur_vs_index[cur_index] = hash_code;
					};break;
				case PS_SAMPLER:
					{
						if(hash_code != sampler_cur_ps_index[cur_index])
						{
							GraphicDriver::ptr()->getDevice()->PSSetSamplers(0, 1, &old_sampler.state);
						};
						sampler_cur_ps_index[cur_index] = hash_code;
					};break;
				case GS_SAMPLER:
					{
						if(hash_code != sampler_cur_gs_index[cur_index])
						{
							GraphicDriver::ptr()->getDevice()->GSSetSamplers(0, 1, &old_sampler.state);
						};
						sampler_cur_gs_index[cur_index] = hash_code;
					};break;
				};
			}
			else
			{
#ifdef _NOTFINAL
				for(unsigned int i = 0; i < sampler_num; ++i)
				{
					if(sampler_cur_vs_index[i] == hash_code || sampler_cur_ps_index[i] == hash_code 
						|| sampler_cur_gs_index[i] == hash_code)
					{
						lerr << "Bad collision between two samplers. Two samplers had the same hash codes and old samplers already set in another slot. Additional info:";
						lmes << "New sampler state information: ";
						printSamplerState(new_sampler);
						lmes << "Old sampler state information: ";
						printSamplerState(old_sampler.desc);
						lwar << "The new sampler will not be set";
						return;
					};
				};
#endif
				if(FAILED(old_sampler.state->Release()))
				{
					lwar << "Cannot release old sampler state block";
					return;
				};
				old_sampler.state = NULL;
			};
		};
		if(!old_sampler.state)
		{
			if(FAILED(GraphicDriver::ptr()->getDevice()->CreateSamplerState(new_sampler.get(), &old_sampler.state)))
			{
				lwar << "Cannot create new sampler state";
				return;
			};
			switch(type)
			{
			case VS_SAMPLER:
				{
					GraphicDriver::ptr()->getDevice()->VSSetSamplers(0, 1, &old_sampler.state);
					sampler_cur_vs_index[cur_index] = hash_code;
				};break;
			case PS_SAMPLER:
				{
					GraphicDriver::ptr()->getDevice()->PSSetSamplers(0, 1, &old_sampler.state);
					sampler_cur_ps_index[cur_index] = hash_code;
				};break;
			case GS_SAMPLER:
				{
					GraphicDriver::ptr()->getDevice()->GSSetSamplers(0, 1, &old_sampler.state);
					sampler_cur_gs_index[cur_index] = hash_code;
				};break;
			};
			old_sampler.desc = new_sampler;
		};
	};
	_new_sampler.clear();
};

#endif