void FDistanceFieldVolumeTextureAtlas::UpdateAllocations() { if (PendingAllocations.Num() > 0) { // Sort largest to smallest for best packing PendingAllocations.Sort(FCompareVolumeAllocation()); for (int32 AllocationIndex = 0; AllocationIndex < PendingAllocations.Num(); AllocationIndex++) { FDistanceFieldVolumeTexture* Texture = PendingAllocations[AllocationIndex]; const FIntVector Size = Texture->VolumeData.Size; if (!BlockAllocator.AddElement((uint32&)Texture->AtlasAllocationMin.X, (uint32&)Texture->AtlasAllocationMin.Y, (uint32&)Texture->AtlasAllocationMin.Z, Size.X, Size.Y, Size.Z)) { UE_LOG(LogStaticMesh,Error,TEXT("Failed to allocate %ux%ux%u in distance field atlas"), Size.X, Size.Y, Size.Z); PendingAllocations.RemoveAt(AllocationIndex); AllocationIndex--; } } if (!VolumeTextureRHI || BlockAllocator.GetSizeX() > VolumeTextureRHI->GetSizeX() || BlockAllocator.GetSizeY() > VolumeTextureRHI->GetSizeY() || BlockAllocator.GetSizeZ() > VolumeTextureRHI->GetSizeZ()) { if (CurrentAllocations.Num() > 0) { static const auto CVarXY = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.DistanceFields.AtlasSizeXY")); const int32 AtlasXY = CVarXY->GetValueOnAnyThread(); static const auto CVarZ = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.DistanceFields.AtlasSizeZ")); const int32 AtlasZ = CVarZ->GetValueOnAnyThread(); // Remove all allocations from the layout so we have a clean slate BlockAllocator = FTextureLayout3d(0, 0, 0, AtlasXY, AtlasXY, AtlasZ, false, false); Generation++; // Re-upload all textures since we had to reallocate PendingAllocations.Append(CurrentAllocations); CurrentAllocations.Empty(); // Sort largest to smallest for best packing PendingAllocations.Sort(FCompareVolumeAllocation()); // Add all allocations back to the layout for (int32 AllocationIndex = 0; AllocationIndex < PendingAllocations.Num(); AllocationIndex++) { FDistanceFieldVolumeTexture* Texture = PendingAllocations[AllocationIndex]; const FIntVector Size = Texture->VolumeData.Size; if (!BlockAllocator.AddElement((uint32&)Texture->AtlasAllocationMin.X, (uint32&)Texture->AtlasAllocationMin.Y, (uint32&)Texture->AtlasAllocationMin.Z, Size.X, Size.Y, Size.Z)) { UE_LOG(LogStaticMesh,Error,TEXT("Failed to allocate %ux%ux%u in distance field atlas"), Size.X, Size.Y, Size.Z); PendingAllocations.RemoveAt(AllocationIndex); AllocationIndex--; } } } FRHIResourceCreateInfo CreateInfo; VolumeTextureRHI = RHICreateTexture3D( BlockAllocator.GetSizeX(), BlockAllocator.GetSizeY(), BlockAllocator.GetSizeZ(), Format, 1, TexCreate_ShaderResource, CreateInfo); UE_LOG(LogStaticMesh,Log,TEXT("Allocated %s"), *GetSizeString()); } for (int32 AllocationIndex = 0; AllocationIndex < PendingAllocations.Num(); AllocationIndex++) { FDistanceFieldVolumeTexture* Texture = PendingAllocations[AllocationIndex]; const FIntVector Size = Texture->VolumeData.Size; const FUpdateTextureRegion3D UpdateRegion( Texture->AtlasAllocationMin.X, Texture->AtlasAllocationMin.Y, Texture->AtlasAllocationMin.Z, 0, 0, 0, Size.X, Size.Y, Size.Z); const int32 FormatSize = GPixelFormats[Format].BlockBytes; // Update the volume texture atlas RHIUpdateTexture3D(VolumeTextureRHI, 0, UpdateRegion, Size.X * FormatSize, Size.X * Size.Y * FormatSize, (const uint8*)Texture->VolumeData.DistanceFieldVolume.GetData()); } CurrentAllocations.Append(PendingAllocations); PendingAllocations.Empty(); } }
void FIndirectLightingCache::UpdateBlock(FScene* Scene, FViewInfo* DebugDrawingView, FBlockUpdateInfo& BlockInfo) { const int32 NumSamplesPerBlock = BlockInfo.Block.TexelSize * BlockInfo.Block.TexelSize * BlockInfo.Block.TexelSize; FSHVectorRGB3 SingleSample; float DirectionalShadowing = 1; FVector SkyBentNormal(0, 0, 1); //always do point interpolation to get valid 3band single sample and directional data. InterpolatePoint(Scene, BlockInfo.Block, DirectionalShadowing, SingleSample, SkyBentNormal); if (CanIndirectLightingCacheUseVolumeTexture(GetFeatureLevel()) && !BlockInfo.Allocation->bPointSample) { static TArray<float> AccumulatedWeight; AccumulatedWeight.Reset(NumSamplesPerBlock); AccumulatedWeight.AddZeroed(NumSamplesPerBlock); //volume textures are encoded as two band, so no reason to waste perf interpolating 3 bands. static TArray<FSHVectorRGB2> AccumulatedIncidentRadiance; AccumulatedIncidentRadiance.Reset(NumSamplesPerBlock); AccumulatedIncidentRadiance.AddZeroed(NumSamplesPerBlock); // Interpolate SH samples from precomputed lighting samples and accumulate lighting data for an entire block InterpolateBlock(Scene, BlockInfo.Block, AccumulatedWeight, AccumulatedIncidentRadiance); static TArray<FFloat16Color> Texture0Data; static TArray<FFloat16Color> Texture1Data; static TArray<FFloat16Color> Texture2Data; Texture0Data.Reset(NumSamplesPerBlock); Texture1Data.Reset(NumSamplesPerBlock); Texture2Data.Reset(NumSamplesPerBlock); Texture0Data.AddUninitialized(NumSamplesPerBlock); Texture1Data.AddUninitialized(NumSamplesPerBlock); Texture2Data.AddUninitialized(NumSamplesPerBlock); const int32 FormatSize = GPixelFormats[PF_FloatRGBA].BlockBytes; check(FormatSize == sizeof(FFloat16Color)); // Encode the SH samples into a texture format // Note the single sample is updated even if this is a volume allocation, because translucent materials only use the single sample EncodeBlock(DebugDrawingView, BlockInfo.Block, AccumulatedWeight, AccumulatedIncidentRadiance, Texture0Data, Texture1Data, Texture2Data); // Setup an update region const FUpdateTextureRegion3D UpdateRegion( BlockInfo.Block.MinTexel.X, BlockInfo.Block.MinTexel.Y, BlockInfo.Block.MinTexel.Z, 0, 0, 0, BlockInfo.Block.TexelSize, BlockInfo.Block.TexelSize, BlockInfo.Block.TexelSize); // Update the volume texture atlas RHIUpdateTexture3D((const FTexture3DRHIRef&)GetTexture0().ShaderResourceTexture, 0, UpdateRegion, BlockInfo.Block.TexelSize * FormatSize, BlockInfo.Block.TexelSize * BlockInfo.Block.TexelSize * FormatSize, (const uint8*)Texture0Data.GetData()); RHIUpdateTexture3D((const FTexture3DRHIRef&)GetTexture1().ShaderResourceTexture, 0, UpdateRegion, BlockInfo.Block.TexelSize * FormatSize, BlockInfo.Block.TexelSize * BlockInfo.Block.TexelSize * FormatSize, (const uint8*)Texture1Data.GetData()); RHIUpdateTexture3D((const FTexture3DRHIRef&)GetTexture2().ShaderResourceTexture, 0, UpdateRegion, BlockInfo.Block.TexelSize * FormatSize, BlockInfo.Block.TexelSize * BlockInfo.Block.TexelSize * FormatSize, (const uint8*)Texture2Data.GetData()); } else { if (GCacheDrawInterpolationPoints != 0 && DebugDrawingView) { FViewElementPDI DebugPDI(DebugDrawingView, NULL); const FVector WorldPosition = BlockInfo.Block.Min; DebugPDI.DrawPoint(WorldPosition, FLinearColor(0, 0, 1), 10, SDPG_World); } } // Record the position that the sample was taken at BlockInfo.Allocation->TargetPosition = BlockInfo.Block.Min + BlockInfo.Block.Size / 2; BlockInfo.Allocation->TargetSamplePacked0[0] = FVector4(SingleSample.R.V[0], SingleSample.R.V[1], SingleSample.R.V[2], SingleSample.R.V[3]) / PI; BlockInfo.Allocation->TargetSamplePacked0[1] = FVector4(SingleSample.G.V[0], SingleSample.G.V[1], SingleSample.G.V[2], SingleSample.G.V[3]) / PI; BlockInfo.Allocation->TargetSamplePacked0[2] = FVector4(SingleSample.B.V[0], SingleSample.B.V[1], SingleSample.B.V[2], SingleSample.B.V[3]) / PI; BlockInfo.Allocation->TargetSamplePacked1[0] = FVector4(SingleSample.R.V[4], SingleSample.R.V[5], SingleSample.R.V[6], SingleSample.R.V[7]) / PI; BlockInfo.Allocation->TargetSamplePacked1[1] = FVector4(SingleSample.G.V[4], SingleSample.G.V[5], SingleSample.G.V[6], SingleSample.G.V[7]) / PI; BlockInfo.Allocation->TargetSamplePacked1[2] = FVector4(SingleSample.B.V[4], SingleSample.B.V[5], SingleSample.B.V[6], SingleSample.B.V[7]) / PI; BlockInfo.Allocation->TargetSamplePacked2 = FVector4(SingleSample.R.V[8], SingleSample.G.V[8], SingleSample.B.V[8], 0) / PI; BlockInfo.Allocation->TargetDirectionalShadowing = DirectionalShadowing; const float BentNormalLength = SkyBentNormal.Size(); BlockInfo.Allocation->TargetSkyBentNormal = FVector4(SkyBentNormal / FMath::Max(BentNormalLength, .0001f), BentNormalLength); if (!BlockInfo.Allocation->bHasEverUpdatedSingleSample) { // If this is the first update, also set the interpolated state to match the new target //@todo - detect and handle teleports in the same way BlockInfo.Allocation->SingleSamplePosition = BlockInfo.Allocation->TargetPosition; for (int32 VectorIndex = 0; VectorIndex < 3; VectorIndex++) // RGB { BlockInfo.Allocation->SingleSamplePacked0[VectorIndex] = BlockInfo.Allocation->TargetSamplePacked0[VectorIndex]; BlockInfo.Allocation->SingleSamplePacked1[VectorIndex] = BlockInfo.Allocation->TargetSamplePacked1[VectorIndex]; } BlockInfo.Allocation->SingleSamplePacked2 = BlockInfo.Allocation->TargetSamplePacked2; BlockInfo.Allocation->CurrentDirectionalShadowing = BlockInfo.Allocation->TargetDirectionalShadowing; BlockInfo.Allocation->CurrentSkyBentNormal = BlockInfo.Allocation->TargetSkyBentNormal; BlockInfo.Allocation->bHasEverUpdatedSingleSample = true; } BlockInfo.Block.bHasEverBeenUpdated = true; }
void FIndirectLightingCache::UpdateBlock(FScene* Scene, FViewInfo* DebugDrawingView, FBlockUpdateInfo& BlockInfo) { const int32 NumSamplesPerBlock = BlockInfo.Block.TexelSize * BlockInfo.Block.TexelSize * BlockInfo.Block.TexelSize; FSHVectorRGB2 SingleSample; float DirectionalShadowing = 1; if (CanIndirectLightingCacheUseVolumeTexture() && BlockInfo.Allocation->bOpaqueRelevance) { static TArray<float> AccumulatedWeight; AccumulatedWeight.Reset(NumSamplesPerBlock); AccumulatedWeight.AddZeroed(NumSamplesPerBlock); static TArray<FSHVectorRGB2> AccumulatedIncidentRadiance; AccumulatedIncidentRadiance.Reset(NumSamplesPerBlock); AccumulatedIncidentRadiance.AddZeroed(NumSamplesPerBlock); // Interpolate SH samples from precomputed lighting samples and accumulate lighting data for an entire block InterpolateBlock(Scene, BlockInfo.Block, AccumulatedWeight, AccumulatedIncidentRadiance); static TArray<FFloat16Color> Texture0Data; static TArray<FFloat16Color> Texture1Data; static TArray<FFloat16Color> Texture2Data; Texture0Data.Reset(NumSamplesPerBlock); Texture1Data.Reset(NumSamplesPerBlock); Texture2Data.Reset(NumSamplesPerBlock); Texture0Data.AddUninitialized(NumSamplesPerBlock); Texture1Data.AddUninitialized(NumSamplesPerBlock); Texture2Data.AddUninitialized(NumSamplesPerBlock); const int32 FormatSize = GPixelFormats[PF_FloatRGBA].BlockBytes; check(FormatSize == sizeof(FFloat16Color)); // Encode the SH samples into a texture format EncodeBlock(DebugDrawingView, BlockInfo.Block, AccumulatedWeight, AccumulatedIncidentRadiance, Texture0Data, Texture1Data, Texture2Data, SingleSample); // Setup an update region const FUpdateTextureRegion3D UpdateRegion( BlockInfo.Block.MinTexel.X, BlockInfo.Block.MinTexel.Y, BlockInfo.Block.MinTexel.Z, 0, 0, 0, BlockInfo.Block.TexelSize, BlockInfo.Block.TexelSize, BlockInfo.Block.TexelSize); // Update the volume texture atlas RHIUpdateTexture3D((const FTexture3DRHIRef&)GetTexture0().ShaderResourceTexture, 0, UpdateRegion, BlockInfo.Block.TexelSize * FormatSize, BlockInfo.Block.TexelSize * BlockInfo.Block.TexelSize * FormatSize, (const uint8*)Texture0Data.GetData()); RHIUpdateTexture3D((const FTexture3DRHIRef&)GetTexture1().ShaderResourceTexture, 0, UpdateRegion, BlockInfo.Block.TexelSize * FormatSize, BlockInfo.Block.TexelSize * BlockInfo.Block.TexelSize * FormatSize, (const uint8*)Texture1Data.GetData()); RHIUpdateTexture3D((const FTexture3DRHIRef&)GetTexture2().ShaderResourceTexture, 0, UpdateRegion, BlockInfo.Block.TexelSize * FormatSize, BlockInfo.Block.TexelSize * BlockInfo.Block.TexelSize * FormatSize, (const uint8*)Texture2Data.GetData()); } else { InterpolatePoint(Scene, BlockInfo.Block, DirectionalShadowing, SingleSample); } // Record the position that the sample was taken at BlockInfo.Allocation->TargetPosition = BlockInfo.Block.Min + BlockInfo.Block.Size / 2; BlockInfo.Allocation->TargetSamplePacked[0] = FVector4(SingleSample.R.V[0], SingleSample.R.V[1], SingleSample.R.V[2], SingleSample.R.V[3]) / PI; BlockInfo.Allocation->TargetSamplePacked[1] = FVector4(SingleSample.G.V[0], SingleSample.G.V[1], SingleSample.G.V[2], SingleSample.G.V[3]) / PI; BlockInfo.Allocation->TargetSamplePacked[2] = FVector4(SingleSample.B.V[0], SingleSample.B.V[1], SingleSample.B.V[2], SingleSample.B.V[3]) / PI; BlockInfo.Allocation->TargetDirectionalShadowing = DirectionalShadowing; if (!BlockInfo.Allocation->bHasEverUpdatedSingleSample) { // If this is the first update, also set the interpolated state to match the new target //@todo - detect and handle teleports in the same way BlockInfo.Allocation->SingleSamplePosition = BlockInfo.Allocation->TargetPosition; for (int32 VectorIndex = 0; VectorIndex < ARRAY_COUNT(BlockInfo.Allocation->SingleSamplePacked); VectorIndex++) { BlockInfo.Allocation->SingleSamplePacked[VectorIndex] = BlockInfo.Allocation->TargetSamplePacked[VectorIndex]; } BlockInfo.Allocation->CurrentDirectionalShadowing = BlockInfo.Allocation->TargetDirectionalShadowing; BlockInfo.Allocation->bHasEverUpdatedSingleSample = true; } BlockInfo.Block.bHasEverBeenUpdated = true; }