// Copyright Epic Games, Inc. All Rights Reserved. #include "Common.ush" #include "ShaderPrint.ush" #include "ColorMap.ush" //////////////////////////////////////////////////////////////////////////////////////////////// // Add texture #if SHADER_ADD_TEXTURE_CS Texture2D InTexture_0; Texture2D InTexture_1; Texture2D InTexture_2; Texture2D InTexture_3; Texture2D InTexture_4; Texture2D InTexture_5; Texture2D InTexture_6; Texture2D InTexture_7; uint4 InSliceIndex[8]; SamplerState InSampler_0; SamplerState InSampler_1; SamplerState InSampler_2; SamplerState InSampler_3; SamplerState InSampler_4; SamplerState InSampler_5; SamplerState InSampler_6; SamplerState InSampler_7; int2 AtlasResolution; uint AtlasSliceCount; uint ValidCount; RWTexture2DArray OutAtlasTexture; [numthreads(8, 8, 1)] void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID) { if (all(DispatchThreadId.xy < uint2(AtlasResolution))) { const uint2 DstPixelPos = DispatchThreadId.xy; uint DstSlice = 0; const float2 SrcUV = (DstPixelPos + 0.5) / float2(AtlasResolution); const uint SrcSlice = DispatchThreadId.z; if (SrcSlice < ValidCount) { float Color = 0; switch (SrcSlice) { case 0: Color = InTexture_0.SampleLevel(InSampler_0, SrcUV, 0).x; DstSlice = InSliceIndex[0].x; break; case 1: Color = InTexture_1.SampleLevel(InSampler_1, SrcUV, 0).x; DstSlice = InSliceIndex[1].x; break; case 2: Color = InTexture_2.SampleLevel(InSampler_2, SrcUV, 0).x; DstSlice = InSliceIndex[2].x; break; case 3: Color = InTexture_3.SampleLevel(InSampler_3, SrcUV, 0).x; DstSlice = InSliceIndex[3].x; break; case 4: Color = InTexture_4.SampleLevel(InSampler_4, SrcUV, 0).x; DstSlice = InSliceIndex[4].x; break; case 5: Color = InTexture_5.SampleLevel(InSampler_5, SrcUV, 0).x; DstSlice = InSliceIndex[5].x; break; case 6: Color = InTexture_6.SampleLevel(InSampler_6, SrcUV, 0).x; DstSlice = InSliceIndex[6].x; break; case 7: Color = InTexture_7.SampleLevel(InSampler_7, SrcUV, 0).x; DstSlice = InSliceIndex[7].x; break; } // Ensure there is no NaN value Color = -min(-Color, 0); DstSlice = min(DstSlice, AtlasSliceCount-1); OutAtlasTexture[uint3(DstPixelPos, DstSlice)] = Color; } } } #endif // SHADER_ADD_TEXTURE_CS #if SHADER_ADD_TEXTURE_PS Texture2D InTexture; SamplerState InSampler; int2 AtlasResolution; void MainPS(float4 SvPosition : SV_POSITION, out float OutColor : SV_Target0) { const float2 SrcUV = SvPosition.xy / float2(AtlasResolution); float Color = InTexture.SampleLevel(InSampler, SrcUV, 0).x; // Ensure there is no NaN value OutColor = -min(-Color, 0); } #endif // SHADER_ADD_TEXTURE_PS //////////////////////////////////////////////////////////////////////////////////////////////// // Debug #if SHADER_DEBUG int2 AtlasResolution; uint AtlasSliceCount; uint TotalSlotCount; uint ValidSlotCount; uint UsedSliceCount; uint bForceUpdate; int2 OutputResolution; Buffer ValidSliceBuffer; SamplerState AtlasSampler; Texture2DArray AtlasTexture; RWTexture2D OutputTexture; FFontColor GetOccupancyColor(float In) { float3 Color = lerp(float3(0, 1, 0), float3(1, 0, 0), saturate(In)); return InitFontColor(Color); } bool IsInSlot(float2 Coord, float2 MinRect, float2 MaxRect) { return all(Coord >= MinRect) && all(Coord <= MaxRect); } bool IsOnBorder(float2 Coord, float2 MinRect, float2 MaxRect, float BorderSize) { const bool bIsWithin = IsInSlot(Coord, MinRect, MaxRect); const bool bIsOnBorder = any(Coord - MinRect <= BorderSize) || any(MaxRect - Coord <= BorderSize); return bIsWithin && bIsOnBorder; } [numthreads(8, 8, 1)] void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID) { // Draw stats if (all(DispatchThreadId == 0)) { FShaderPrintContext Context = InitShaderPrintContext(true, uint2(50, 100)); Print(Context, TEXT("Atlas resolution : "), FontSilver); Print(Context, AtlasResolution.x, FontOrange, 3, 0); Print(Context, TEXT("x"), FontSilver); Print(Context, AtlasResolution.y, FontOrange, 3, 0); Newline(Context); Print(Context, TEXT("Atlas slice : "), FontSilver); Print(Context, UsedSliceCount, FontOrange, 3, 0); Print(Context, TEXT("/"), FontSilver); Print(Context, AtlasSliceCount, FontOrange, 3, 0); Newline(Context); const float Occupancy = UsedSliceCount / float(AtlasSliceCount); const FFontColor OccupancyColor = GetOccupancyColor(Occupancy); Print(Context, TEXT("Atlas occupancy : "), FontSilver); Print(Context, Occupancy * 100.f, OccupancyColor, 4, 1); Print(Context, TEXT("% "), FontSilver); Newline(Context); Print(Context, TEXT("Slot count : "), FontSilver); Print(Context, ValidSlotCount, FontOrange); Newline(Context); if (TotalSlotCount > AtlasSliceCount) { Print(Context, TEXT("Slot over budget : "), FontSilver); Print(Context, TotalSlotCount - AtlasSliceCount, FontRed, 3, 0); Print(Context, TEXT(" - "), FontRed); Print(Context, TotalSlotCount, FontRed, 3, 0); Print(Context, TEXT("/"), FontRed); Print(Context, AtlasSliceCount, FontRed, 3, 0); Print(Context, TEXT(" "), FontRed); Newline(Context); } Print(Context, TEXT("Force Update : "), FontSilver); if (bForceUpdate) { Print(Context, TEXT("True"), FontGreen); } else { Print(Context, TEXT("False"), FontRed); } Newline(Context); Print(Context, TEXT("Atlas format : "), FontSilver); Print(Context, TEXT("PF_R16"), FontOrange); Newline(Context); } // Draw atlas const uint2 GridRes = ceil(sqrt(AtlasSliceCount)).xx; const uint2 SlotResolution = AtlasResolution / GridRes; const uint2 Coord = DispatchThreadId.xy; const float2 OutputUV = float2(DispatchThreadId.xy) / float2(OutputResolution); const float AtlasRatio = AtlasResolution.y / float(AtlasResolution.x); const float OutputRatio = OutputResolution.y / float(OutputResolution.x); const float ScaleFactor = 0.25f; const uint2 DisplayAtlasOffset = uint2(50, 220); const uint2 DisplayAtlasResolution = uint2(OutputResolution.x * ScaleFactor, OutputResolution.x * ScaleFactor * AtlasResolution.y / AtlasResolution.x); if (all(Coord > DisplayAtlasOffset) && all(Coord < DisplayAtlasOffset + DisplayAtlasResolution)) { const float2 AtlasUV = (Coord - DisplayAtlasOffset) / float2(DisplayAtlasResolution); const float2 AtlasCoord = AtlasUV * AtlasResolution; if (all(AtlasUV < 1.0f)) { const float BorderSize = float(AtlasResolution.x) / float(DisplayAtlasResolution.x) * 2.f; const float2 IESSlotUV = frac(AtlasUV * GridRes); const uint IESIndex = floor(AtlasUV.x * GridRes.x) + floor(AtlasUV.y * GridRes.y) * GridRes.x; float4 Color = 0; if (IESIndex < AtlasSliceCount) { const bool bIsValid = ValidSliceBuffer[IESIndex] > 0; if (bIsValid) { Color = AtlasTexture.SampleLevel(AtlasSampler, float3(IESSlotUV, IESIndex), 0); } if (IsOnBorder(IESSlotUV * SlotResolution, float2(0,0), SlotResolution.xx, BorderSize)) { Color = bIsValid ? float4(0, 1, 0, 1) : float4(0.5, 0.5, 0.5, 1); } } OutputTexture[DispatchThreadId.xy] = float4(Color.xyz, 1.0f); } } else { OutputTexture[DispatchThreadId.xy] = 0.f; } } #endif // SHADER_DEBUG