#include "DeferredShadowSMTiled.h"
#include "DirectionalLight.h"
#include "ShadowApp.h"

#include "..\DXFunctions\FontManager.h"
#include "..\GameGUI\MenuManager.h"
#include "..\GameGUI\Menu.h"
#include "..\GameGUI\Panel.h"
#include "..\GameGUI\Label.h"
#include "..\GameGUI\Slider.h"
#include "..\GameGUI\CheckBox.h"
#include "..\GameGUI\RadioButton.h"
#include "..\GameGUI\ComboBox.h"

static const int g_ReqResDownSampleSizeCnt = 6;
static const int g_ReqResDownSampleSizes[g_ReqResDownSampleSizeCnt] = {200, 256, 320, 370, 420, 512};

DeferredShadowSMTiled::DeferredShadowSMTiled(ShadowApp* const pApp) : DeferredShadowTechnique(pApp)
{
	m_FXSMTiled = m_pApp->GetDevice()->GetShaderManager()->CreateFX("Shaders\\SMTiledShadow.fx");

	m_Tiling = -1;
	m_Xi = 0;
	m_DownSampleReqRes = true;
	m_ReqResDownSampleSize = -1;
	m_UseAvg = false;

	m_RTTileResX_x4 = NULL;
	m_RTTileResY_x4 = NULL;

	m_RTTileResX = NULL;
	m_RTTileResY = NULL;

	m_pSMTGP = NULL;
	
	m_RequiredRes = m_pApp->GetDevice()->GetViewPort()->CreateViewPortRenderTexture(1, FMT_A8R8G8B8);
	m_RequiredResDownSampled = NULL;
	SetReqResDownSampleSize(320);
	
	SetTiling(6); //2^6 = 64	64*4 = maximum because of 256 is max for argb8

	D3DVERTEXELEMENT9 declSample[] =
    {
        { 0, 0, D3DDECLTYPE_SHORT2 , D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
        D3DDECL_END()
    };
	m_VDeclSample = new VertexDeclaration(m_pApp->GetDevice(), declSample);

	m_VBRes = 0;
	m_VBSamples = NULL;

	CreateMenu();
}

DeferredShadowSMTiled::~DeferredShadowSMTiled(void)
{
	SAFERELEASE(m_FXSMTiled);

	SAFERELEASE(m_RTTileResX_x4);
	SAFERELEASE(m_RTTileResY_x4);

	SAFERELEASE(m_RTTileResX);
	SAFERELEASE(m_RTTileResY);

	SAFERELEASE(m_RequiredRes);
	SAFERELEASE(m_RequiredResDownSampled);

	SAFERELEASE(m_VBSamples);

	delete m_VDeclSample;

	if (m_pSMTGP)
	{
		for (unsigned i = 0; i <= m_Tiling; i++)
			delete [] m_pSMTGP[i];
		delete [] m_pSMTGP;
	}
}

void DeferredShadowSMTiled::SetTiling(const unsigned tiling)
{
	if (m_Tiling != tiling)
	{
		if (m_pSMTGP)
		{
			for (unsigned i = 0; i <= m_Tiling; i++)
				delete [] m_pSMTGP[i];
			delete [] m_pSMTGP;
		}

		m_Tiling = tiling;

		m_pSMTGP = new float*[m_Tiling + 1];

		int s = 1;
		for (unsigned i = 0; i <= m_Tiling; i++)
		{
			m_pSMTGP[i] = new float[s * 2]; //*2 because we store x and y res
			s <<= 2; //*2*2
		}

		CreateRenderTargets();
	}
}

void DeferredShadowSMTiled::SetReqResDownSampleSize(const unsigned size)
{
	if (m_ReqResDownSampleSize != size)
	{
		m_ReqResDownSampleSize = size;

		SAFERELEASE(m_RequiredResDownSampled);
		//m_RequiredResDownSampled = m_pApp->GetDevice()->GetTextureManager()->CreateRenderTexture(m_ReqResDownSampleSize, m_ReqResDownSampleSize, FMT_A8R8G8B8);
		m_RequiredResDownSampled = m_pApp->GetDevice()->GetTextureManager()->CreateOffscreenRenderTexture(m_ReqResDownSampleSize, m_ReqResDownSampleSize, FMT_A8R8G8B8, false, false, false);
	}
}

void DeferredShadowSMTiled::CreateRenderTargets(void)
{
	SAFERELEASE(m_RTTileResX_x4);
	SAFERELEASE(m_RTTileResY_x4);

	SAFERELEASE(m_RTTileResX);
	SAFERELEASE(m_RTTileResY);

	unsigned tilecount = 1 << m_Tiling;

	m_RTTileResX_x4 = m_pApp->GetDevice()->GetTextureManager()->CreateRenderTexture(tilecount * 4, tilecount * 4, FMT_R32F);
	m_RTTileResX_x4->CreateDepthStencil(FMT_D24X8);
	m_RTTileResY_x4 = m_pApp->GetDevice()->GetTextureManager()->CreateRenderTexture(tilecount * 4, tilecount * 4, FMT_R32F);
	m_RTTileResY_x4->CreateDepthStencil(FMT_D24X8);
	
	m_RTTileResX = m_pApp->GetDevice()->GetTextureManager()->CreateOffscreenRenderTexture(tilecount, tilecount, FMT_R32F, true, false, true);
	m_RTTileResX->GetGPUSurface()->CreateDepthStencil(FMT_D24X8);
	m_RTTileResY = m_pApp->GetDevice()->GetTextureManager()->CreateOffscreenRenderTexture(tilecount, tilecount, FMT_R32F, true, false, true);
	m_RTTileResY->GetGPUSurface()->CreateDepthStencil(FMT_D24X8);
}

void DeferredShadowSMTiled::RenderShadowMask(void)
{
	if (!m_FXSMTiled->IsCompiled())
		return;

	m_FXSMTiled->SetMatrix("g_matInvProj", DXMatrix::InvertMatrix(m_pApp->GetActiveCam().GetProjMatrix()));
	m_FXSMTiled->SetMatrix("g_matInvView", DXMatrix::InvertMatrix(m_pApp->GetActiveCam().GetViewMatrix()));
	m_FXSMTiled->SetMatrix("g_matInvViewProj",  DXMatrix::InvertMatrix(m_pApp->GetActiveCam().GetViewProjMatrix()));
	m_FXSMTiled->SetMatrix("g_matView", m_pApp->GetActiveCam().GetViewMatrix()); 

	m_FXSMTiled->SetTexture("g_texNormal", m_pApp->m_DSNormal->GetSurface());
	m_FXSMTiled->SetTexture("g_texDiffuse", m_pApp->m_DSDiffuse->GetSurface());

	if (m_pApp->GetWorldPosType() == WPT_WORLDPOS)
		m_FXSMTiled->SetTexture("g_texWorld", m_pApp->m_DSWorld->GetSurface());
	else
		m_FXSMTiled->SetTexture("g_texDepth", m_pApp->m_DSDepth->GetSurface());

	if (m_pApp->GetUseIDBuffer())
		m_FXSMTiled->SetTexture("g_texID", m_pApp->m_DSID->GetSurface());

	m_FXSMTiled->SetFloat("SM_ShadowMapSize", m_pApp->m_SM->GetShadowMapSize());
	m_FXSMTiled->SetFloat("SM_DetphBias", m_pApp->GetConstBias());
	m_FXSMTiled->SetFloat("SM_SlopeBias", m_pApp->GetSlopeBias());
	m_FXSMTiled->SetMatrix("SM_matSM", m_pApp->m_SM->GetSMMatrix());
	m_FXSMTiled->SetTexture("SM_ShadowMap", m_pApp->GetShadowMapping()->GetShadowMap());
	m_FXSMTiled->SetTexture("SM_IDBuffer", m_pApp->m_SM->GetIDBuffer());

	float rtsize[2] = {m_pApp->GetDevice()->GetViewPort()->GetWidth(), m_pApp->GetDevice()->GetViewPort()->GetHeight()};
	m_FXSMTiled->SetFloatArray("g_RTSize", rtsize, 2);
	m_FXSMTiled->SetFloat("g_RTIntermediateSize", 4 * (1 << m_Tiling));

	m_FXSMTiled->SetFloatArray("g_LightDir", m_pApp->GetActiveScene()->GetLight()->GetDirection(), 3);
	
	RenderRequiredRes();

	if(m_pApp->m_VisType == VIS_REQRES)
		return;

	if (m_DownSampleReqRes)
		m_FXSMTiled->SetTexture("g_texRequiredRes", m_RequiredResDownSampled->GetGPUSurface()->GetSurface());
	else
		m_FXSMTiled->SetTexture("g_texRequiredRes", m_RequiredRes->GetSurface());
	
	if (0)
		CalculateTileResCPU();
	else
		CalculateTileResGPU();

	BuildPyramid(0, 0, 0);

//	float x0 = m_pSMTGP[0][0];
//	float y0 = m_pSMTGP[0][1];

//	x0 == y0;

	//m_pApp->GetActiveScene()->GetLight()->RenderShadowMap();

	//HRESULT hr;
	//LPDIRECT3DSURFACE9 surf;

	//m_pApp->GetShadowMapping()->GetShadowMap()->GetTextureSurface()->GetSurfaceLevel(0, &surf);
	//hr = D3DXSaveSurfaceToFile("000sm.png", D3DXIFF_PNG, surf, NULL, NULL);
	//surf->Release();

	m_matLigthProj = m_pApp->GetShadowMapping()->GetLightProj();
	m_matInvSMTex = DXMatrix::InvertMatrix(m_pApp->GetShadowMapping()->GetSMTexMatrix());

	if(m_pApp->m_BenchType == BT_CONVERGENCE && m_pApp->m_RenderRef)
		m_pApp->m_DSShadowRef->StartRender(0);
	else
		m_pApp->m_DSShadow[0]->StartRender(0);

	m_pApp->GetDevice()->Clear(D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 0, 0);

	//wlog("xxx");
	ProcessPyramid(0, 0, 0);

	if(m_pApp->m_BenchType == BT_CONVERGENCE && m_pApp->m_RenderRef)
		m_pApp->m_DSShadowRef->EndRender();
	else
		m_pApp->m_DSShadow[0]->EndRender();
}

void DeferredShadowSMTiled::ProcessPyramid(const unsigned x, const unsigned y, const unsigned level)
{
	int reqxres = 1 << (int)m_pSMTGP[level][x + y * (1<<level)];
	int reqyres = 1 << (int)m_pSMTGP[level][(1<<level) * (1<<level) + x + y * (1<<level)];
	
	int curres = m_pApp->GetSMSize() << (level + m_Xi); //additionally shitf by quality parameter

	if (reqxres <= 1 || reqyres <= 1)
		return;

	if (reqxres <= curres && reqyres <= curres || level == m_Tiling) //render shadowmap for this tile
	//if (level == 0) 
	{
		RenderTileShadow(x, y, 1, 1, level);
		//RenderTileShadow(0, 0, 2, 2, 1);
	}
	else
	{
		int subx = x << 1;
		int suby = y << 1;
		int sublevel = level + 1;

		bool rendertile[4] = {false, false, false, false};

		//try to combine 2 subtiles 
		int subreqxres[4], subreqyres[4];

		subreqxres[0] = 1 << (int)m_pSMTGP[sublevel][subx + suby * (1<<sublevel)];
		subreqyres[0] = 1 << (int)m_pSMTGP[sublevel][subx + suby * (1<<sublevel) + (1<<sublevel) * (1<<sublevel)];
		subreqxres[1] = 1 << (int)m_pSMTGP[sublevel][subx + 1 + suby * (1<<sublevel)];
		subreqyres[1] = 1 << (int)m_pSMTGP[sublevel][subx + 1 + suby * (1<<sublevel) + (1<<sublevel) * (1<<sublevel)];
		subreqxres[2] = 1 << (int)m_pSMTGP[sublevel][subx + (suby + 1) * (1<<sublevel)];
		subreqyres[2] = 1 << (int)m_pSMTGP[sublevel][subx + (suby + 1) * (1<<sublevel) + (1<<sublevel) * (1<<sublevel)];
		subreqxres[3] = 1 << (int)m_pSMTGP[sublevel][subx + 1 + (suby + 1) * (1<<sublevel)];
		subreqyres[3] = 1 << (int)m_pSMTGP[sublevel][subx + 1 + (suby + 1) * (1<<sublevel) + (1<<sublevel) * (1<<sublevel)];

		for (int i = 0; i < 4; i++)
		{
			if (subreqxres[i] <= 1 || subreqyres[i] <= 1)
				rendertile[i] = true;
		}

		//tile 0 && 1 -> yres doubles
		if (!rendertile[0] && !rendertile[1] && subreqxres[0] <= curres && subreqyres[0] <= curres<<1 && subreqxres[1] <= curres && subreqyres[1] <= curres<<1)
		{
			RenderTileShadow(subx, suby, 2, 1, level+1);
			rendertile[0] = rendertile[1] = true;
		}

		//tile 2 && 3 -> yres doubles
		if (!rendertile[2] && !rendertile[3] && subreqxres[2] <= curres && subreqyres[2] <= curres<<1 && subreqxres[3] <= curres && subreqyres[3] <= curres<<1)
		{
			RenderTileShadow(subx, suby + 1, 2, 1, level+1);

			rendertile[2] = rendertile[3] = true;
		}

		//tile 0 && 2 -> xres doubles
		if (!rendertile[0] && !rendertile[2] && subreqxres[0] <= curres<<1 && subreqyres[0] <= curres && subreqxres[2] <= curres<<1 && subreqyres[2] <= curres)
		{
			RenderTileShadow(subx, suby, 1, 2, level+1);

			rendertile[0] = rendertile[2] = true;
		}

		//tile 1 && 3 -> xres doubles
		if (!rendertile[1] && !rendertile[3] && subreqxres[1] <= curres<<1 && subreqyres[1] <= curres && subreqxres[3] <= curres<<1 && subreqyres[3] <= curres)
		{
			RenderTileShadow(subx+1, suby, 1, 2, level+1);

			rendertile[1] = rendertile[3] = true;
		}

		if (!rendertile[0])
			ProcessPyramid(subx, suby, sublevel);

		if (!rendertile[1])
			ProcessPyramid(subx + 1, suby, sublevel);

		if (!rendertile[2])
			ProcessPyramid(subx, suby + 1, sublevel);

		if (!rendertile[3])
			ProcessPyramid(subx + 1, suby + 1, sublevel);
	}
}

void DeferredShadowSMTiled::RenderTileShadow(const unsigned x, const unsigned y, const unsigned xl, const unsigned yl, const unsigned level)
{
	//wlog("rendertile %d %d %d %d %d", x, xl, y, yl, level);

	//setup projection of tile
	DXMatrix matToTexSpace = DXMatrix::TranslationMatrix(1, -1, 0) * DXMatrix::ScalingMatrix(0.5, 0.5, 1); //transfrom from device coordinates to texture/tile coordinates
	DXMatrix matToScreenSpace = DXMatrix::ScalingMatrix(2, 2, 1) * DXMatrix::TranslationMatrix(-1, 1, 0); //transform from tex/tile to device coord
	
	DXMatrix matTile;
	matTile = m_matLigthProj * matToTexSpace;
	matTile *= DXMatrix::TranslationMatrix(-((x << (m_Tiling - level)) / (float)(1 << m_Tiling)), (y << (m_Tiling - level)) / (float)(1 << m_Tiling), 0) * 
				DXMatrix::ScalingMatrix((1 << level) / xl, (1 << level) / yl, 1); //transform to tile
	matTile *= matToScreenSpace;
	matTile *= DXMatrix::ScalingMatrix(1 - 2 / m_pApp->m_SM->GetShadowMapSize(), 1 - 2 / m_pApp->m_SM->GetShadowMapSize(), 1); //scale view to slightly overlap, otherwise there are pixel holes

	m_pApp->GetShadowMapping()->SetLightViewProj(m_pApp->GetShadowMapping()->GetLightView(), matTile);

	ViewFrustum vf(Camera(m_pApp->GetShadowMapping()->GetLightView(), matTile));
	Frustum f;
	for (int i = 0; i < 6; i++)
		f.planes.push_back(vf.ClipPlanes[i]);
	m_pApp->GetActiveScene()->GetLight()->ClearRenderList();
	m_pApp->GetActiveScene()->GetSpatialPartitioningTree()->TraverseVisibleObjects(f, DirectionalLight::GatherShadowCasters, m_pApp->GetActiveScene()->GetLight());

	m_pApp->GetActiveScene()->GetLight()->RenderShadowMap();

	m_FXSMTiled->SetFloat("g_BiasScale", 0.02f + 0.98f / (1<<level)); 
	m_FXSMTiled->SetMatrix("SM_matSM", m_pApp->GetShadowMapping()->GetSMMatrix());
	m_FXSMTiled->SetInt("g_iPass", m_pApp->GetActiveScene()->GetLight()->GetPassesDone() - 1);

	if(m_pApp->m_VisType == VIS_SAMPLE)
	{
		m_pApp->DrawSampleVis();
	}

	XString tech = "RenderSMTile";

	if (m_pApp->GetWorldPosType() == WPT_DEPTH)
		tech.appand("_Depth");
	else if (m_pApp->GetWorldPosType() == WPT_LINEARDEPTH)
		tech.appand("_LinearDepth");
	else if (m_pApp->GetWorldPosType() == WPT_WORLDPOS)
		tech.appand("_World");

	if (m_pApp->GetSMFiltering())
		tech.appand("_Filter");
	else if (m_pApp->GetUseIDBuffer())
		tech.appand("_ID");

	m_FXSMTiled->SetTechnique(tech);

	m_pApp->RenderFSQuad(m_FXSMTiled);
}

DXVector2 DeferredShadowSMTiled::BuildPyramid(const unsigned x, const unsigned y, const unsigned level)
{
	if (level == m_Tiling)
	{
		return DXVector2(m_pSMTGP[level][x + y * (1<<level)], m_pSMTGP[level][(1<<level) * (1<<level) + x + y * (1<<level)]);
	}

	DXVector2 vmax = BuildPyramid((x<<1), (y<<1), level+1);
	vmax = DXVector2::Max(vmax, BuildPyramid((x<<1)+1, (y<<1), level+1));
	vmax = DXVector2::Max(vmax, BuildPyramid((x<<1), (y<<1)+1, level+1));
	vmax = DXVector2::Max(vmax, BuildPyramid((x<<1)+1, (y<<1)+1, level+1));

	m_pSMTGP[level][x + y * (1<<level)] = vmax.x;
	m_pSMTGP[level][(1<<level) * (1<<level) + x + y * (1<<level)] = vmax.y;

	return vmax;
}

void DeferredShadowSMTiled::CalculateTileResCPU(void)
{
	unsigned tilecount = 1 << m_Tiling;
	int size = tilecount * tilecount; 
	ZeroMemory(m_pSMTGP[m_Tiling], sizeof(float) * size * 2);

	m_RequiredResDownSampled->UpdateOffscreenSurface();
	m_RequiredResDownSampled->Lock();

	BYTE* pRow = (BYTE*)m_RequiredResDownSampled->GetLockedData()->pBits;
	unsigned rowoffset = m_RequiredResDownSampled->GetLockedData()->Pitch;

	//take write max to each tile
	for (int y = 0; y < m_ReqResDownSampleSize; y++)
	{
		DWORD* pData = (DWORD*)pRow;
		for (int x = 0; x < m_ReqResDownSampleSize; x++)
		{
			DWORD ti = *pData;
			BYTE tx = (ti >> 16) & 0xff;
			BYTE ty = (ti >> 8) & 0xff;
			BYTE reqresx = (ti >> 24) & 0xff;
			BYTE reqresy = (ti >> 0) & 0xff;

			int itx = tx >> 2;
			int ity = ty >> 2;

			if (reqresx > 0 && reqresy > 0)
			{
				if (reqresy > 17)
					int i = 0;
				m_pSMTGP[m_Tiling][itx + ity * tilecount] = _max(reqresx, m_pSMTGP[m_Tiling][itx + ity * tilecount]);
				m_pSMTGP[m_Tiling][itx + ity * tilecount + size] = _max(reqresy, m_pSMTGP[m_Tiling][itx + ity * tilecount + size]);
			}

			pData++;
		}

		pRow += rowoffset;
	}

	m_RequiredResDownSampled->Unlock();

	//
	/*memcpy((float*)(m_RTTileResX->GetLockedData()->pBits), m_pSMTGP[m_Tiling], size * sizeof(float));
	memcpy((float*)(m_RTTileResY->GetLockedData()->pBits), m_pSMTGP[m_Tiling] + size, size * sizeof(float));

	m_RTTileResX->Unlock();

	HRESULT hr;
	LPDIRECT3DSURFACE9 surf;

	//m_RTTileResX_x4->GetTexture()->GetTextureSurface()->GetSurfaceLevel(0, &surf);
	m_RTTileResX->GetOffscreenSurface()->GetTextureSurface()->GetSurfaceLevel(0, &surf);
	hr = D3DXSaveSurfaceToFile("testx.hdr", D3DXIFF_HDR, surf, NULL, NULL);
	surf->Release();

	m_RTTileResX->Lock();*/
}

void DeferredShadowSMTiled::CalculateTileResGPU(void)
{
	//the shadow map gets divided in 32x32 tiles and first the minimum and maximum needed confidence has to be found
	//therefore transform all fragments from screenspace to the shadow map and accumulate the min and max conf
	
	float screenxl = m_pApp->GetDevice()->GetViewPort()->GetWidth();
	float screenyl = m_pApp->GetDevice()->GetViewPort()->GetHeight();

	unsigned tilecount = 1 << m_Tiling;

	//create vertex buffer that has one point per screen space fragment
	//unsigned vbres = screenxl + ((int)screenyl << 1 );
	unsigned vbres = m_DownSampleReqRes ? m_ReqResDownSampleSize + (m_ReqResDownSampleSize << 1) : screenxl + ((int)screenyl << 1);

	if (m_VBRes != vbres)
	{
		SAFERELEASE(m_VBSamples);

		m_VBSamples = m_pApp->GetDevice()->GetBufferManager()->CreateVB((m_DownSampleReqRes ? m_ReqResDownSampleSize * m_ReqResDownSampleSize : screenxl * screenyl) * sizeof(short) * 2, 0, D3DUSAGE_WRITEONLY, POOL_MANAGED);
		short* vbdata;
		m_VBSamples->GetBuffer()->Lock(0, 0, (void**)&vbdata, 0);

		for(short y = 0; y < (m_DownSampleReqRes ? m_ReqResDownSampleSize : screenyl); y++)
		{
			for(short x = 0; x < (m_DownSampleReqRes ? m_ReqResDownSampleSize : screenxl); x++)
			{
				*vbdata++ = x;
				*vbdata++ = y;
			}
		}

		m_VBSamples->GetBuffer()->Unlock();

		m_VBRes = vbres;
	}
	
	float rtreqsize[2] = {m_DownSampleReqRes ? m_ReqResDownSampleSize : screenxl, m_DownSampleReqRes ? m_ReqResDownSampleSize : screenyl};
	m_FXSMTiled->SetFloatArray("g_RTRequiredResSize", rtreqsize, 2);
	m_pApp->GetDevice()->SetStreamSource(0, m_VBSamples->GetBuffer(), 0, sizeof(short) * 2);
	m_pApp->GetDevice()->SetVertexDeclaration(m_VDeclSample);

	
	//render the required x res to each tile in the intermediate texture
	m_RTTileResX_x4->StartRender(0);
	
	m_pApp->GetDevice()->Clear(D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 0, 0);

	m_FXSMTiled->SetTechnique("RenderTileRes_X");

	RENDERFX(m_FXSMTiled, 
		m_pApp->GetDevice()->DrawPrimitive(PT_POINTLIST, 0, m_DownSampleReqRes ? m_ReqResDownSampleSize  * m_ReqResDownSampleSize : screenxl * screenyl);
	);

	m_RTTileResX_x4->EndRender();


	//render the required y res to each tile in the intermediate texture
	m_RTTileResY_x4->StartRender(0);

	m_pApp->GetDevice()->Clear(D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 0, 0);

	m_FXSMTiled->SetTechnique("RenderTileRes_Y");

	RENDERFX(m_FXSMTiled, 
		m_pApp->GetDevice()->DrawPrimitive(PT_POINTLIST, 0, m_DownSampleReqRes ? m_ReqResDownSampleSize  * m_ReqResDownSampleSize : screenxl * screenyl);
	);

	m_RTTileResY_x4->EndRender();

	//downsample intermediate texture
	
	if (m_UseAvg)
		m_FXSMTiled->SetTechnique("RenderDownSampleX4_Avg");
	else
		m_FXSMTiled->SetTechnique("RenderDownSampleX4_Max");

	m_FXSMTiled->SetTexture("g_texTileResIntermediate", m_RTTileResX_x4->GetSurface());

	m_RTTileResX->StartRender(0);
	m_pApp->RenderQuad(0, 0, tilecount, tilecount, m_FXSMTiled, tilecount, tilecount);
	m_RTTileResX->EndRender();


	m_FXSMTiled->SetTexture("g_texTileResIntermediate", m_RTTileResY_x4->GetSurface());

	m_RTTileResY->StartRender(0);
	m_pApp->RenderQuad(0, 0, tilecount, tilecount, m_FXSMTiled, tilecount, tilecount);
	m_RTTileResY->EndRender();
	
	//dont need to pay regard to pitch because rt is 32bit alligned
	int size = tilecount * tilecount;
	memcpy(m_pSMTGP[m_Tiling], (float*)(m_RTTileResX->GetLockedData()->pBits), size * sizeof(float));
	memcpy(m_pSMTGP[m_Tiling] + size, (float*)(m_RTTileResY->GetLockedData()->pBits), size * sizeof(float));

	//for (int i = 0; i < size; i++)
	//{
	//	float a = m_pSMTGP[m_Tiling][i];
	//	if (a != 0)
	//		a = a;
	//}

	//HRESULT hr;
	//LPDIRECT3DSURFACE9 surf;

	//m_RTTileResX_x4->GetTexture()->GetTextureSurface()->GetSurfaceLevel(0, &surf);
	//m_RTTileResX->GetGPUSurface()->GetTexture()->GetTextureSurface()->GetSurfaceLevel(0, &surf);
	//hr = D3DXSaveSurfaceToFile("testxgpu.hdr", D3DXIFF_HDR, surf, NULL, NULL);
	//surf->Release();
}

void DeferredShadowSMTiled::RenderRequiredRes(void)
{
	m_RequiredRes->StartRender(0);
			
	XString tech = "RenderRequiredRes";
	if (m_pApp->GetWorldPosType() == WPT_DEPTH)
		tech.appand("_Depth");
	else if (m_pApp->GetWorldPosType() == WPT_LINEARDEPTH)
		tech.appand("_LinearDepth");
	else if (m_pApp->GetWorldPosType() == WPT_WORLDPOS)
		tech.appand("_World");
	m_FXSMTiled->SetTechnique(tech);

	m_pApp->GetDevice()->Clear(D3DCLEAR_TARGET, 0x00000000, 0, 0);

	m_pApp->RenderFSQuad(m_FXSMTiled);

	m_RequiredRes->EndRender();

	//downsample to 256x256
	if (m_DownSampleReqRes)
	{
		m_FXSMTiled->SetTechnique("RenderReqResDownSample");

		m_FXSMTiled->SetFloat("g_ReqResDownSampleSize", m_ReqResDownSampleSize);
		m_FXSMTiled->SetTexture("g_texRequiredRes", m_RequiredRes->GetSurface());

		m_RequiredResDownSampled->StartRender(0);
		m_pApp->RenderQuad(0, 0, m_ReqResDownSampleSize, m_ReqResDownSampleSize, m_FXSMTiled, m_ReqResDownSampleSize, m_ReqResDownSampleSize);
		m_RequiredResDownSampled->EndRender();
	}
 
	HRESULT hr;
	LPDIRECT3DSURFACE9 surf; 

	m_RequiredRes->GetTexture()->GetTextureSurface()->GetSurfaceLevel(0, &surf);
	//hr = D3DXSaveSurfaceToFile("reqres.png", D3DXIFF_PNG, surf, NULL, NULL);
	surf->Release();  

	//((DXTexture*)(m_RequiredResDownSampled->GetGPUSurface()->GetSurface()))->GetTextureSurface()->GetSurfaceLevel(0, &surf);
	//hr = D3DXSaveSurfaceToFile("reqresdown.png", D3DXIFF_PNG, surf, NULL, NULL);
	//surf->Release(); 
}

void DeferredShadowSMTiled::CreateMenu(void)
{
	m_pMenu = new Menu(m_pApp->GetMenuManager(), 213);

	Panel* pnBack = new Panel(m_pApp->GetMenuManager(), 42342);
	pnBack->SetPosition(300, 85);
	pnBack->SetSize(250, 200);
	pnBack->SetOpacity(0.8f);
	pnBack->SetTexture(m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\ck.png", FMT_A8R8G8B8, POOL_MANAGED));
	pnBack->SetAlphaBlend(true);
	pnBack->SetFixedBorder(4);

	m_pMenu->AddChild(pnBack);

	FontParam fp;
	fp.m_Height = 16;
	Font* font = m_pApp->GetDevice()->GetFontManager()->Create(fp);

	int y = 20;

	//tiling slider: 16/32/64

	Label* lbTiling = new Label(m_pApp->GetMenuManager(), 0);
	lbTiling->SetSize(110, 20);
	lbTiling->SetPosition(10, y);
	lbTiling->SetFont(font);
	lbTiling->SetText("Tiling:");

	pnBack->AddChild(lbTiling);

	Label* lbTilingValue = new Label(m_pApp->GetMenuManager(), 0);
	lbTilingValue->SetUserData(this);
	lbTilingValue->SetSize(40, 20);
	lbTilingValue->SetPosition(60, y);
	lbTilingValue->SetFont(font);
	lbTilingValue->SetText("xxx");
	lbTilingValue->SetTextAlign(AL_RIGHTBOTTOM);

	pnBack->AddChild(lbTilingValue);

	Slider* slTiling = new Slider(m_pApp->GetMenuManager(), 345);
	slTiling->SetUserData(lbTilingValue);
	slTiling->SetPosition(100, y+2);
	slTiling->SetSize(130, 20);
	slTiling->SetRange(4, 6);
	slTiling->SetSliderSize(10, 20, 20, 10);
	slTiling->SetTexture(m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\slider.png", FMT_A8R8G8B8, POOL_MANAGED), 
					m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\nob.png", FMT_A8R8G8B8, POOL_MANAGED));
	slTiling->SetAlphaBlend(true);

	pnBack->AddChild(slTiling);
	slTiling->SetEventHandler(GE_VALUECHANGED, OnTilingChanged);
	slTiling->SetValue((int)m_Tiling);

	y += 30;

	//xi slider: 0-5

	Label* lbXi = new Label(m_pApp->GetMenuManager(), 0);
	lbXi->SetSize(110, 20);
	lbXi->SetPosition(10, y);
	lbXi->SetFont(font);
	lbXi->SetText("Xi:");

	pnBack->AddChild(lbXi);

	Label* lbXiValue = new Label(m_pApp->GetMenuManager(), 0);
	lbXiValue->SetUserData(this);
	lbXiValue->SetSize(40, 20);
	lbXiValue->SetPosition(60, y);
	lbXiValue->SetFont(font);
	lbXiValue->SetText("xxx");
	lbXiValue->SetTextAlign(AL_RIGHTBOTTOM);

	pnBack->AddChild(lbXiValue);

	Slider* slXi = new Slider(m_pApp->GetMenuManager(), 345);
	slXi->SetValue(-1);
	slXi->SetUserData(lbXiValue);
	slXi->SetPosition(100, y+2);
	slXi->SetSize(130, 20);
	slXi->SetRange(0, 5);
	slXi->SetSliderSize(10, 20, 20, 10);
	slXi->SetTexture(m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\slider.png", FMT_A8R8G8B8, POOL_MANAGED), 
					m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\nob.png", FMT_A8R8G8B8, POOL_MANAGED));
	slXi->SetAlphaBlend(true);

	pnBack->AddChild(slXi);
	slXi->SetEventHandler(GE_VALUECHANGED, OnXiChanged);
	slXi->SetValue((int)m_Xi);

	y += 30;

	//ck downsamplereqres

	CheckBox* ckDownSampleReqRes = new CheckBox(m_pApp->GetMenuManager(), 24);
	ckDownSampleReqRes->SetUserData(this);
	ckDownSampleReqRes->SetPosition(10, y);
	ckDownSampleReqRes->SetSize(100, 20);
	ckDownSampleReqRes->SetChecked(m_DownSampleReqRes);
	ckDownSampleReqRes->SetText("UseDownSampledReqRes");
	ckDownSampleReqRes->SetTexture(m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\ck.png", FMT_A8R8G8B8, POOL_MANAGED), 
					m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\ckc.png", FMT_A8R8G8B8, POOL_MANAGED));
	ckDownSampleReqRes->SetAlphaBlend(true);
	ckDownSampleReqRes->SetFont(font);
	ckDownSampleReqRes->SetEventHandler(GE_VALUECHANGED, OnUseDownSampleReqResChanged);

	pnBack->AddChild(ckDownSampleReqRes);

	y += 30;

	//rd max avg

	RadioButton* rbmax = new RadioButton(m_pApp->GetMenuManager(), 33);
	rbmax->SetUserData(this);
	rbmax->SetPosition(10, y);
	rbmax->SetSize(80, 19);
	rbmax->SetText("Max");
	rbmax->SetTexture(m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\rb.png", FMT_A8R8G8B8, POOL_MANAGED), 
					m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\rbc.png", FMT_A8R8G8B8, POOL_MANAGED));
	rbmax->SetAlphaBlend(true);
	rbmax->SetChecked(!m_UseAvg);
	rbmax->SetFont(font);
	rbmax->SetEventHandler(GE_VALUECHANGED, OnUseMaxChanged);

	pnBack->AddChild(rbmax);

	RadioButton* rbavg = new RadioButton(m_pApp->GetMenuManager(), 34);
	rbavg->SetUserData(this);
	rbavg->SetPosition(100, y);
	rbavg->SetSize(80, 19);
	rbavg->SetText("Avg");
	rbavg->SetTexture(m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\rb.png", FMT_A8R8G8B8, POOL_MANAGED), 
					m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\rbc.png", FMT_A8R8G8B8, POOL_MANAGED));
	rbavg->SetAlphaBlend(true);
	rbavg->SetChecked(m_UseAvg);
	rbavg->SetFont(font);
	rbavg->SetEventHandler(GE_VALUECHANGED, OnUseAvgChanged);

	pnBack->AddChild(rbavg);

	y += 30;

	//reqresdownsamplesize cb: 200,256,320,370,420,512
	
	Label *lbReqResDSS = new Label(m_pApp->GetMenuManager(), 2);
	lbReqResDSS->SetPosition(10, y);
	lbReqResDSS->SetSize(100, 20);
	lbReqResDSS->SetText("ReqResDSSize: ");
	lbReqResDSS->SetFont(font);
	
	pnBack->AddChild(lbReqResDSS);

	ComboBox* cbReqResDSS = new ComboBox(m_pApp->GetMenuManager(), 12344);
	cbReqResDSS->SetUserData(this);
	cbReqResDSS->SetPosition(100, y);
	cbReqResDSS->SetSize(130, 22);
	cbReqResDSS->SetDropDownHeight(g_ReqResDownSampleSizeCnt*19 + 8);
	cbReqResDSS->SetDropDownItemHeight(19);
	for (int i = 0; i < g_ReqResDownSampleSizeCnt; i++)
		cbReqResDSS->AddItem(XString(g_ReqResDownSampleSizes[i]));
	cbReqResDSS->SetTexture(m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\input.png", FMT_A8R8G8B8, POOL_MANAGED), false, 3, 
					m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\combo.png", FMT_A8R8G8B8, POOL_MANAGED), true,
					m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\list.png", FMT_A8R8G8B8, POOL_MANAGED), 
					m_pApp->GetDevice()->GetTextureManager()->CreateTexture("GUI\\listsel.png", FMT_A8R8G8B8, POOL_MANAGED), false, 4);
	cbReqResDSS->SetTextColor(D3DXCOLOR(0,0,0,1), D3DXCOLOR(1,1,1,1));
	cbReqResDSS->SetFont(font);
	cbReqResDSS->SetClientBorder(5);
	cbReqResDSS->SetSelectedIndex((int)1);
	cbReqResDSS->SetEventHandler(GE_VALUECHANGED, OnReqResDonwSampleSizeChanged);

	pnBack->AddChild(cbReqResDSS);
}

void DeferredShadowSMTiled::OnTilingChanged(GUIElement* const sender, const GUIEVENT evnt, void* pData)
{
	Slider* sl = dynamic_cast<Slider*>(sender);
	Label* lb = (Label*)sl->GetUserData();
	DeferredShadowSMTiled* me = (DeferredShadowSMTiled*)lb->GetUserData();

	if (me && sl && lb)
	{
		lb->SetText(XString(sl->GetValue()));
		me->SetTiling(sl->GetValue());
	}
}

void DeferredShadowSMTiled::OnXiChanged(GUIElement* const sender, const GUIEVENT evnt, void* pData)
{
	Slider* sl = dynamic_cast<Slider*>(sender);
	Label* lb = (Label*)sl->GetUserData();
	DeferredShadowSMTiled* me = (DeferredShadowSMTiled*)lb->GetUserData();

	if (me && sl && lb)
	{
		lb->SetText(XString(sl->GetValue()));
		me->m_Xi = sl->GetValue();
	}
}

void DeferredShadowSMTiled::OnUseDownSampleReqResChanged(GUIElement* const sender, const GUIEVENT evnt, void* pData)
{
	CheckBox* ck = dynamic_cast<CheckBox*>(sender);
	DeferredShadowSMTiled* me = (DeferredShadowSMTiled*)ck->GetUserData();

	if (me && ck)
	{
		me->m_DownSampleReqRes = ck->IsChecked();
	}
}

void DeferredShadowSMTiled::OnReqResDonwSampleSizeChanged(GUIElement* const sender, const GUIEVENT evnt, void* pData)
{

}

void DeferredShadowSMTiled::OnUseAvgChanged(GUIElement* const sender, const GUIEVENT evnt, void* pData)
{
	RadioButton* rb = dynamic_cast<RadioButton*>(sender);
	DeferredShadowSMTiled* me = (DeferredShadowSMTiled*)rb->GetUserData();

	if (me && rb && rb->IsChecked())
	{
		me->m_UseAvg = true;
	}
}

void DeferredShadowSMTiled::OnUseMaxChanged(GUIElement* const sender, const GUIEVENT evnt, void* pData)
{
	RadioButton* rb = dynamic_cast<RadioButton*>(sender);
	DeferredShadowSMTiled* me = (DeferredShadowSMTiled*)rb->GetUserData();

	if (me && rb && rb->IsChecked())
	{
		me->m_UseAvg = false;
	}
}