From 64c45059cf45cde7b69e2adf880e40df0b29c2d9 Mon Sep 17 00:00:00 2001
From: D-AIRY <admin@ds-servers.com>
Date: Tue, 7 Apr 2020 13:30:34 +0300
Subject: [PATCH] Some LPV improvements

---
 build/engine/shaders/base/skybox.vs          |  10 +-
 build/engine/shaders/gi/gi_cubes.gs          |  10 +-
 build/engine/shaders/gi/gi_cubes.ps          |   4 +-
 build/engine/shaders/gi/gi_cubes.vs          |  10 +-
 build/engine/shaders/gi/gi_inject.gs         |   2 +-
 build/engine/shaders/gi/gi_inject.ps         |   2 +-
 build/engine/shaders/gi/gi_inject.vs         |   6 +-
 build/engine/shaders/gi/gi_inject_cube.vs    |   2 +-
 build/engine/shaders/lighting/lighting_gi.ps |  22 +-
 build/engine/shaders/struct.h                |   3 +
 source/game/BaseCharacter.cpp                |   2 +-
 source/game/GameData.cpp                     |   2 +-
 source/light/IXLight.h                       |   8 +-
 source/light/light.cpp                       |  12 +-
 source/light/light.h                         |  17 +-
 source/render/RenderPipeline.cpp             | 118 +++++--
 source/render/ShadowCache.cpp                | 336 ++++++++++++-------
 source/render/ShadowCache.h                  | 177 +++++++++-
 source/render/shadow.cpp                     |  18 +-
 source/render/shadow.h                       |  12 +-
 20 files changed, 561 insertions(+), 212 deletions(-)

diff --git a/build/engine/shaders/base/skybox.vs b/build/engine/shaders/base/skybox.vs
index 8feabafbe..4bcd80608 100644
--- a/build/engine/shaders/base/skybox.vs
+++ b/build/engine/shaders/base/skybox.vs
@@ -4,8 +4,8 @@ skybox.vs
 Скайбокс
 */
 
-#include "../struct.h"
-#include "../const.h"
+#include <struct.h>
+#include <const.h>
 
 //##########################################################################
 
@@ -13,8 +13,10 @@ VSO_SkyBox main(VSI_SkyBox IN)
 {
 	VSO_SkyBox OUT = (VSO_SkyBox)0;
 
-	OUT.vPosition = mul(half4(IN.vPosition + g_vPosCam.xyz, 1.f), g_mVP);
+	OUT.vPosition = mul(float4(IN.vPosition + g_vPosCam.xyz, 1.f), g_mVP);
 	OUT.vTexUV = IN.vTexUV;
-
+	OUT.vNormal = -IN.vPosition;
+	OUT.vPos = OUT.vPosition;
+	
 	return OUT;
 }
diff --git a/build/engine/shaders/gi/gi_cubes.gs b/build/engine/shaders/gi/gi_cubes.gs
index e0813a667..c93ebcc98 100644
--- a/build/engine/shaders/gi/gi_cubes.gs
+++ b/build/engine/shaders/gi/gi_cubes.gs
@@ -4,16 +4,16 @@ terrax_colored.vs
 
 */
 
-#include "../struct.h"
+#include <struct.h>
 
 //##########################################################################
 
 cbuffer perFrame: register(b1)
 {
-	half4x4 g_mVP;
-	half4x4 g_mViewInv;
-	half2 g_vNearFar;
-	half3 g_vParamProj;
+	float4x4 g_mVP;
+	float4x4 g_mViewInv;
+	float2 g_vNearFar;
+	float3 g_vParamProj;
 };
 
 //##########################################################################
diff --git a/build/engine/shaders/gi/gi_cubes.ps b/build/engine/shaders/gi/gi_cubes.ps
index 7daaf9228..36bcd694d 100644
--- a/build/engine/shaders/gi/gi_cubes.ps
+++ b/build/engine/shaders/gi/gi_cubes.ps
@@ -3,8 +3,8 @@
 .ps
 */
 
-#include "../lpv.h"
-#include "../struct.h"
+#include <lpv.h>
+#include <struct.h>
 
 //##########################################################################
 
diff --git a/build/engine/shaders/gi/gi_cubes.vs b/build/engine/shaders/gi/gi_cubes.vs
index e04748139..fbf745edf 100644
--- a/build/engine/shaders/gi/gi_cubes.vs
+++ b/build/engine/shaders/gi/gi_cubes.vs
@@ -4,16 +4,16 @@ terrax_colored.vs
 
 */
 
-#include "../struct.h"
+#include <struct.h>
 
 //##########################################################################
 
 cbuffer perFrame: register(b1)
 {
-	half4x4 g_mVP;
-	half4x4 g_mViewInv;
-	half2 g_vNearFar;
-	half3 g_vParamProj;
+	float4x4 g_mVP;
+	float4x4 g_mViewInv;
+	float2 g_vNearFar;
+	float3 g_vParamProj;
 };
 
 cbuffer b8: register(b8)
diff --git a/build/engine/shaders/gi/gi_inject.gs b/build/engine/shaders/gi/gi_inject.gs
index 49def7304..3211681dc 100644
--- a/build/engine/shaders/gi/gi_inject.gs
+++ b/build/engine/shaders/gi/gi_inject.gs
@@ -1,5 +1,5 @@
 
-#include "../lpv.h"
+#include <lpv.h>
 
 struct GS_IN
 {
diff --git a/build/engine/shaders/gi/gi_inject.ps b/build/engine/shaders/gi/gi_inject.ps
index 3f6af2b4f..23ba047ed 100644
--- a/build/engine/shaders/gi/gi_inject.ps
+++ b/build/engine/shaders/gi/gi_inject.ps
@@ -1,4 +1,4 @@
-#include "../lpv.h"
+#include <lpv.h>
 
 struct PS_IN
 {
diff --git a/build/engine/shaders/gi/gi_inject.vs b/build/engine/shaders/gi/gi_inject.vs
index e8d108594..d370489a9 100644
--- a/build/engine/shaders/gi/gi_inject.vs
+++ b/build/engine/shaders/gi/gi_inject.vs
@@ -1,5 +1,5 @@
 
-#include "../lpv.h"
+#include <lpv.h>
 
 cbuffer perLight: register(b7)
 {
@@ -82,8 +82,8 @@ RsmTexel GetRsmTexel(int2 coords, uint2 vTexSize)
 	return(tx);
 }
 
-#define KERNEL_SIZE 4
-#define STEP_SIZE 1
+// #define KERNEL_SIZE 4
+// #define STEP_SIZE 1
 
 GS_IN main(VS_IN input)
 {	
diff --git a/build/engine/shaders/gi/gi_inject_cube.vs b/build/engine/shaders/gi/gi_inject_cube.vs
index 2dd7345d2..921fad332 100644
--- a/build/engine/shaders/gi/gi_inject_cube.vs
+++ b/build/engine/shaders/gi/gi_inject_cube.vs
@@ -1,5 +1,5 @@
 
-#include "../lpv.h"
+#include <lpv.h>
 
 cbuffer perLight: register(b7)
 {
diff --git a/build/engine/shaders/lighting/lighting_gi.ps b/build/engine/shaders/lighting/lighting_gi.ps
index 40ffd8973..a7ea35324 100644
--- a/build/engine/shaders/lighting/lighting_gi.ps
+++ b/build/engine/shaders/lighting/lighting_gi.ps
@@ -4,15 +4,15 @@ lighting_gi.ps
 
 */
 
-#include "../lpv.h"
-#include "../struct.h"
-#include "../mtrl.h"
+#include <lpv.h>
+#include <struct.h>
+#include <mtrl.h>
 
 //##########################################################################
 
 cbuffer perFrame: register(b1)
 {
-	half3 g_vViewPos;
+	float3 g_vViewPos;
 };
 
 cbuffer b8: register(b8)
@@ -39,17 +39,17 @@ PSO_Lbuffer main(VSO_ResPos IN)
 {
 	PSO_Lbuffer OUT;
 
-	half4 vNormals = g_txNormals.Sample(g_sPointClamp, IN.vTexUV);
-	half fDepth = g_txDepth.Sample(g_sPointClamp,IN.vTexUV).r;
+	float4 vNormals = g_txNormals.Sample(g_sPointClamp, IN.vTexUV);
+	float fDepth = g_txDepth.Sample(g_sPointClamp,IN.vTexUV).r;
 	
 	// xy_ - размеры пикселя текстуры, __z - размер сетки в мировых
-	half3 vTexSize = half3(1.0 / 32.0, 1.0 / 32.0, 16); 
-	half3 vOrigin = half3(0, 0, 0); // Центр сетки
-	half4 vPosition = half4(g_vViewPos.xyz + IN.vWorldRay * fDepth, 1.0); // Мировая позиция пиксела
+	float3 vTexSize = float3(1.0 / 32.0, 1.0 / 32.0, 16); 
+	float3 vOrigin = float3(0, 0, 0); // Центр сетки
+	float4 vPosition = float4(g_vViewPos.xyz + IN.vWorldRay * fDepth, 1.0); // Мировая позиция пиксела
 	
 	vPosition -= floor(g_vPosCam);
 	
-	half3 vNormalPixel = normalize(NormalDecode(vNormals.xyz).xyz);
+	float3 vNormalPixel = normalize(NormalDecode(vNormals.xyz).xyz);
 	
 	/* half3 vLocalPos = (vPosition - vOrigin);
 		
@@ -92,7 +92,7 @@ PSO_Lbuffer main(VSO_ResPos IN)
 	float g = saturate(dot(SHintensity, vColorG));
 	float b = saturate(dot(SHintensity, vColorB));
 	
-	OUT.vAmdient.xyz = float3(r, g, b) / PI;
+	OUT.vAmdient.xyz = float3(r, g, b) / PI/*  * 6.0f */;
 	OUT.vAmdient.w = 1;
 	OUT.vSpecular = 0;
 	
diff --git a/build/engine/shaders/struct.h b/build/engine/shaders/struct.h
index 92b48e02b..a72254e40 100644
--- a/build/engine/shaders/struct.h
+++ b/build/engine/shaders/struct.h
@@ -172,6 +172,9 @@ struct VSO_SkyBox
 {
 	float4 vPosition	:POSITION0;
 	float3 vTexUV	:TEXCOORD0;
+	
+	float3 vNormal	:TEXCOORD1;
+	float4 vPos		:TEXCOORD2;
 };
 
 //!@}
diff --git a/source/game/BaseCharacter.cpp b/source/game/BaseCharacter.cpp
index d3c4109dd..853df59f5 100644
--- a/source/game/BaseCharacter.cpp
+++ b/source/game/BaseCharacter.cpp
@@ -112,7 +112,7 @@ CBaseCharacter::CBaseCharacter(CEntityManager * pMgr):
 	m_flashlight->setOrient(m_pHeadEnt->getOrient() * SMQuaternion(SM_PIDIV2, 'x'));
 	m_flashlight->setParent(m_pHeadEnt);
 	m_flashlight->setDist(20.f);
-	m_flashlight->setOuterAngle(SMToRadian(60));
+	m_flashlight->setOuterAngle(SMToRadian(30)); // 60
 	m_flashlight->setInnerAngle(SMToRadian(10));
 	m_flashlight->setColor(float3(3.5, 3.5, 3.5));
 	//m_flashlight->setShadowType(-1);
diff --git a/source/game/GameData.cpp b/source/game/GameData.cpp
index e55186694..4206db5e7 100644
--- a/source/game/GameData.cpp
+++ b/source/game/GameData.cpp
@@ -441,7 +441,7 @@ GameData::GameData(HWND hWnd, bool isGame):
 
 	m_pLightSystem = (IXLightSystem*)Core_GetIXCore()->getPluginManager()->getInterface(IXLIGHTSYSTEM_GUID);
 
-	if(m_pLightSystem)
+	if(m_pLightSystem && false)
 	{
 		//	252, 212, 64
 		IXLightSun *pSun = m_pLightSystem->createSun();
diff --git a/source/light/IXLight.h b/source/light/IXLight.h
index 3efcb4806..b2995edd8 100644
--- a/source/light/IXLight.h
+++ b/source/light/IXLight.h
@@ -39,6 +39,8 @@ enum LIGHT_RENDER_TYPE
 	LRT_LIGHT = 1, //!< Только освещение, без LPV 
 	LRT_LPV = 2, //!< Рендер для LPV в 32x32 
 	LRT_FULL = 3, //!< Полноразмерный рендер 
+
+	LRT_ALL = LRT_FULL
 };
 DEFINE_ENUM_FLAG_OPERATORS(LIGHT_RENDER_TYPE);
 
@@ -73,9 +75,11 @@ public:
 	//@TODO: Remove this method
 	virtual LIGHT_RENDER_TYPE getRenderType() = 0;
 	//@TODO: Remove this method
-	virtual bool isDirty() = 0;
+	virtual bool isDirty(LIGHT_RENDER_TYPE type) = 0;
+	//@TODO: Remove this method
+	virtual void markClean(LIGHT_RENDER_TYPE type) = 0;
 	//@TODO: Remove this method
-	virtual void markClean() = 0;
+	virtual void testDirty() = 0;
 
 	virtual IXLightSpot *asSpot() = 0;
 	virtual IXLightSun *asSun() = 0;
diff --git a/source/light/light.cpp b/source/light/light.cpp
index e65fb1b0a..44dd97b3d 100644
--- a/source/light/light.cpp
+++ b/source/light/light.cpp
@@ -53,7 +53,7 @@ void CXLight::setColor(const float3 &vColor)
 	m_pMutationObserver->setHalfExtents(float3(getMaxDistance() * 0.5f));
 	m_isVSDataDirty = true;
 	m_isPSDataDirty = true;
-	m_isDirty = true;
+	m_dirty = LRT_ALL;
 }
 
 float3 CXLight::getPosition()
@@ -70,7 +70,7 @@ void CXLight::setPosition(const float3 &vPosition)
 	m_pMutationObserver->setPosition(vPosition);
 	m_isVSDataDirty = true;
 	m_isPSDataDirty = true;
-	m_isDirty = true;
+	m_dirty = LRT_ALL;
 }
 
 float CXLight::getShadowIntencity()
@@ -99,7 +99,7 @@ void CXLight::setEnabled(bool isEnabled)
 	}
 	m_isEnable = isEnabled;
 	m_pMutationObserver->setEnabled(isEnabled);
-	m_isDirty = true;
+	m_dirty = LRT_ALL;
 }
 
 bool CXLight::isShadowDynamic()
@@ -292,7 +292,7 @@ void CXLightSun::setDirection(const SMQuaternion &qDirection)
 	}
 	m_qDirection = qDirection;
 	m_isVSDataDirty = true;
-	m_isDirty = true;
+	m_dirty = LRT_ALL;
 }
 
 void CXLightSun::updatePSConstants(IGXDevice *pDevice)
@@ -369,7 +369,7 @@ void CXLightSpot::setOuterAngle(float fAngle)
 	m_fOuterAngle = fAngle;
 	m_isVSDataDirty = true;
 	m_isPSDataDirty = true;
-	m_isDirty = true;
+	m_dirty = LRT_ALL;
 }
 SMQuaternion CXLightSpot::getDirection()
 {
@@ -384,7 +384,7 @@ void CXLightSpot::setDirection(const SMQuaternion &qDirection)
 	m_qDirection = qDirection;
 	m_isVSDataDirty = true;
 	m_isPSDataDirty = true;
-	m_isDirty = true;
+	m_dirty = LRT_ALL;
 }
 
 void CXLightSpot::updatePSConstants(IGXDevice *pDevice)
diff --git a/source/light/light.h b/source/light/light.h
index b171211b1..850fcc887 100644
--- a/source/light/light.h
+++ b/source/light/light.h
@@ -58,18 +58,21 @@ public:
 		return(m_renderType);
 	}
 
-	bool isDirty() override
+	void testDirty() override
 	{
-		if(m_isDirty)
+		if(m_pMutationObserver->wasTriggered())
 		{
 			m_pMutationObserver->reset();
+			m_dirty = LRT_ALL;
 		}
-		return(m_isDirty || m_pMutationObserver->wasTriggered());
 	}
-	void markClean() override
+	bool isDirty(LIGHT_RENDER_TYPE type) override
 	{
-		m_isDirty = false;
-		m_pMutationObserver->reset();
+		return(m_dirty & type);
+	}
+	void markClean(LIGHT_RENDER_TYPE type) override
+	{
+		m_dirty &= ~type;
 	}
 
 protected:
@@ -105,7 +108,7 @@ protected:
 	IFrustum *m_pFrustum = NULL;
 	IXRenderableVisibility *m_pVisibility = NULL;
 	LIGHT_RENDER_TYPE m_renderType = LRT_NONE;
-	bool m_isDirty = true;
+	LIGHT_RENDER_TYPE m_dirty = LRT_ALL;
 
 	IXMutationObserver *m_pMutationObserver = NULL;
 };
diff --git a/source/render/RenderPipeline.cpp b/source/render/RenderPipeline.cpp
index dee40bda6..26d0648b6 100644
--- a/source/render/RenderPipeline.cpp
+++ b/source/render/RenderPipeline.cpp
@@ -21,6 +21,9 @@ CRenderPipeline::CRenderPipeline(IGXDevice *pDevice):
 	IPluginManager *pPluginManager = pCore->getPluginManager();
 	m_pMaterialSystem = (IXMaterialSystem*)pPluginManager->getInterface(IXMATERIALSYSTEM_GUID);
 
+	Core_0RegisterCVarBool("dev_lpv_cubes", false, "Отображать сетку LPV");
+	Core_0RegisterCVarBool("dev_lpv_points", false, "Отображать VPL при инъекции в LPV");
+	
 	XVertexOutputElement voelGeneric[] = {
 		{"vPosition", GXDECLTYPE_FLOAT4, GXDECLUSAGE_POSITION},
 		{"vTexUV", GXDECLTYPE_FLOAT2, GXDECLUSAGE_TEXCOORD},
@@ -176,9 +179,20 @@ CRenderPipeline::CRenderPipeline(IGXDevice *pDevice):
 		GXMacro pVariant1[] = {
 			{"IS_CUBEMAP", "1"},
 			{NULL, NULL}
+		}; 
+		GXMacro pVariant2[] = {
+			{"ENABLE_RSM", "1"},
+			{NULL, NULL}
+		};
+		GXMacro pVariant3[] = {
+			{"IS_CUBEMAP", "1"},
+			{"ENABLE_RSM", "1"},
+			{NULL, NULL}
 		};
 		XRenderPassVariantElement pVariants[] = {
 			pVariant1,
+			pVariant2,
+			pVariant3,
 			NULL
 		};
 
@@ -626,7 +640,12 @@ void CRenderPipeline::renderFrame()
 #endif
 
 end:
-	//showGICubes();
+	const bool *dev_lpv_cubes = GET_PCVAR_BOOL("dev_lpv_cubes");
+	if(*dev_lpv_cubes)
+	{
+		showGICubes();
+	}
+	
 	showFrameStats();
 
 	getXUI()->render();
@@ -696,12 +715,12 @@ void CRenderPipeline::showGICubes()
 	pCtx->setRenderBuffer(m_pGICubesRB);
 	SGCore_ShaderBind(m_idGICubesShader);
 	pCtx->setGSConstant(m_pLightingShaderDataVS, 1);
-	pCtx->setDepthStencilState(NULL);
+	pCtx->setDepthStencilState(m_pDepthStencilStateDefault);
 	pCtx->setPSTexture(m_pGIAccumRed2, 0);
 	pCtx->setPSTexture(m_pGIAccumGreen2, 1);
 	pCtx->setPSTexture(m_pGIAccumBlue2, 2);
 	pCtx->setSamplerState(gdata::rstates::pSamplerPointClamp, 0);
-
+	
 	pCtx->drawPrimitive(0, m_uGICubesCount);
 
 	pCtx->setPrimitiveTopology(GXPT_TRIANGLELIST);
@@ -798,6 +817,7 @@ void CRenderPipeline::renderGI()
 		++uCounts[m_pLightSystem->getLight(i)->getType()];
 	}
 	m_pShadowCache->setLightsCount(uCounts[LIGHT_TYPE_POINT], uCounts[LIGHT_TYPE_SPOT], uCounts[LIGHT_TYPE_SUN] != 0);
+	m_pShadowCache->setRSMLightsCount(uCounts[LIGHT_TYPE_POINT], uCounts[LIGHT_TYPE_SPOT], uCounts[LIGHT_TYPE_SUN] != 0);
 
 	m_pShadowCache->setObserverCamera(gdata::pCamera);
 
@@ -860,7 +880,14 @@ void CRenderPipeline::renderGI()
 		//если свет виден фрустуму камеры (это надо было заранее просчитать) и если свет включен
 		if(pLight->isEnabled() && pLight->getRenderType() != LRT_NONE)
 		{
-			m_pShadowCache->addLight(pLight);
+			if(pLight->getRenderType() & LRT_LPV)
+			{
+				m_pShadowCache->addRSMLight(pLight);
+			}
+			if(pLight->getRenderType() & LRT_LIGHT)
+			{
+				m_pShadowCache->addLight(pLight);
+			}
 		}
 	}
 
@@ -904,15 +931,7 @@ void CRenderPipeline::renderGI()
 		{
 			pLight = m_pShadowCache->getLight(i);
 			pShadow = m_pShadowCache->getShadow(i);
-
-			if(pLight->getType() == LIGHT_TYPE_SUN)
-			{
-			//	continue;
-			}
-
-			//пока что назначаем шейдер без теней
-			//ID idshaderkit = pLight->getType() == LIGHT_TYPE_SPOT ? gdata::shaders_id::kit::idComLightingSpotNonShadow : gdata::shaders_id::kit::idComLightingNonShadow;
-
+			
 			//если не глобальный источник
 			if(pLight->getType() != LIGHT_TYPE_SUN)
 			{
@@ -990,7 +1009,18 @@ void CRenderPipeline::renderGI()
 
 			SGCore_ScreenQuadDraw();
 		}
-		
+	}
+
+	bool isFirstRun = true;
+	while((uShadowCount = m_pShadowCache->processNextRSMBunch()))
+	{
+		pCtx->setDepthStencilSurface(pOldDSSurface);
+		pCtx->setBlendState(gdata::rstates::pBlendAlphaOneOne);
+
+		pCtx->setVSConstant(m_pLightingShaderDataVS, 1);
+		pCtx->setPSConstant(m_pLightingShaderDataPS, 1);
+
+
 		IGXSurface *pLPVRed = m_pGIAccumRed->asRenderTarget();
 		IGXSurface *pLPVGreen = m_pGIAccumGreen->asRenderTarget();
 		IGXSurface *pLPVBlue = m_pGIAccumBlue->asRenderTarget();
@@ -1007,36 +1037,64 @@ void CRenderPipeline::renderGI()
 		IGXDepthStencilSurface *pOldSurface = pCtx->getDepthStencilSurface();
 		pCtx->unsetDepthStencilSurface();
 
-		pCtx->clear(GX_CLEAR_COLOR);
+		if(isFirstRun)
+		{
+			pCtx->clear(GX_CLEAR_COLOR);
+			isFirstRun = false;
+		}
 
+		IBaseReflectiveShadowMap *pShadow = NULL;
 		//inject VPLs into LPV grid
 		for(UINT i = 0; i < uShadowCount; ++i)
 		{
-			pShadow = m_pShadowCache->getShadow(i);
-		//	pShadow->genLPV();
+			pShadow = m_pShadowCache->getRSMShadow(i);
+			pShadow->genLPV();
 		}
 
 		pCtx->setColorTarget(NULL);
 		pCtx->setColorTarget(NULL, 1);
 		pCtx->setColorTarget(NULL, 2);
 
-#if 0
-		auto pTarget = m_pGBufferColor->asRenderTarget();
-		m_pDevice->setColorTarget(pAmbientSurf);
-		mem_release(pTarget);
-		for(UINT i = 0; i < uShadowCount; ++i)
+		const bool *dev_lpv_points = GET_PCVAR_BOOL("dev_lpv_points");
+		if(*dev_lpv_points)
 		{
-			pShadow = m_pShadowCache->getShadow(i);
-			pShadow->genLPV(true);
+			auto pTarget = m_pGBufferColor->asRenderTarget();
+			pCtx->setColorTarget(pAmbientSurf);
+			mem_release(pTarget);
+			for(UINT i = 0; i < uShadowCount; ++i)
+			{
+				pShadow = m_pShadowCache->getRSMShadow(i);
+				pShadow->genLPV(true);
+			}
+			pCtx->setColorTarget(NULL);
 		}
-		m_pDevice->setColorTarget(NULL);
-#endif
 
 		pCtx->setDepthStencilSurface(pOldSurface);
 		mem_release(pOldSurface);
 
 		//break;
 	}
+	if(isFirstRun)
+	{
+		IGXSurface *pLPVRed = m_pGIAccumRed->asRenderTarget();
+		IGXSurface *pLPVGreen = m_pGIAccumGreen->asRenderTarget();
+		IGXSurface *pLPVBlue = m_pGIAccumBlue->asRenderTarget();
+
+		pCtx->setColorTarget(pLPVRed);
+		pCtx->setColorTarget(pLPVGreen, 1);
+		pCtx->setColorTarget(pLPVBlue, 2);
+
+		mem_release(pLPVRed);
+		mem_release(pLPVGreen);
+		mem_release(pLPVBlue);
+
+		pCtx->clear(GX_CLEAR_COLOR);
+		isFirstRun = false;
+
+		pCtx->setColorTarget(NULL);
+		pCtx->setColorTarget(NULL, 1);
+		pCtx->setColorTarget(NULL, 2);
+	}
 
 	SGCore_ShaderUnBind();
 
@@ -1048,7 +1106,7 @@ void CRenderPipeline::renderGI()
 	{
 		SGCore_ShaderBind(m_idLPVPropagateShader);
 
-		for(UINT i = 0; i < 1/*6*/; ++i)
+		for(UINT i = 0; i < 6/*6*/; ++i)
 		{
 			pCtx->setCSTexture(m_pGIAccumRed, 0);
 			pCtx->setCSTexture(m_pGIAccumGreen, 1);
@@ -1069,8 +1127,6 @@ void CRenderPipeline::renderGI()
 			pCtx->setCSTexture(NULL, 2);
 
 
-
-
 			pCtx->setCSTexture(m_pGIAccumRed2, 0);
 			pCtx->setCSTexture(m_pGIAccumGreen2, 1);
 			pCtx->setCSTexture(m_pGIAccumBlue2, 2);
@@ -1108,6 +1164,8 @@ void CRenderPipeline::renderGI()
 
 		//SGCore_ShaderSetVRF(SHADER_TYPE_PIXEL, idshader, "g_vViewPos", &gdata::vConstCurrCamPos);
 
+	//	pCtx->setPSConstant(m_pCameraShaderDataVS, 8);
+
 		SGCore_ShaderBind(idshaderkit);
 
 		pCtx->setSamplerState(gdata::rstates::pSamplerPointClamp, 0);
@@ -1169,6 +1227,8 @@ void CRenderPipeline::renderGI()
 
 	pCtx->setColorTarget(pBackBuf);
 	mem_release(pBackBuf);
+
+	//Sleep(16);
 }
 void CRenderPipeline::renderPostprocessMain()
 {
diff --git a/source/render/ShadowCache.cpp b/source/render/ShadowCache.cpp
index 3ac1f20d7..b65f5a153 100644
--- a/source/render/ShadowCache.cpp
+++ b/source/render/ShadowCache.cpp
@@ -33,7 +33,11 @@ private:
 
 CShadowCache::CShadowCache(IXRenderPipeline *pRenderPipeline, IXMaterialSystem *pMaterialSystem):
 	m_pRenderPipeline(pRenderPipeline),
-	m_pMaterialSystem(pMaterialSystem)
+	m_pMaterialSystem(pMaterialSystem),
+	m_shadowMaps(m_aFrameLights, m_aReadyMaps, m_pRenderPipeline, LRT_LIGHT),
+	m_shadowCubeMaps(m_aFrameLights, m_aReadyMaps, m_pRenderPipeline, LRT_LIGHT),
+	m_reflectiveShadowMaps(m_aFrameRSMLights, m_aReadyReflectiveMaps, m_pRenderPipeline, LRT_LPV),
+	m_reflectiveShadowCubeMaps(m_aFrameRSMLights, m_aReadyReflectiveMaps, m_pRenderPipeline, LRT_LPV)
 {
 	m_pShadowSizeCvarListener = new CRShadowSizeCvarListener(Core_GetIXCore(), this);
 	Core_GetIXCore()->getEventChannel<XEventCvarChanged>(EVENT_CVAR_CHANGED_GUID)->addListener(m_pShadowSizeCvarListener);
@@ -73,13 +77,13 @@ CShadowCache::CShadowCache(IXRenderPipeline *pRenderPipeline, IXMaterialSystem *
 	//rasterizerDesc.cullMode = GXCULL_FRONT;
 	//rasterizerDesc.useConservativeRasterization = true;
 	m_pRasterizerConservative = m_pRenderPipeline->getDevice()->createRasterizerState(&rasterizerDesc);
-
 }
 CShadowCache::~CShadowCache()
 {
 	Core_GetIXCore()->getEventChannel<XEventCvarChanged>(EVENT_CVAR_CHANGED_GUID)->removeListener(m_pShadowSizeCvarListener);
 	mem_delete(m_pShadowSizeCvarListener);
 	mem_delete(m_pShadowPSSM);
+	mem_delete(m_pReflectiveShadowSun);
 }
 
 void CShadowCache::setLightsCount(UINT uPoints, UINT uSpots, bool hasGlobal)
@@ -96,7 +100,7 @@ void CShadowCache::setLightsCount(UINT uPoints, UINT uSpots, bool hasGlobal)
 
 	size_t stPointsMemory = uPoints * CShadowCubeMap::GetMapMemory(uPointShadowmapSize);
 	size_t stSpotsMemory = uSpots * CShadowMap::GetMapMemory(uSpotShadowmapSize);
-	size_t stSunMemory = 0;
+	size_t stSunMemory = CShadowPSSM::GetMapMemory(uSunShadowmapSize);
 
 	size_t stTotal = stPointsMemory + stSpotsMemory;
 	stMaxMem -= stSunMemory;
@@ -125,46 +129,84 @@ void CShadowCache::setLightsCount(UINT uPoints, UINT uSpots, bool hasGlobal)
 			}
 		}
 	}
+	
+	m_shadowMaps.setSize(uSpots, uSpotShadowmapSize);
+	m_shadowCubeMaps.setSize(uPoints, uPointShadowmapSize);
+
+	m_aReadyMaps.resizeFast(uSpots + uPoints + (hasGlobal ? 1 : 0));
 
-	if(m_aShadowMaps.size() != uSpots)
+	if(hasGlobal)
 	{
-		UINT i = m_aShadowMaps.size();
-		m_aShadowMaps.resizeFast(uSpots);
-		for(; i < uSpots; ++i)
+		if(!m_pShadowPSSM)
 		{
-			m_aShadowMaps[i].map.init(m_pRenderPipeline->getDevice(), uSpotShadowmapSize);
+			m_pShadowPSSM = new ShadowPSSM();
+			m_pShadowPSSM->map.init(m_pRenderPipeline->getDevice(), uSunShadowmapSize);
 		}
 	}
+	else
+	{
+		mem_delete(m_pShadowPSSM);
+	}
+}
+
+void CShadowCache::setRSMLightsCount(UINT uPoints, UINT uSpots, bool hasGlobal)
+{
+	//static const float *s_pfRRSMQuality = GET_PCVAR_FLOAT("r_rsm_quality");
+	const UINT uSpotShadowmapSize = (UINT)(RSM_SPOT_SIZE);
+	const UINT uPointShadowmapSize = (UINT)(RSM_POINT_SIZE);
+	const UINT uSunShadowmapSize = (UINT)(RSM_SUN_SIZE);
+
+	//size_t stMaxMem = (size_t)(m_pRenderPipeline->getDevice()->getAdapterDesc()->sizeTotalMemory * *s_pfRSMMaxMemory);
+	size_t stMaxMem = (size_t)(m_pRenderPipeline->getDevice()->getAdapterDesc()->sizeTotalMemory * 0.3f);
 
-	if(m_aShadowCubeMaps.size() != uPoints)
+	size_t stPointsMemory = uPoints * CReflectiveShadowCubeMap::GetMapMemory(uPointShadowmapSize);
+	size_t stSpotsMemory = uSpots * CReflectiveShadowMap::GetMapMemory(uSpotShadowmapSize);
+	size_t stSunMemory = CReflectiveShadowSun::GetMapMemory(uSunShadowmapSize);
+
+	size_t stTotal = stPointsMemory + stSpotsMemory;
+	stMaxMem -= stSunMemory;
+
+	if(stTotal > stMaxMem)
 	{
-		UINT i = m_aShadowCubeMaps.size();
-		m_aShadowCubeMaps.resizeFast(uPoints);
-		m_aShadowCubeMapsQueue.resizeFast(uPoints);
-		for(; i < uPoints; ++i)
+		float fCoeff = (float)stMaxMem / (float)stTotal;
+
+		stPointsMemory = (size_t)(stPointsMemory * fCoeff);
+		stSpotsMemory = (size_t)(stSpotsMemory * fCoeff);
+
+		if(uSpots)
 		{
-			m_aShadowCubeMaps[i].map.init(m_pRenderPipeline->getDevice(), uPointShadowmapSize);
+			uSpots = (UINT)(stSpotsMemory / CReflectiveShadowMap::GetMapMemory(uSpotShadowmapSize));
+			if(!uSpots)
+			{
+				uSpots = 1;
+			}
 		}
-
-		for(i = 0; i < uPoints; ++i)
+		if(uPoints)
 		{
-			m_aShadowCubeMapsQueue[i] = &m_aShadowCubeMaps[i];
+			uPoints = (UINT)(stPointsMemory / CReflectiveShadowCubeMap::GetMapMemory(uPointShadowmapSize));
+			if(!uPoints)
+			{
+				uPoints = 1;
+			}
 		}
 	}
 
-	m_aReadyMaps.resizeFast(uSpots + uPoints + (hasGlobal ? 1 : 0));
+	m_reflectiveShadowMaps.setSize(uSpots, uSpotShadowmapSize);
+	m_reflectiveShadowCubeMaps.setSize(uPoints, uPointShadowmapSize);
+
+	m_aReadyReflectiveMaps.resizeFast(uSpots + uPoints + (hasGlobal ? 1 : 0));
 
 	if(hasGlobal)
 	{
-		if(!m_pShadowPSSM)
+		if(!m_pReflectiveShadowSun)
 		{
-			m_pShadowPSSM = new ShadowPSSM();
-			m_pShadowPSSM->map.init(m_pRenderPipeline->getDevice(), uSunShadowmapSize);
+			m_pReflectiveShadowSun = new ReflectiveShadowSun();
+			m_pReflectiveShadowSun->map.init(m_pRenderPipeline->getDevice(), uSunShadowmapSize);
 		}
 	}
 	else
 	{
-		mem_delete(m_pShadowPSSM);
+		mem_delete(m_pReflectiveShadowSun);
 	}
 }
 
@@ -172,27 +214,27 @@ void CShadowCache::nextFrame()
 {
 	++m_uCurrentFrame;
 	m_aFrameLights.clearFast();
+	m_aFrameRSMLights.clearFast();
 	m_isFirstBunch = true;
-
-	{
-		ShadowCubeMap *pSM;
-		for(UINT i = 0, l = m_aShadowCubeMaps.size(); i < l; ++i)
-		{
-			pSM = &m_aShadowCubeMaps[i];
-
-			if(pSM->uLastUsed != UINT_MAX)
-			{
-				++pSM->uLastUsed;
-			}
-		}
-	}
+	
+	m_shadowMaps.updateLastUsed();
+	m_shadowCubeMaps.updateLastUsed();
+	m_reflectiveShadowMaps.updateLastUsed();
+	m_reflectiveShadowCubeMaps.updateLastUsed();
 }
 
 void CShadowCache::addLight(IXLight *pLight)
 {
+	pLight->testDirty();
 	m_aFrameLights.push_back(pLight);
 }
 
+void CShadowCache::addRSMLight(IXLight *pLight)
+{
+	pLight->testDirty();
+	m_aFrameRSMLights.push_back(pLight);
+}
+
 UINT CShadowCache::processNextBunch()
 {
 	if(!m_aFrameLights.size())
@@ -212,50 +254,21 @@ UINT CShadowCache::processNextBunch()
 	bool isSomeFound = false;
 	if(m_isFirstBunch)
 	{
-		ID id;
+		if(m_shadowMaps.processFirstBunch())
 		{
-			ShadowMap *pSM;
-			for(UINT i = 0, l = m_aShadowMaps.size(); i < l; ++i)
-			{
-				pSM = &m_aShadowMaps[i];
-				if(!pSM->isDirty && (id = m_aFrameLights.indexOf(pSM->pLight)) >= 0)
-				{
-					if(pSM->pLight->isDirty())
-					{
-						pSM->pLight->markClean();
-						pSM->isDirty = true;
-					}
-					m_aFrameLights.erase(id);
-					pSM->shouldProcess = true;
-					isSomeFound = true;
-				}
-			}
+			isSomeFound = true;
 		}
-
+		if(m_shadowCubeMaps.processFirstBunch())
 		{
-			ShadowCubeMap *pSM;
-			for(UINT i = 0, l = m_aShadowCubeMaps.size(); i < l; ++i)
-			{
-				pSM = &m_aShadowCubeMaps[i];
-				if(!pSM->isDirty && (id = m_aFrameLights.indexOf(pSM->pLight)) >= 0)
-				{
-					if(pSM->pLight->isDirty())
-					{
-						pSM->pLight->markClean();
-						pSM->isDirty = true;
-					}
-					m_aFrameLights.erase(id);
-					pSM->shouldProcess = true;
-					isSomeFound = true;
-				}
-			}
+			isSomeFound = true;
 		}
 
+		ID id;
 		if(false && m_pShadowPSSM && !m_pShadowPSSM->isDirty && (id = m_aFrameLights.indexOf(m_pShadowPSSM->pLight)) >= 0)
 		{
-			if(m_pShadowPSSM->pLight->isDirty())
+			if(m_pShadowPSSM->pLight->isDirty(LRT_LIGHT))
 			{
-				m_pShadowPSSM->pLight->markClean();
+				m_pShadowPSSM->pLight->markClean(LRT_LIGHT);
 				m_pShadowPSSM->isDirty = true;
 			}
 			m_aFrameLights.erase(id);
@@ -271,33 +284,24 @@ UINT CShadowCache::processNextBunch()
 		UINT uPoints = 0, uSpots = 0;
 		bool isFound = false;
 
-		m_aShadowCubeMapsQueue.quickSort([](const ShadowCubeMap *a, const ShadowCubeMap *b){
-			return(a->uLastUsed > b->uLastUsed);
-		});
+		m_shadowMaps.sortQueue();
+		m_shadowCubeMaps.sortQueue();
 
 		for(int i = (int)m_aFrameLights.size() - 1; i >= 0; --i)
 		{
 			switch(m_aFrameLights[i]->getType())
 			{
 			case LIGHT_TYPE_SPOT:
-				if(m_aShadowMaps.size() > uSpots)
+				if(m_shadowMaps.checkIthLight(uSpots, i))
 				{
-					m_aShadowMaps[uSpots].isDirty = true;
-					m_aShadowMaps[uSpots].shouldProcess = true;
-					m_aShadowMaps[uSpots].pLight = m_aFrameLights[i];
 					++uSpots;
-					m_aFrameLights.erase(i);
 					isFound = true;
 				}
 				break;
 			case LIGHT_TYPE_POINT:
-				if(m_aShadowCubeMapsQueue.size() > uPoints)
+				if(m_shadowCubeMaps.checkIthLight(uSpots, i))
 				{
-					m_aShadowCubeMapsQueue[uPoints]->isDirty = true;
-					m_aShadowCubeMapsQueue[uPoints]->shouldProcess = true;
-					m_aShadowCubeMapsQueue[uPoints]->pLight = m_aFrameLights[i];
 					++uPoints;
-					m_aFrameLights.erase(i);
 					isFound = true;
 				}
 				break;
@@ -319,25 +323,8 @@ UINT CShadowCache::processNextBunch()
 
 	// render shadows
 	m_pMaterialSystem->bindRenderPass(m_pRenderPassShadow);
-	{
-		ShadowMap *pSM;
-		for(UINT i = 0, l = m_aShadowMaps.size(); i < l; ++i)
-		{
-			pSM = &m_aShadowMaps[i];
-			if(pSM->shouldProcess)
-			{
-				pSM->shouldProcess = false;
-				if(pSM->isDirty)
-				{
-					pSM->map.setLight(pSM->pLight);
-					pSM->map.process(m_pRenderPipeline);
-					pSM->isDirty = false;
-				}
 
-				m_aReadyMaps.push_back({&pSM->map, pSM->pLight});
-			}
-		}
-	}
+	m_shadowMaps.processTheRest();
 
 	m_pMaterialSystem->bindGS(m_pPSSMGeometryShader[*r_pssm_splits - 1]);
 
@@ -359,33 +346,135 @@ UINT CShadowCache::processNextBunch()
 	m_pMaterialSystem->bindGS(m_pCubemapGeometryShader);
 
 	m_pMaterialSystem->bindRenderPass(m_pRenderPassShadow, 1);
+
+	m_shadowCubeMaps.processTheRest();
+
+	m_pMaterialSystem->bindGS(NULL);
+
+	Core_RMatrixSet(G_RI_MATRIX_VIEW, &mOldView);
+	Core_RMatrixSet(G_RI_MATRIX_PROJECTION, &mOldProj);
+
+	return(m_aReadyMaps.size());
+}
+
+UINT CShadowCache::processNextRSMBunch()
+{
+	if(!m_aFrameRSMLights.size())
 	{
-		ShadowCubeMap *pSM;
-		for(UINT i = 0, l = m_aShadowCubeMaps.size(); i < l; ++i)
+		return(0);
+	}
+
+	SMMATRIX mOldView, mOldProj;
+	Core_RMatrixGet(G_RI_MATRIX_VIEW, &mOldView);
+	Core_RMatrixGet(G_RI_MATRIX_PROJECTION, &mOldProj);
+
+	m_pRenderPipeline->getDevice()->getThreadContext()->setDepthStencilState(NULL);
+	m_pRenderPipeline->getDevice()->getThreadContext()->setRasterizerState(m_pRasterizerConservative);
+
+	bool isSomeFound = false;
+	if(m_isFirstRSMBunch)
+	{
+		if(m_reflectiveShadowMaps.processFirstBunch())
+		{
+			isSomeFound = true;
+		}
+		if(m_reflectiveShadowCubeMaps.processFirstBunch())
+		{
+			isSomeFound = true;
+		}
+
+		ID id;
+		if(false && m_pReflectiveShadowSun && !m_pReflectiveShadowSun->isDirty && (id = m_aFrameRSMLights.indexOf(m_pReflectiveShadowSun->pLight)) >= 0)
+		{
+			if(m_pReflectiveShadowSun->pLight->isDirty(LRT_LPV))
+			{
+				m_pReflectiveShadowSun->pLight->markClean(LRT_LPV);
+				m_pReflectiveShadowSun->isDirty = true;
+			}
+			m_aFrameRSMLights.erase(id);
+			m_pReflectiveShadowSun->shouldProcess = true;
+			isSomeFound = true;
+		}
+
+		m_isFirstBunch = false;
+	}
+
+	if(!isSomeFound)
+	{
+		UINT uPoints = 0, uSpots = 0;
+		bool isFound = false;
+
+		m_reflectiveShadowMaps.sortQueue();
+		m_reflectiveShadowCubeMaps.sortQueue();
+
+		for(int i = (int)m_aFrameRSMLights.size() - 1; i >= 0; --i)
 		{
-			pSM = &m_aShadowCubeMaps[i];
-			if(pSM->shouldProcess)
+			switch(m_aFrameRSMLights[i]->getType())
 			{
-				pSM->uLastUsed = 0;
-				pSM->shouldProcess = false;
-				if(pSM->isDirty)
+			case LIGHT_TYPE_SPOT:
+				if(m_reflectiveShadowMaps.checkIthLight(uSpots, i))
 				{
-					pSM->map.setLight(pSM->pLight);
-					pSM->map.process(m_pRenderPipeline);
-					pSM->isDirty = false;
+					++uSpots;
+					isFound = true;
 				}
-
-				m_aReadyMaps.push_back({&pSM->map, pSM->pLight});
+				break;
+			case LIGHT_TYPE_POINT:
+				if(m_reflectiveShadowCubeMaps.checkIthLight(uSpots, i))
+				{
+					++uPoints;
+					isFound = true;
+				}
+				break;
+			case LIGHT_TYPE_SUN:
+				m_pReflectiveShadowSun->isDirty = true;
+				m_pReflectiveShadowSun->shouldProcess = true;
+				m_pReflectiveShadowSun->pLight = m_aFrameRSMLights[i];
+				m_aFrameRSMLights.erase(i);
+				isFound = true;
+				break;
+			}
+			if(!isFound)
+			{
+				break;
 			}
 		}
 	}
+	m_aReadyReflectiveMaps.clearFast();
+
+	// render shadows
+	m_pMaterialSystem->bindRenderPass(m_pRenderPassShadow, 2);
+
+	m_reflectiveShadowMaps.processTheRest();
+
+	//m_pMaterialSystem->bindGS(m_pPSSMGeometryShader[*r_pssm_splits - 1]);
+
+	if(m_pReflectiveShadowSun && m_pReflectiveShadowSun->shouldProcess && m_pCamera)
+	{
+		m_pReflectiveShadowSun->shouldProcess = false;
+
+		if(m_pReflectiveShadowSun->isDirty)
+		{
+			m_pReflectiveShadowSun->map.setObserverCamera(m_pCamera);
+			m_pReflectiveShadowSun->map.setLight(m_pReflectiveShadowSun->pLight);
+			m_pReflectiveShadowSun->map.process(m_pRenderPipeline);
+			m_pReflectiveShadowSun->isDirty = false;
+		}
+
+		m_aReadyReflectiveMaps.push_back({&m_pReflectiveShadowSun->map, m_pReflectiveShadowSun->pLight});
+	}
+
+	m_pMaterialSystem->bindGS(m_pCubemapGeometryShader);
+
+	m_pMaterialSystem->bindRenderPass(m_pRenderPassShadow, 3);
+
+	m_reflectiveShadowCubeMaps.processTheRest();
 
 	m_pMaterialSystem->bindGS(NULL);
 
 	Core_RMatrixSet(G_RI_MATRIX_VIEW, &mOldView);
 	Core_RMatrixSet(G_RI_MATRIX_PROJECTION, &mOldProj);
 
-	return(m_aReadyMaps.size());
+	return(m_aReadyReflectiveMaps.size());
 }
 
 IXLight* CShadowCache::getLight(ID id)
@@ -394,12 +483,24 @@ IXLight* CShadowCache::getLight(ID id)
 	return(m_aReadyMaps[id].pLight);
 }
 
+IXLight* CShadowCache::getRSMLight(ID id)
+{
+	assert(ID_VALID(id) && m_aReadyReflectiveMaps.size() > (UINT)id);
+	return(m_aReadyReflectiveMaps[id].pLight);
+}
+
 IBaseShadowMap* CShadowCache::getShadow(ID id)
 {
 	assert(ID_VALID(id) && m_aReadyMaps.size() > (UINT)id);
 	return(m_aReadyMaps[id].pShadowMap);
 }
 
+IBaseReflectiveShadowMap* CShadowCache::getRSMShadow(ID id)
+{
+	assert(ID_VALID(id) && m_aReadyReflectiveMaps.size() > (UINT)id);
+	return(m_aReadyReflectiveMaps[id].pShadowMap);
+}
+
 void CShadowCache::setObserverCamera(ICamera *pCamera)
 {
 	m_pCamera = pCamera;
@@ -407,8 +508,7 @@ void CShadowCache::setObserverCamera(ICamera *pCamera)
 
 void CShadowCache::dropCaches()
 {
-	m_aShadowMaps.resizeFast(0);
-	m_aShadowCubeMaps.resizeFast(0);
-	m_aShadowCubeMapsQueue.resizeFast(0);
+	m_shadowMaps.dropCaches();
+	m_shadowCubeMaps.dropCaches();
 	mem_delete(m_pShadowPSSM);
 }
diff --git a/source/render/ShadowCache.h b/source/render/ShadowCache.h
index b7f35df6e..e25bb24f5 100644
--- a/source/render/ShadowCache.h
+++ b/source/render/ShadowCache.h
@@ -26,6 +26,7 @@ public:
 
 	//! Установка количества лампочек, инициализация кэша
 	void setLightsCount(UINT iPoints, UINT iSpots, bool hasGlobal);
+	void setRSMLightsCount(UINT iPoints, UINT iSpots, bool hasGlobal);
 
 	//! Указывает, что начался новый кадр
 	void nextFrame();
@@ -34,10 +35,14 @@ public:
 
 	//! Добавляет источник к текущему проходу, В случае отсутствия свободных слотов, возвращает false
 	void addLight(IXLight *pLight);
+	void addRSMLight(IXLight *pLight);
 
 	UINT processNextBunch();
+	UINT processNextRSMBunch();
 	IXLight *getLight(ID id);
+	IXLight *getRSMLight(ID id);
 	IBaseShadowMap *getShadow(ID id);
+	IBaseReflectiveShadowMap *getRSMShadow(ID id);
 
 protected:
 	IXRenderPipeline *m_pRenderPipeline;
@@ -50,7 +55,9 @@ protected:
 
 	UINT m_uCurrentFrame = 0;
 	Array<IXLight*> m_aFrameLights;
+	Array<IXLight*> m_aFrameRSMLights;
 	bool m_isFirstBunch = true;
+	bool m_isFirstRSMBunch = true;
 
 	struct ShadowMap
 	{
@@ -58,6 +65,7 @@ protected:
 		bool isDirty = false;
 		bool shouldProcess = false;
 		IXLight *pLight = NULL;
+		UINT uLastUsed = UINT_MAX;
 	};
 
 	struct ShadowCubeMap
@@ -78,11 +86,156 @@ protected:
 
 		SX_ALIGNED_OP_MEM2();
 	};
+	
+	struct ReflectiveShadowMap
+	{
+		CReflectiveShadowMap map;
+		bool isDirty = false;
+		bool shouldProcess = false;
+		IXLight *pLight = NULL;
+		UINT uLastUsed = UINT_MAX;
+	};
 
-	Array<ShadowMap> m_aShadowMaps;
-	Array<ShadowCubeMap> m_aShadowCubeMaps;
-	Array<ShadowCubeMap*> m_aShadowCubeMapsQueue;
-	ShadowPSSM *m_pShadowPSSM = NULL;
+	struct ReflectiveShadowCubeMap
+	{
+		CReflectiveShadowCubeMap map;
+		bool isDirty = false;
+		bool shouldProcess = false;
+		IXLight *pLight = NULL;
+		UINT uLastUsed = UINT_MAX;
+	};
+
+	struct ReflectiveShadowSun
+	{
+		CReflectiveShadowSun map;
+		bool isDirty = false;
+		bool shouldProcess = false;
+		IXLight *pLight = NULL;
+
+		SX_ALIGNED_OP_MEM2();
+	};
+
+
+	template<class T, class R> struct Cache
+	{
+	private:
+		IXRenderPipeline *m_pRenderPipeline;
+		Array<T> m_aMaps;
+		Array<T*> m_aMapsQueue;
+		LIGHT_RENDER_TYPE m_renderType;
+		Array<IXLight*> &m_aFrameLights;
+		Array<R> &m_aReadyMaps;
+	public:
+		Cache(Array<IXLight*> &aFrameLights, Array<R> &aReadyMaps, IXRenderPipeline *pRenderPipeline, LIGHT_RENDER_TYPE renderType):
+			m_aFrameLights(aFrameLights),
+			m_aReadyMaps(aReadyMaps),
+			m_pRenderPipeline(pRenderPipeline),
+			m_renderType(renderType)
+		{
+		}
+		void setRenderType(LIGHT_RENDER_TYPE renderType)
+		{
+			m_renderType = renderType;
+		}
+		void setSize(UINT uSize, UINT uMapSize)
+		{
+			if(m_aMaps.size() != uSize)
+			{
+				UINT i = m_aMaps.size();
+				m_aMaps.resizeFast(uSize);
+				m_aMapsQueue.resizeFast(uSize);
+				for(; i < uSize; ++i)
+				{
+					m_aMaps[i].map.init(m_pRenderPipeline->getDevice(), uMapSize);
+				}
+
+				for(i = 0; i < uSize; ++i)
+				{
+					m_aMapsQueue[i] = &m_aMaps[i];
+				}
+			}
+		}
+		void updateLastUsed()
+		{
+			T *pSM;
+			for(UINT i = 0, l = m_aMaps.size(); i < l; ++i)
+			{
+				pSM = &m_aMaps[i];
+
+				if(pSM->uLastUsed != UINT_MAX)
+				{
+					++pSM->uLastUsed;
+				}
+			}
+		}
+
+		bool processFirstBunch()
+		{
+			T *pSM;
+			ID id;
+			bool isSomeFound = false;
+			for(UINT i = 0, l = m_aMaps.size(); i < l; ++i)
+			{
+				pSM = &m_aMaps[i];
+				if(!pSM->isDirty && (id = m_aFrameLights.indexOf(pSM->pLight)) >= 0)
+				{
+					if(pSM->pLight->isDirty(m_renderType))
+					{
+						pSM->pLight->markClean(m_renderType);
+						pSM->isDirty = true;
+					}
+					m_aFrameLights.erase(id);
+					pSM->shouldProcess = true;
+					isSomeFound = true;
+				}
+			}
+			return(isSomeFound);
+		}
+		void sortQueue()
+		{
+			m_aMapsQueue.quickSort([](const T *a, const T *b){
+				return(a->uLastUsed > b->uLastUsed);
+			});
+		}
+		bool checkIthLight(UINT uMap, int i)
+		{
+			if(m_aMapsQueue.size() > uMap)
+			{
+				m_aMapsQueue[uMap]->isDirty = true;
+				m_aMapsQueue[uMap]->shouldProcess = true;
+				m_aMapsQueue[uMap]->pLight = m_aFrameLights[i];
+				m_aFrameLights.erase(i);
+				return(true);
+			}
+			return(false);
+		}
+		void processTheRest()
+		{
+			T *pSM;
+			for(UINT i = 0, l = m_aMaps.size(); i < l; ++i)
+			{
+				pSM = &m_aMaps[i];
+				if(pSM->shouldProcess)
+				{
+					pSM->uLastUsed = 0;
+					pSM->shouldProcess = false;
+					if(pSM->isDirty)
+					{
+						pSM->map.setLight(pSM->pLight);
+						pSM->map.process(m_pRenderPipeline);
+						pSM->isDirty = false;
+					}
+
+					m_aReadyMaps.push_back({&pSM->map, pSM->pLight});
+				}
+			}
+		}
+		void dropCaches()
+		{
+			m_aMaps.resizeFast(0);
+			m_aMapsQueue.resizeFast(0);
+		}
+	};
 
 	struct ReadyShadows
 	{
@@ -92,6 +245,22 @@ protected:
 
 	Array<ReadyShadows> m_aReadyMaps;
 
+	struct ReadyReflectiveShadows
+	{
+		IBaseReflectiveShadowMap *pShadowMap;
+		IXLight *pLight;
+	};
+
+	Array<ReadyReflectiveShadows> m_aReadyReflectiveMaps;
+
+	Cache<ShadowMap, ReadyShadows> m_shadowMaps;
+	Cache<ShadowCubeMap, ReadyShadows> m_shadowCubeMaps;
+	Cache<ReflectiveShadowMap, ReadyReflectiveShadows> m_reflectiveShadowMaps;
+	Cache<ReflectiveShadowCubeMap, ReadyReflectiveShadows> m_reflectiveShadowCubeMaps;
+
+	ShadowPSSM *m_pShadowPSSM = NULL;
+	ReflectiveShadowSun *m_pReflectiveShadowSun = NULL;
+
 	//XGeometryShaderHandler *m_pRSMGeometryShader = NULL;
 	XGeometryShaderHandler *m_pCubemapGeometryShader = NULL;
 	XGeometryShaderHandler *m_pPSSMGeometryShader[PSSM_MAX_SPLITS];
diff --git a/source/render/shadow.cpp b/source/render/shadow.cpp
index da8630757..e2adbab0e 100644
--- a/source/render/shadow.cpp
+++ b/source/render/shadow.cpp
@@ -774,11 +774,11 @@ void CShadowPSSM::genShadow(IGXTexture2D *pShadowMap, IGXTexture2D *pGBufferDept
 
 //##########################################################################
 
-CReflectiveShadowPSSM::CReflectiveShadowPSSM()
+CReflectiveShadowSun::CReflectiveShadowSun()
 {
 }
 
-CReflectiveShadowPSSM::~CReflectiveShadowPSSM()
+CReflectiveShadowSun::~CReflectiveShadowSun()
 {
 	mem_release(m_pSamplerComparisonLinearClamp);
 	mem_release(m_pDepthStencilSurface);
@@ -798,7 +798,7 @@ CReflectiveShadowPSSM::~CReflectiveShadowPSSM()
 	}
 }
 
-UINT CReflectiveShadowPSSM::GetMapMemory(UINT uSize)
+UINT CReflectiveShadowSun::GetMapMemory(UINT uSize)
 {
 	//GXFMT_R32F - shadow
 	//GXFMT_A8R8G8B8 - xyz:color; w:???
@@ -807,12 +807,12 @@ UINT CReflectiveShadowPSSM::GetMapMemory(UINT uSize)
 	return(uSize * uSize * 12 * PSSM_MAX_SPLITS);
 }
 
-void CReflectiveShadowPSSM::setObserverCamera(ICamera *pCamera)
+void CReflectiveShadowSun::setObserverCamera(ICamera *pCamera)
 {
 	m_pCamera = pCamera;
 }
 
-void CReflectiveShadowPSSM::init(IGXDevice *pContext, UINT uSize)
+void CReflectiveShadowSun::init(IGXDevice *pContext, UINT uSize)
 {
 	m_pDevice = pContext;
 
@@ -882,12 +882,12 @@ void CReflectiveShadowPSSM::init(IGXDevice *pContext, UINT uSize)
 	m_pCameraShaderDataVS = m_pDevice->createConstantBuffer(sizeof(m_cameraShaderData.vs));
 }
 
-void CReflectiveShadowPSSM::setLight(IXLight *pLight)
+void CReflectiveShadowSun::setLight(IXLight *pLight)
 {
 	m_pLight = pLight;
 }
 
-void CReflectiveShadowPSSM::process(IXRenderPipeline *pRenderPipeline)
+void CReflectiveShadowSun::process(IXRenderPipeline *pRenderPipeline)
 {
 	assert(m_pLight && m_pLight->getType() == LIGHT_TYPE_SUN);
 	if(!m_pDevice)
@@ -960,6 +960,10 @@ void CReflectiveShadowPSSM::process(IXRenderPipeline *pRenderPipeline)
 #endif
 }
 
+void CReflectiveShadowSun::genLPV(bool isDebug)
+{
+}
+
 //##########################################################################
 
 CShadowCubeMap::CShadowCubeMap()
diff --git a/source/render/shadow.h b/source/render/shadow.h
index 6ef64f394..4d56c2612 100644
--- a/source/render/shadow.h
+++ b/source/render/shadow.h
@@ -15,6 +15,10 @@ See the license in LICENSE
 #include <gcore/sxgcore.h>
 #include <light/IXLight.h>
 
+#define RSM_POINT_SIZE 32
+#define RSM_SPOT_SIZE 32
+#define RSM_SUN_SIZE 32
+
 class IXTexture;
 class IBaseShadowMap
 {
@@ -258,11 +262,11 @@ private:
 
 //##########################################################################
 
-class CReflectiveShadowPSSM: public IBaseShadowMap
+class CReflectiveShadowSun: public IBaseReflectiveShadowMap
 {
 public:
-	CReflectiveShadowPSSM();
-	~CReflectiveShadowPSSM();
+	CReflectiveShadowSun();
+	~CReflectiveShadowSun();
 
 	SX_ALIGNED_OP_MEM2();
 
@@ -273,7 +277,7 @@ public:
 	void setObserverCamera(ICamera *pCamera);
 	void setLight(IXLight *pLight);
 	void process(IXRenderPipeline *pRenderPipeline);
-	//	void genLPV(bool isDebug = false) override;
+	void genLPV(bool isDebug = false) override;
 
 private:
 	IGXDevice *m_pDevice = NULL;
-- 
GitLab