void FDeferredShadingSceneRenderer::PropagateLPVs()
{
	for(int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
	{
		FViewInfo& View = Views[ViewIndex];

		FSceneViewState* ViewState = (FSceneViewState*)Views[ViewIndex].State;
		if(ViewState)
		{
			FLightPropagationVolume* LightPropagationVolume = ViewState->GetLightPropagationVolume();

			if(LightPropagationVolume)
			{
				SCOPED_DRAW_EVENT(UpdateLPVs, DEC_SCENE_ITEMS);
				SCOPE_CYCLE_COUNTER(STAT_UpdateLPVs);
				LightPropagationVolume->Propagate();
			}
		}
	}
}
void FRCPassPostProcessLpvIndirect::Process(FRenderingCompositePassContext& Context)
{
	SCOPED_DRAW_EVENT(PostProcessLpvIndirect, DEC_SCENE_ITEMS);

	const FPostProcessSettings& PostprocessSettings = Context.View.FinalPostProcessSettings;
	const FSceneView& View = Context.View;
	const FSceneViewFamily& ViewFamily = *(View.Family);

	FIntRect SrcRect = View.ViewRect;
	// todo: view size should scale with input texture size so we can do SSAO in half resolution as well
	FIntRect DestRect = View.ViewRect;
	FIntPoint DestSize = DestRect.Size();

	uint32 NumReflectionCaptures = ViewFamily.Scene->GetRenderScene()->ReflectionSceneData.RegisteredReflectionCaptures.Num();

	const FSceneRenderTargetItem& DestColorRenderTarget = GSceneRenderTargets.SceneColor->GetRenderTargetItem();

	// Set the view family's render target/viewport.
	FTextureRHIParamRef RenderTargets[] =
	{
		DestColorRenderTarget.TargetableTexture,
	};

	// Set the view family's render target/viewport.
	RHISetRenderTargets(1, RenderTargets, GSceneRenderTargets.GetSceneDepthSurface(), 0, NULL);

	Context.SetViewportAndCallRHI(View.ViewRect);

	// set the state
	if ( ViewFamily.EngineShowFlags.LpvLightingOnly )
	{
		RHISetBlendState( TStaticBlendState<>::GetRHI() );
	}
	else
	{
		// additive blending
		RHISetBlendState(TStaticBlendState<CW_RGB,BO_Add,BF_One,BF_One>::GetRHI());
	}
	RHISetRasterizerState(TStaticRasterizerState<>::GetRHI());
	RHISetDepthStencilState(TStaticDepthStencilState<false,CF_Always>::GetRHI());

	TShaderMapRef<FPostProcessVS> VertexShader(GetGlobalShaderMap());

	FSceneViewState* ViewState = (FSceneViewState*)View.State;
	FLightPropagationVolume* Lpv = NULL;
	bool bUseLpv = false;
	if ( ViewState )
	{
		Lpv = ViewState->GetLightPropagationVolume();

		bUseLpv = Lpv && PostprocessSettings.LPVIntensity > 0.0f;
	}

	if ( !bUseLpv )
	{
		return;
	}

	TShaderMapRef<FPostProcessLpvIndirectPS> PixelShader(GetGlobalShaderMap());

	static FGlobalBoundShaderState BoundShaderState;

	// call it once after setting up the shader data to avoid the warnings in the function
	SetGlobalBoundShaderState(BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader);

	FLpvReadUniformBufferParameters	LpvReadUniformBufferParams;
	FLpvReadUniformBufferRef LpvReadUniformBuffer;

	LpvReadUniformBufferParams = Lpv->GetReadUniformBufferParams();
	LpvReadUniformBuffer = FLpvReadUniformBufferRef::CreateUniformBufferImmediate( LpvReadUniformBufferParams, UniformBuffer_SingleUse ); 

#if LPV_VOLUME_TEXTURE
	FTextureRHIParamRef LpvBufferSrvs[7];
	for ( int i = 0; i < 7; i++ ) 
	{
		LpvBufferSrvs[i] = Lpv->GetLpvBufferSrv(i);
	}
	PixelShader->SetParameters(LpvBufferSrvs, LpvReadUniformBuffer, Context );
#else
	FShaderResourceViewRHIParamRef LpvBufferSrv = Lpv->GetLpvBufferSrv();
	PixelShader->SetParameters(LpvBufferSrv, LpvReadUniformBuffer, Context );
#endif

	// Draw a quad mapping scene color to the view's render target
	DrawRectangle( 
		0, 0,
		View.ViewRect.Width(), View.ViewRect.Height(),
		View.ViewRect.Min.X, View.ViewRect.Min.Y, 
		View.ViewRect.Width(), View.ViewRect.Height(),
		View.ViewRect.Size(),
		GSceneRenderTargets.SceneColor->GetDesc().Extent);

	RHICopyToResolveTarget(DestColorRenderTarget.TargetableTexture, DestColorRenderTarget.ShaderResourceTexture, false, FResolveParams());
}
void FRCPassPostProcessLpvIndirect::Process(FRenderingCompositePassContext& Context)
{
	FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(Context.RHICmdList);

	{
		FRenderingCompositeOutput* OutputOfMyInput = GetInput(ePId_Input0)->GetOutput();
		PassOutputs[0].PooledRenderTarget = OutputOfMyInput->PooledRenderTarget;
		OutputOfMyInput->RenderTargetDesc.DebugName = PassOutputs[0].RenderTargetDesc.DebugName;
		PassOutputs[0].RenderTargetDesc = OutputOfMyInput->RenderTargetDesc;

		check(PassOutputs[0].RenderTargetDesc.Extent.X);
		check(PassOutputs[0].RenderTargetDesc.Extent.Y);
	}

	const FFinalPostProcessSettings& PostprocessSettings = Context.View.FinalPostProcessSettings;
	const FSceneView& View = Context.View;

	FSceneViewState* ViewState = (FSceneViewState*)View.State;

	if(!ViewState)
	{
		return;
	}

	// This check should be inclusive to stereo views
	const bool bIncludeStereoViews = true;
	FLightPropagationVolume* Lpv = ViewState->GetLightPropagationVolume(Context.GetFeatureLevel(), bIncludeStereoViews);

	const FLightPropagationVolumeSettings& LPVSettings = PostprocessSettings.BlendableManager.GetSingleFinalDataConst<FLightPropagationVolumeSettings>();

	if(!Lpv || LPVSettings.LPVIntensity == 0.0f)
	{
		return;
	}

	const FSceneViewFamily& ViewFamily = *(View.Family);

	FIntRect SrcRect = View.ViewRect;
	// todo: view size should scale with input texture size so we can do SSAO in half resolution as well
	FIntRect DestRect = View.ViewRect;
	FIntPoint DestSize = DestRect.Size();

	const bool bMixing = CVarLPVMixing.GetValueOnRenderThread() != 0;
	// Apply specular separately if we're mixing reflection environment with indirect lighting
	const bool bApplySeparateSpecularRT = View.Family->EngineShowFlags.ReflectionEnvironment && bMixing;

	const FSceneRenderTargetItem& DestColorRenderTarget = SceneContext.GetSceneColor()->GetRenderTargetItem();
	const FSceneRenderTargetItem& DestSpecularRenderTarget = SceneContext.LightAccumulation->GetRenderTargetItem();

	const FSceneRenderTargetItem& DestDirectionalOcclusionRenderTarget = SceneContext.DirectionalOcclusion->GetRenderTargetItem();

	// Make sure the LPV Update has completed
	Lpv->InsertGPUWaitForAsyncUpdate(Context.RHICmdList);

	if ( LPVSettings.LPVDirectionalOcclusionIntensity > 0.0001f )
	{
		DoDirectionalOcclusionPass(Context);
	}

	FTextureRHIParamRef RenderTargets[2];
	RenderTargets[0] = DestColorRenderTarget.TargetableTexture;
	RenderTargets[1] = DestSpecularRenderTarget.TargetableTexture;

	// Set the view family's render target/viewport.
	// If specular not applied: set only color target
	uint32 NumRenderTargets = 1; 
	if ( bApplySeparateSpecularRT ) 
	{
		NumRenderTargets = 2;
	}

	SetRenderTargets(Context.RHICmdList, NumRenderTargets, RenderTargets, 0, ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilNop);

	Context.SetViewportAndCallRHI(View.ViewRect);

	// set the state
	Context.RHICmdList.SetBlendState(TStaticBlendState<CW_RGB,BO_Add,BF_One,BF_One,BO_Add,BF_One,BF_One>::GetRHI());
	Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
	Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false,CF_Always>::GetRHI());

	TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());

	TShaderMapRef< TPostProcessLpvIndirectPS<false> >	  PixelShaderNoSpecular(Context.GetShaderMap());
	TShaderMapRef< TPostProcessLpvIndirectPS<true> >	  PixelShaderWithSpecular(Context.GetShaderMap());

	FPostProcessLpvIndirectPS* PixelShader = NULL;
	int BoundShaderIndex = -1;
	if ( bApplySeparateSpecularRT )
	{
		PixelShader = (FPostProcessLpvIndirectPS*)*PixelShaderWithSpecular;
		BoundShaderIndex = 0;
	}
	else
	{
		PixelShader = (FPostProcessLpvIndirectPS*)*PixelShaderNoSpecular;
		BoundShaderIndex = 1;
	}
	static FGlobalBoundShaderState BoundShaderState[2];

	// call it once after setting up the shader data to avoid the warnings in the function
	SetGlobalBoundShaderState(Context.RHICmdList, Context.GetFeatureLevel(), BoundShaderState[BoundShaderIndex], GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, PixelShader);

	FLpvReadUniformBufferParameters	LpvReadUniformBufferParams;
	FLpvReadUniformBufferRef LpvReadUniformBuffer;

	LpvReadUniformBufferParams = Lpv->GetReadUniformBufferParams();
	LpvReadUniformBuffer = FLpvReadUniformBufferRef::CreateUniformBufferImmediate( LpvReadUniformBufferParams, UniformBuffer_SingleDraw ); 

	FTextureRHIParamRef LpvBufferSrvs[7];
	for ( int i = 0; i < 7; i++ ) 
	{
		LpvBufferSrvs[i] = Lpv->GetLpvBufferSrv(i);
	}

	PixelShader->SetParameters( LpvBufferSrvs, Lpv->GetAOVolumeTextureSRV(), LpvReadUniformBuffer, Context );

	{
		SCOPED_DRAW_EVENT(Context.RHICmdList, PostProcessLpvIndirect );

		DrawPostProcessPass(
			Context.RHICmdList,
			0, 0,
			View.ViewRect.Width(), View.ViewRect.Height(),
			View.ViewRect.Min.X, View.ViewRect.Min.Y,
			View.ViewRect.Width(), View.ViewRect.Height(),
			View.ViewRect.Size(),
			SceneContext.GetBufferSizeXY(),
			*VertexShader,
			View.StereoPass,
			Context.HasHmdMesh());

		Context.RHICmdList.CopyToResolveTarget(DestColorRenderTarget.TargetableTexture, DestColorRenderTarget.ShaderResourceTexture, false, FResolveParams());
		if(bApplySeparateSpecularRT)
		{
			Context.RHICmdList.CopyToResolveTarget(DestSpecularRenderTarget.TargetableTexture, DestSpecularRenderTarget.ShaderResourceTexture, false, FResolveParams());
		}
	}

	if ( LPVSettings.LPVDirectionalOcclusionIntensity > 0.0001f )
	{
		GRenderTargetPool.VisualizeTexture.SetCheckPoint(Context.RHICmdList, SceneContext.DirectionalOcclusion);
	}
}
void FRCPassPostProcessLpvIndirect::DoDirectionalOcclusionPass(FRenderingCompositePassContext& Context) const
{
	FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(Context.RHICmdList);

	SCOPED_DRAW_EVENT(Context.RHICmdList, PostProcessLpvDirectionalOcclusion );
	const FSceneRenderTargetItem& DestDirectionalOcclusionRenderTarget = SceneContext.DirectionalOcclusion->GetRenderTargetItem();
	const FViewInfo& View = Context.View;
	FSceneViewState* ViewState = (FSceneViewState*)View.State;

	if(!ViewState)
	{
		return;
	}

	const FFinalPostProcessSettings& PostprocessSettings = Context.View.FinalPostProcessSettings;
	const FLightPropagationVolumeSettings& LPVSettings = PostprocessSettings.BlendableManager.GetSingleFinalDataConst<FLightPropagationVolumeSettings>();
	
	FLightPropagationVolume* Lpv = ViewState->GetLightPropagationVolume(Context.GetFeatureLevel());

	if(!Lpv || LPVSettings.LPVIntensity == 0.0f)
	{
		return;
	}

	FTextureRHIParamRef RenderTarget = DestDirectionalOcclusionRenderTarget.TargetableTexture;

	SetRenderTargets(Context.RHICmdList, 1, &RenderTarget, NULL, ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilNop);

	Context.SetViewportAndCallRHI(View.ViewRect);
	Context.RHICmdList.SetBlendState( TStaticBlendState<>::GetRHI() );
	Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
	Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false,CF_Always>::GetRHI());
	TShaderMapRef<FPostProcessVS> VertexShader(View.ShaderMap);

	FLpvReadUniformBufferParameters	LpvReadUniformBufferParams;
	FLpvReadUniformBufferRef LpvReadUniformBuffer;

	TShaderMapRef< FPostProcessLpvDirectionalOcclusionPS > PixelShader(View.ShaderMap);

	static FGlobalBoundShaderState BoundShaderState;
	
	// call it once after setting up the shader data to avoid the warnings in the function
	SetGlobalBoundShaderState(Context.RHICmdList, Context.GetFeatureLevel(), BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader);

	LpvReadUniformBufferParams = Lpv->GetReadUniformBufferParams();
	LpvReadUniformBuffer = FLpvReadUniformBufferRef::CreateUniformBufferImmediate( LpvReadUniformBufferParams, UniformBuffer_SingleDraw ); 

	PixelShader->SetParameters( Lpv->GetAOVolumeTextureSRV(), LpvReadUniformBuffer, Context );

	DrawPostProcessPass(
		Context.RHICmdList,
		0, 0,
		View.ViewRect.Width(), View.ViewRect.Height(),
		View.ViewRect.Min.X, View.ViewRect.Min.Y,
		View.ViewRect.Width(), View.ViewRect.Height(),
		View.ViewRect.Size(),
		SceneContext.GetBufferSizeXY(),
		*VertexShader, 
		View.StereoPass, 
		Context.HasHmdMesh());
}
void FDeferredShadingSceneRenderer::BeginOcclusionTests(FRHICommandListImmediate& RHICmdList, bool bRenderQueries)
{
	SCOPE_CYCLE_COUNTER(STAT_BeginOcclusionTestsTime);
	int32 NumBufferedFrames = FOcclusionQueryHelpers::GetNumBufferedFrames();
	FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
	const bool bUseDownsampledDepth = SceneContext.UseDownsizedOcclusionQueries() && IsValidRef(SceneContext.SmallDepthZ) && IsValidRef(SceneContext.GetSmallDepthSurface());	

	if (bUseDownsampledDepth)
	{
		SetRenderTarget(RHICmdList, NULL, SceneContext.GetSmallDepthSurface(), ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilWrite);
	}
	else
	{
		SetRenderTarget(RHICmdList, NULL, SceneContext.GetSceneDepthSurface(), ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilWrite);
	}

	if (bRenderQueries)
	{
		RHICmdList.BeginOcclusionQueryBatch();


		// Perform occlusion queries for each view
		for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
		{
			SCOPED_DRAW_EVENT(RHICmdList, BeginOcclusionTests);
			FViewInfo& View = Views[ViewIndex];

			if (bUseDownsampledDepth)
			{
				const uint32 DownsampledX = FMath::TruncToInt(View.ViewRect.Min.X / SceneContext.GetSmallColorDepthDownsampleFactor());
				const uint32 DownsampledY = FMath::TruncToInt(View.ViewRect.Min.Y / SceneContext.GetSmallColorDepthDownsampleFactor());
				const uint32 DownsampledSizeX = FMath::TruncToInt(View.ViewRect.Width() / SceneContext.GetSmallColorDepthDownsampleFactor());
				const uint32 DownsampledSizeY = FMath::TruncToInt(View.ViewRect.Height() / SceneContext.GetSmallColorDepthDownsampleFactor());

				// Setup the viewport for rendering to the downsampled depth buffer
				RHICmdList.SetViewport(DownsampledX, DownsampledY, 0.0f, DownsampledX + DownsampledSizeX, DownsampledY + DownsampledSizeY, 1.0f);
			}
			else
			{
				RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
			}

			FSceneViewState* ViewState = (FSceneViewState*)View.State;

			if (ViewState && !View.bDisableQuerySubmissions)
			{
				// Depth tests, no depth writes, no color writes, opaque, solid rasterization wo/ backface culling.
				RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI());
				// We only need to render the front-faces of the culling geometry (this halves the amount of pixels we touch)
				RHICmdList.SetRasterizerState(View.bReverseCulling ? TStaticRasterizerState<FM_Solid, CM_CCW>::GetRHI() : TStaticRasterizerState<FM_Solid, CM_CW>::GetRHI());
				RHICmdList.SetBlendState(TStaticBlendState<CW_NONE>::GetRHI());

				// Lookup the vertex shader.
				TShaderMapRef<FOcclusionQueryVS> VertexShader(View.ShaderMap);
				SetGlobalBoundShaderState(RHICmdList, View.GetFeatureLevel(), OcclusionTestBoundShaderState, GetVertexDeclarationFVector3(), *VertexShader, NULL);
				VertexShader->SetParameters(RHICmdList, View);

				// Issue this frame's occlusion queries (occlusion queries from last frame may still be in flight)
				const uint32 QueryIndex = FOcclusionQueryHelpers::GetQueryIssueIndex(ViewState->PendingPrevFrameNumber, NumBufferedFrames);
				FSceneViewState::ShadowKeyOcclusionQueryMap& ShadowOcclusionQueryMap = ViewState->ShadowOcclusionQueryMaps[QueryIndex];

				// Clear primitives which haven't been visible recently out of the occlusion history, and reset old pending occlusion queries.
				ViewState->TrimOcclusionHistory(RHICmdList, ViewFamily.CurrentRealTime, ViewFamily.CurrentRealTime - GEngine->PrimitiveProbablyVisibleTime, ViewFamily.CurrentRealTime, ViewState->OcclusionFrameCounter);

				// Give back all these occlusion queries to the pool.
				for (TMap<FSceneViewState::FProjectedShadowKey, FRenderQueryRHIRef>::TIterator QueryIt(ShadowOcclusionQueryMap); QueryIt; ++QueryIt)
				{
					//FRenderQueryRHIParamRef Query = QueryIt.Value();
					//check( Query.GetRefCount() == 1 );
					ViewState->OcclusionQueryPool.ReleaseQuery(QueryIt.Value());
				}
				ShadowOcclusionQueryMap.Reset();

				{
					SCOPED_DRAW_EVENT(RHICmdList, ShadowFrustumQueries);

					for (TSparseArray<FLightSceneInfoCompact>::TConstIterator LightIt(Scene->Lights); LightIt; ++LightIt)
					{
						const FVisibleLightInfo& VisibleLightInfo = VisibleLightInfos[LightIt.GetIndex()];

						for (int32 ShadowIndex = 0; ShadowIndex < VisibleLightInfo.AllProjectedShadows.Num(); ShadowIndex++)
						{
							const FProjectedShadowInfo& ProjectedShadowInfo = *VisibleLightInfo.AllProjectedShadows[ShadowIndex];

							if (ProjectedShadowInfo.DependentView && ProjectedShadowInfo.DependentView != &View)
							{
								continue;
							}

							if (ProjectedShadowInfo.CascadeSettings.bOnePassPointLightShadow)
							{
								FLightSceneProxy& LightProxy = *(ProjectedShadowInfo.GetLightSceneInfo().Proxy);

								// Query one pass point light shadows separately because they don't have a shadow frustum, they have a bounding sphere instead.
								FSphere LightBounds = LightProxy.GetBoundingSphere();

								const bool bCameraInsideLightGeometry = ((FVector)View.ViewMatrices.ViewOrigin - LightBounds.Center).SizeSquared() < FMath::Square(LightBounds.W * 1.05f + View.NearClippingDistance * 2.0f);
								if (!bCameraInsideLightGeometry)
								{
									const FRenderQueryRHIRef ShadowOcclusionQuery = ViewState->OcclusionQueryPool.AllocateQuery();
									RHICmdList.BeginRenderQuery(ShadowOcclusionQuery);

									FSceneViewState::FProjectedShadowKey Key(ProjectedShadowInfo);

									checkSlow(ShadowOcclusionQueryMap.Find(Key) == NULL);
									ShadowOcclusionQueryMap.Add(Key, ShadowOcclusionQuery);

									// Draw bounding sphere
									VertexShader->SetParametersWithBoundingSphere(RHICmdList, View, LightBounds);
									StencilingGeometry::DrawVectorSphere(RHICmdList);

									RHICmdList.EndRenderQuery(ShadowOcclusionQuery);
								}
							}
							// Don't query preshadows, since they are culled if their subject is occluded.
							// Don't query if any subjects are visible because the shadow frustum will be definitely unoccluded
							else if (!ProjectedShadowInfo.IsWholeSceneDirectionalShadow() && !ProjectedShadowInfo.bPreShadow && !ProjectedShadowInfo.SubjectsVisible(View))
							{
								IssueProjectedShadowOcclusionQuery(RHICmdList, View, ProjectedShadowInfo, *VertexShader, NumBufferedFrames);
							}
						}

						// Issue occlusion queries for all per-object projected shadows that we would have rendered but were occluded last frame.
						for (int32 ShadowIndex = 0; ShadowIndex < VisibleLightInfo.OccludedPerObjectShadows.Num(); ShadowIndex++)
						{
							const FProjectedShadowInfo& ProjectedShadowInfo = *VisibleLightInfo.OccludedPerObjectShadows[ShadowIndex];
							IssueProjectedShadowOcclusionQuery(RHICmdList, View, ProjectedShadowInfo, *VertexShader, NumBufferedFrames);
						}
					}
				}

				// Don't do primitive occlusion if we have a view parent or are frozen.
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
				if (!ViewState->HasViewParent() && !ViewState->bIsFrozen)
#endif
				{
					VertexShader->SetParameters(RHICmdList, View);

					{
						SCOPED_DRAW_EVENT(RHICmdList, IndividualQueries);
						View.IndividualOcclusionQueries.Flush(RHICmdList);
					}
					{
						SCOPED_DRAW_EVENT(RHICmdList, GroupedQueries);
						View.GroupedOcclusionQueries.Flush(RHICmdList);
					}
				}
			}
		}

		RHICmdList.EndOcclusionQueryBatch();
	}

	if (bUseDownsampledDepth)
	{
		// Restore default render target
		SceneContext.BeginRenderingSceneColor(RHICmdList, ESimpleRenderTargetMode::EUninitializedColorExistingDepth, FExclusiveDepthStencil::DepthRead_StencilWrite);
	}
}