void FIndirectLightingCache::UpdateCachePrimitivesInternal(FScene* Scene, FSceneRenderer& Renderer, bool bAllowUnbuiltPreview, TMap<FIntVector, FBlockUpdateInfo>& OutBlocksToUpdate, TArray<FIndirectLightingCacheAllocation*>& OutTransitionsOverTimeToUpdate)
{
	SCOPE_CYCLE_COUNTER(STAT_UpdateIndirectLightingCachePrims);
	const TMap<FPrimitiveComponentId, FAttachmentGroupSceneInfo>& AttachmentGroups = Scene->AttachmentGroups;
	if (IndirectLightingAllowed(Scene, Renderer))
	{		
		if (bUpdateAllCacheEntries)
		{
			const uint32 PrimitiveCount = Scene->Primitives.Num();

			for (uint32 PrimitiveIndex = 0; PrimitiveIndex < PrimitiveCount; ++PrimitiveIndex)
			{
				FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->Primitives[PrimitiveIndex];
				const bool bPrecomputedLightingBufferWasDirty = PrimitiveSceneInfo->NeedsPrecomputedLightingBufferUpdate();

				UpdateCachePrimitive(AttachmentGroups, PrimitiveSceneInfo, false, true, OutBlocksToUpdate, OutTransitionsOverTimeToUpdate);

				// If it was already dirty, then the primitive is already in one of the view dirty primitive list at this point.
				// This also ensures that a primitive does not get added twice to the list, which could create an array reallocation.
				if (!bPrecomputedLightingBufferWasDirty)
				{
					PrimitiveSceneInfo->MarkPrecomputedLightingBufferDirty();

					// Check if it is visible otherwise, it will be updated next time it is visible.
					for (int32 ViewIndex = 0; ViewIndex < Renderer.Views.Num(); ViewIndex++)
					{
						FViewInfo& View = Renderer.Views[ViewIndex];

						if (View.PrimitiveVisibilityMap[PrimitiveIndex])
						{
							// Since the update can be executed on a threaded job (see GILCUpdatePrimTaskEnabled), no reallocation must happen here.
							checkSlow(View.DirtyPrecomputedLightingBufferPrimitives.Num() < View.DirtyPrecomputedLightingBufferPrimitives.Max());
							View.DirtyPrecomputedLightingBufferPrimitives.Push(PrimitiveSceneInfo);
							break; // We only need to add it in one of the view list.
						}
					}
				}
			}
		}
		else
		{
			TArray<uint32> SetBitIndices[4];
			{
				QUICK_SCOPE_CYCLE_COUNTER(STAT_UpdateCachePreWalk);
			
				for (int32 ViewIndex = 0; ViewIndex < Renderer.Views.Num(); ViewIndex++)
				{
					FViewInfo& View = Renderer.Views[ViewIndex];
					SetBitIndices[ViewIndex].Reserve(View.PrimitiveVisibilityMap.Num());

					for (FSceneSetBitIterator BitIt(View.PrimitiveVisibilityMap); BitIt; ++BitIt)
					{
						uint32 PrimitiveIndex = BitIt.GetIndex();
						SetBitIndices[ViewIndex].Add(PrimitiveIndex);					
					}

					// Any visible primitives with an indirect shadow need their ILC updated, since that determines the indirect shadow direction
					for (int32 IndirectPrimitiveIndex = 0; IndirectPrimitiveIndex < View.IndirectShadowPrimitives.Num(); IndirectPrimitiveIndex++)
					{
						int32 PrimitiveIndex = View.IndirectShadowPrimitives[IndirectPrimitiveIndex]->GetIndex();
						SetBitIndices[ViewIndex].AddUnique(PrimitiveIndex);
					}
				}			
			}

			// Go over the views and operate on any relevant visible primitives
			for (int32 ViewIndex = 0; ViewIndex < Renderer.Views.Num(); ViewIndex++)
			{
				FViewInfo& View = Renderer.Views[ViewIndex];

				const TArray<uint32>& SetBits = SetBitIndices[ViewIndex];
				for (int32 i = 0; i < SetBits.Num(); ++i)
				{
					uint32 PrimitiveIndex = SetBits[i];

					FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->Primitives[PrimitiveIndex];
					const bool bPrecomputedLightingBufferWasDirty = PrimitiveSceneInfo->NeedsPrecomputedLightingBufferUpdate();

					const FPrimitiveViewRelevance& PrimitiveRelevance = View.PrimitiveViewRelevanceMap[PrimitiveIndex];
					UpdateCachePrimitive(AttachmentGroups, PrimitiveSceneInfo, bAllowUnbuiltPreview, PrimitiveRelevance.bOpaqueRelevance, OutBlocksToUpdate, OutTransitionsOverTimeToUpdate);

					// If it was already dirty, then the primitive is already in one of the view dirty primitive list at this point.
					// This also ensures that a primitive does not get added twice to the list, which could create an array reallocation.
					if (!bPrecomputedLightingBufferWasDirty && PrimitiveSceneInfo->NeedsPrecomputedLightingBufferUpdate())
					{
						// Since the update can be executed on a threaded job (see GILCUpdatePrimTaskEnabled), no reallocation must happen here.
						checkSlow(View.DirtyPrecomputedLightingBufferPrimitives.Num() < View.DirtyPrecomputedLightingBufferPrimitives.Max());
						View.DirtyPrecomputedLightingBufferPrimitives.Push(PrimitiveSceneInfo);
					}
				}
			}
		}
		bUpdateAllCacheEntries = false;
	}
}
void FIndirectLightingCache::UpdateCache(FScene* Scene, FSceneRenderer& Renderer, bool bAllowUnbuiltPreview)
{
	if (IsIndirectLightingCacheAllowed())
	{
		bool bAnyViewAllowsIndirectLightingCache = false;

		for (int32 ViewIndex = 0; ViewIndex < Renderer.Views.Num(); ViewIndex++)
		{
			bAnyViewAllowsIndirectLightingCache |= Renderer.Views[ViewIndex].Family->EngineShowFlags.IndirectLightingCache;
		}

		if (bAnyViewAllowsIndirectLightingCache)
		{
			SCOPE_CYCLE_COUNTER(STAT_UpdateIndirectLightingCache);

			TMap<FIntVector, FBlockUpdateInfo> BlocksToUpdate;
			TArray<FIndirectLightingCacheAllocation*> TransitionsOverTimeToUpdate;

			if (bUpdateAllCacheEntries)
			{
				const uint32 PrimitiveCount = Scene->Primitives.Num();

				for (uint32 PrimitiveIndex = 0; PrimitiveIndex < PrimitiveCount; ++PrimitiveIndex)
				{
					FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->Primitives[PrimitiveIndex];

					UpdateCachePrimitive(Scene, PrimitiveSceneInfo, false, true, BlocksToUpdate, TransitionsOverTimeToUpdate);
				}
			}

			// Go over the views and operate on any relevant visible primitives
			for (int32 ViewIndex = 0; ViewIndex < Renderer.Views.Num(); ViewIndex++)
			{
				FViewInfo& View = Renderer.Views[ViewIndex];

				if (!bUpdateAllCacheEntries)
				{
					for (FSceneSetBitIterator BitIt(View.PrimitiveVisibilityMap); BitIt; ++BitIt)
					{
						uint32 PrimitiveIndex = BitIt.GetIndex();
						FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->Primitives[PrimitiveIndex];
						const FPrimitiveViewRelevance& PrimitiveRelevance = View.PrimitiveViewRelevanceMap[PrimitiveIndex];

						UpdateCachePrimitive(Scene, PrimitiveSceneInfo, bAllowUnbuiltPreview, PrimitiveRelevance.bOpaqueRelevance, BlocksToUpdate, TransitionsOverTimeToUpdate);
					}
				}

				UpdateTranslucentVolumeCache(View, BlocksToUpdate, TransitionsOverTimeToUpdate);
			}

			UpdateBlocks(Scene, Renderer.Views.GetTypedData(), BlocksToUpdate);

			UpdateTransitionsOverTime(TransitionsOverTimeToUpdate, Renderer.ViewFamily.DeltaWorldTime);

			if (GCacheDrawLightingSamples || Renderer.ViewFamily.EngineShowFlags.VolumeLightingSamples || GCacheDrawDirectionalShadowing)
			{
				FViewElementPDI DebugPDI(Renderer.Views.GetTypedData(), NULL);

				for (int32 VolumeIndex = 0; VolumeIndex < Scene->PrecomputedLightVolumes.Num(); VolumeIndex++)
				{
					const FPrecomputedLightVolume* PrecomputedLightVolume = Scene->PrecomputedLightVolumes[VolumeIndex];

					PrecomputedLightVolume->DebugDrawSamples(&DebugPDI, GCacheDrawDirectionalShadowing != 0);
				}
			}
		}

		bUpdateAllCacheEntries = false;
	}
}