429 lines
13 KiB
HLSL
429 lines
13 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Common.ush"
|
|
#include "ShaderPrint.ush"
|
|
#include "ColorMap.ush"
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Clear texture
|
|
#if SHADER_CLEAR_TEXTURE
|
|
|
|
int2 Resolution;
|
|
|
|
RWTexture2D<float4> OutputTexture;
|
|
|
|
[numthreads(8, 8, 1)]
|
|
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
const uint2 PixelCoord = DispatchThreadId.xy;
|
|
if (all(PixelCoord < Resolution))
|
|
{
|
|
float4 Random = 0;
|
|
Random.x = InterleavedGradientNoise(PixelCoord + 0.5f, 0);
|
|
Random.y = InterleavedGradientNoise(PixelCoord + 0.5f, 1);
|
|
Random.z = InterleavedGradientNoise(PixelCoord + 0.5f, 2);
|
|
Random.w = 1;
|
|
|
|
Random = float4(1, 0, 1, 1);
|
|
OutputTexture[PixelCoord] = Random;
|
|
}
|
|
}
|
|
|
|
#endif // SHADER_CLEAR_TEXTURE
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Add texture
|
|
#if SHADER_RECT_VS
|
|
|
|
Buffer<uint4> SlotBuffer;
|
|
Buffer<float4> ScaleOffsetBuffer;
|
|
uint SlotBufferOffset;
|
|
int2 AtlasResolution;
|
|
int bHasScaleOffset;
|
|
|
|
void MainVS(
|
|
uint InVertexId : SV_VertexID,
|
|
uint InInstanceId : SV_InstanceID,
|
|
out float4 OutPosition : SV_Position,
|
|
out float2 OutCoord : RECT_COORD,
|
|
out float2 OutUV : RECT_UV,
|
|
out uint OutId : RECT_ID)
|
|
{
|
|
const uint4 Rect = SlotBuffer.Load(SlotBufferOffset + InInstanceId);
|
|
const int2 RectOffset = Rect.xy;
|
|
const int2 RectResolution = Rect.zw;
|
|
|
|
// Only used for source texture, when copying source texture data into the atlas
|
|
float4 ScaleOffset = float4(1.0f, 1.0f, 0.0f, 0.0f);
|
|
if (bHasScaleOffset != 0)
|
|
{
|
|
ScaleOffset = ScaleOffsetBuffer.Load(SlotBufferOffset + InInstanceId);;
|
|
}
|
|
|
|
OutId = InInstanceId;
|
|
|
|
// UV & Coords relative to the rect
|
|
float2 SrcUV = 0;
|
|
SrcUV.x = InVertexId == 1 || InVertexId == 2 || InVertexId == 4 ? 1.f : 0.f;
|
|
SrcUV.y = InVertexId == 2 || InVertexId == 4 || InVertexId == 5 ? 1.f : 0.f;
|
|
OutUV = SrcUV * ScaleOffset.xy + ScaleOffset.zw;
|
|
OutCoord = SrcUV * RectResolution;
|
|
|
|
// UV relative to the atlas
|
|
const float2 UVOffset = RectOffset / float2(AtlasResolution);
|
|
const float2 UVScale = RectResolution / float2(AtlasResolution);
|
|
const float2 DstUV = SrcUV * UVScale + UVOffset;
|
|
OutPosition = float4(DstUV * float2(2.0f, -2.0f) + float2(-1.0, 1.0f), 0.5f, 1.0f);
|
|
}
|
|
#endif // SHADER_RECT_VS
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Add texture
|
|
#if SHADER_ADD_TEXTURE
|
|
|
|
int InTextureMIPBias;
|
|
Texture2D<float4> InTexture0;
|
|
Texture2D<float4> InTexture1;
|
|
Texture2D<float4> InTexture2;
|
|
Texture2D<float4> InTexture3;
|
|
Texture2D<float4> InTexture4;
|
|
Texture2D<float4> InTexture5;
|
|
Texture2D<float4> InTexture6;
|
|
Texture2D<float4> InTexture7;
|
|
|
|
SamplerState InSampler0;
|
|
SamplerState InSampler1;
|
|
SamplerState InSampler2;
|
|
SamplerState InSampler3;
|
|
SamplerState InSampler4;
|
|
SamplerState InSampler5;
|
|
SamplerState InSampler6;
|
|
SamplerState InSampler7;
|
|
|
|
void MainPS(
|
|
in float4 InPosition : SV_Position,
|
|
in float2 InCoord : RECT_COORD,
|
|
in float2 InUV : RECT_UV,
|
|
in uint InId : RECT_ID,
|
|
out float4 OutTexture : SV_Target0)
|
|
{
|
|
float4 Color = 0;
|
|
switch (InId)
|
|
{
|
|
case 0: Color = InTexture0.SampleLevel(InSampler0, InUV, InTextureMIPBias); break;
|
|
case 1: Color = InTexture1.SampleLevel(InSampler1, InUV, InTextureMIPBias); break;
|
|
case 2: Color = InTexture2.SampleLevel(InSampler2, InUV, InTextureMIPBias); break;
|
|
case 3: Color = InTexture3.SampleLevel(InSampler3, InUV, InTextureMIPBias); break;
|
|
case 4: Color = InTexture4.SampleLevel(InSampler4, InUV, InTextureMIPBias); break;
|
|
case 5: Color = InTexture5.SampleLevel(InSampler5, InUV, InTextureMIPBias); break;
|
|
case 6: Color = InTexture6.SampleLevel(InSampler6, InUV, InTextureMIPBias); break;
|
|
case 7: Color = InTexture7.SampleLevel(InSampler7, InUV, InTextureMIPBias); break;
|
|
}
|
|
|
|
// Ensure there is no NaN value
|
|
Color = -min(-Color, 0);
|
|
|
|
OutTexture = Color;
|
|
}
|
|
|
|
#endif // SHADER_ADD_TEXTURE
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Copy texture
|
|
#if SHADER_COPY_TEXTURE
|
|
|
|
uint MipLevel;
|
|
Buffer<uint4> SrcSlotBuffer;
|
|
|
|
Texture2D<float4> SourceAtlasTexture;
|
|
SamplerState InSampler;
|
|
|
|
void MainPS(
|
|
in float4 InPosition : SV_Position,
|
|
in float2 InCoord : RECT_COORD,
|
|
in float2 InUV : RECT_UV,
|
|
in uint InId : RECT_ID,
|
|
out float4 OutTexture : SV_Target0)
|
|
{
|
|
const uint2 SrcRectOffset = SrcSlotBuffer[InId].xy;
|
|
const uint2 SourceCoord = InCoord + SrcRectOffset;
|
|
const float4 Color = SourceAtlasTexture.Load(uint3(SourceCoord, MipLevel));
|
|
OutTexture = Color;
|
|
}
|
|
|
|
#endif // SHADER_COPY_TEXTURE
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Filter texture
|
|
#if SHADER_FILTER_TEXTURE
|
|
|
|
float KernelSize;
|
|
uint FilterQuality;
|
|
int2 SrcAtlasResolution;
|
|
Buffer<uint4> SrcSlotBuffer;
|
|
Texture2D<float4> SourceAtlasTexture;
|
|
SamplerState SourceAtlasSampler;
|
|
|
|
void MainPS(
|
|
in float4 InPosition : SV_Position,
|
|
in float2 InCoord : RECT_COORD,
|
|
in float2 InUV : RECT_UV,
|
|
in uint InId : RECT_ID,
|
|
out float4 OutTexture : SV_Target0)
|
|
{
|
|
|
|
if (FilterQuality)
|
|
{
|
|
// 2x2 box filtering
|
|
const float2 SrcCoord = uint2(InCoord) * 2.f;
|
|
const uint4 SrcRect = SrcSlotBuffer[InId];
|
|
const uint2 SrcRectOffset = SrcRect.xy;
|
|
const uint2 SrcRectResolution = SrcRect.zw;
|
|
const uint2 SrcRectMax = SrcRectOffset + SrcRectResolution;
|
|
|
|
const float2 Coord00 = SrcRectOffset + SrcCoord + float2(0,0);
|
|
const float2 Coord10 = SrcRectOffset + SrcCoord + float2(1,0);
|
|
const float2 Coord01 = SrcRectOffset + SrcCoord + float2(0,1);
|
|
const float2 Coord11 = SrcRectOffset + SrcCoord + float2(1,1);
|
|
|
|
const float W00 = all(Coord00 < SrcRectMax) ? 1.f : 0.f;
|
|
const float W10 = all(Coord10 < SrcRectMax) ? 1.f : 0.f;
|
|
const float W01 = all(Coord01 < SrcRectMax) ? 1.f : 0.f;
|
|
const float W11 = all(Coord11 < SrcRectMax) ? 1.f : 0.f;
|
|
const float InvW = 1.f / max(1.f, float(W00 + W10 + W01 + W11));
|
|
|
|
const float4 Color0 = SourceAtlasTexture.Load(uint3(Coord00, 0));
|
|
const float4 Color1 = SourceAtlasTexture.Load(uint3(Coord10, 0));
|
|
const float4 Color2 = SourceAtlasTexture.Load(uint3(Coord01, 0));
|
|
const float4 Color3 = SourceAtlasTexture.Load(uint3(Coord11, 0));
|
|
|
|
OutTexture = (W00*Color0 + W10*Color1 + W01*Color2 + W11*Color3) * InvW;
|
|
}
|
|
else
|
|
{
|
|
// 5x5 Gaussian filtering (naive, non-separable, with bilinear filtering)
|
|
//
|
|
// Possible improvement, but probably not worth it since the filtering is done only once, when the texture is uploaded:
|
|
// * Separable filter (but requires compute with border, or RT ping-pong)
|
|
// * Recursive sparse filter with MIP prefiltering (Deriche & co)
|
|
const float2 SrcCoord = InCoord * 2.f; // -> InCoord is xxx.5f, so SrcCoord is at the center of the 2x2 block
|
|
const uint4 SrcRect = SrcSlotBuffer[InId];
|
|
const uint2 SrcRectOffset = SrcRect.xy;
|
|
const uint2 SrcRectResolution = SrcRect.zw;
|
|
|
|
const float2 MinCoord = SrcRectOffset;
|
|
const float2 MaxCoord = SrcRectOffset + SrcRectResolution - float2(1,1);
|
|
|
|
const float2 InvAtlasResolution = 1.0f / float2(SrcAtlasResolution);
|
|
|
|
float3 AccColor = 0;
|
|
float AccW = 0;
|
|
const float Variance = 3.0f; // std ~1.4. weight, at radius=3 ~0.01;
|
|
const int KernelExtent = 2;
|
|
for (int y = -KernelExtent; y <= KernelExtent; ++y)
|
|
for (int x = -KernelExtent; x <= KernelExtent; ++x)
|
|
{
|
|
const float2 Offset = float2(x, y);
|
|
const float2 Coord = SrcRectOffset + SrcCoord + Offset;
|
|
|
|
if (all(Coord >= MinCoord) && all(Coord < MaxCoord))
|
|
{
|
|
const float2 UV = Coord * InvAtlasResolution;
|
|
const float D2 = dot(Offset, Offset);
|
|
const float W = exp(-D2 / Variance);
|
|
const float3 Color = SourceAtlasTexture.SampleLevel(SourceAtlasSampler, UV, 0).xyz; // MIP=0 as it is an SRV narrowed around SrcMIP
|
|
|
|
AccColor += W * Color;
|
|
AccW += W;
|
|
}
|
|
}
|
|
OutTexture = float4(AccColor / max(0.001f, AccW), 1.0f);
|
|
}
|
|
}
|
|
|
|
#endif // SHADER_FILTER_TEXTURE
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Debug
|
|
#if SHADER_DEBUG
|
|
|
|
int2 AtlasResolution;
|
|
float AtlasMaxMipLevel;
|
|
float Occupancy;
|
|
uint SlotCount;
|
|
uint HorizonCount;
|
|
uint FreeCount;
|
|
int2 OutputResolution;
|
|
uint AtlasMIPIndex;
|
|
uint AtlasSourceTextureMIPBias;
|
|
uint AtlasFormat;
|
|
|
|
uint ForceUpdate;
|
|
uint ResetOnChange;
|
|
uint ClearAtlas;
|
|
|
|
SamplerState AtlasSampler;
|
|
Texture2D<float4> AtlasTexture;
|
|
RWTexture2D<float4> OutputTexture;
|
|
Buffer<uint4> SlotBuffer;
|
|
Buffer<uint4> HorizonBuffer;
|
|
Buffer<uint4> FreeBuffer;
|
|
|
|
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, FontOrange);
|
|
Newline(Context);
|
|
|
|
Print(Context, TEXT("Atlas max. MIP : "), FontSilver);
|
|
Print(Context, AtlasMaxMipLevel, FontOrange);
|
|
Newline(Context);
|
|
|
|
Print(Context, TEXT("Atlas MIP Index : "), FontSilver);
|
|
Print(Context, AtlasMIPIndex, FontOrange);
|
|
Newline(Context);
|
|
|
|
const FFontColor OccupancyColor = GetOccupancyColor(Occupancy);
|
|
Print(Context, TEXT("Atlas occupancy : "), FontSilver);
|
|
Print(Context, Occupancy * 100.f, OccupancyColor);
|
|
Newline(Context);
|
|
|
|
Print(Context, TEXT("Textures count : "), FontSilver);
|
|
Print(Context, SlotCount, FontOrange);
|
|
Newline(Context);
|
|
|
|
Print(Context, TEXT("Src. MIP bias : "), FontSilver);
|
|
Print(Context, AtlasSourceTextureMIPBias, FontOrange);
|
|
Newline(Context);
|
|
|
|
Print(Context, TEXT("Atlas format : "), FontSilver);
|
|
if (AtlasFormat == 0)
|
|
{ Print(Context, TEXT("PF_FloatR11G11B10"), FontOrange); }
|
|
else if (AtlasFormat == 1)
|
|
{ Print(Context, TEXT("PF_FloatRGBA"), FontOrange); }
|
|
else
|
|
{ Print(Context, TEXT("Unknown"), FontOrange); }
|
|
Newline(Context);
|
|
|
|
Print(Context, TEXT("Force Update : "), FontSilver);
|
|
PrintBool(Context, ForceUpdate);
|
|
Newline(Context);
|
|
|
|
Print(Context, TEXT("Reset On Change : "), FontSilver);
|
|
PrintBool(Context, ResetOnChange);
|
|
Newline(Context);
|
|
|
|
Print(Context, TEXT("Clear Atlas : "), FontSilver);
|
|
PrintBool(Context, ClearAtlas);
|
|
Newline(Context);
|
|
|
|
}
|
|
|
|
// Draw atlas
|
|
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, 250);
|
|
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))
|
|
{
|
|
// Display the atlas, but gray scale it for easing slot visualization
|
|
const float TintScale_Valid = 1.0f;
|
|
const float TintScale_Invalid = 0.25f;
|
|
const float4 AtlasColor = AtlasTexture.SampleLevel(AtlasSampler, AtlasUV, AtlasMIPIndex);
|
|
|
|
float4 OutColor = 0.0f;
|
|
|
|
// Color valid slot with a green border
|
|
const float BorderSize = float(AtlasResolution.x) / float(DisplayAtlasResolution.x) * 2.f;
|
|
bool bScaleDownColor = true;
|
|
|
|
// Valid Slot
|
|
for (uint SlotIt = 0; SlotIt < SlotCount; ++SlotIt)
|
|
{
|
|
const uint4 Slot = SlotBuffer[SlotIt];
|
|
if (IsInSlot(AtlasCoord, Slot.xy, Slot.xy + Slot.zw))
|
|
{
|
|
// only show actual used texture samples ?
|
|
OutColor = AtlasColor * TintScale_Valid;
|
|
bScaleDownColor = false;
|
|
if (IsOnBorder(AtlasCoord, Slot.xy, Slot.xy + Slot.zw, BorderSize))
|
|
{
|
|
OutColor = float4(0, 1, 0, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Horizon
|
|
for (uint HorizonIt = 0; HorizonIt < HorizonCount; ++HorizonIt)
|
|
{
|
|
const uint4 Slot = HorizonBuffer[HorizonIt];
|
|
if (IsInSlot(AtlasCoord, Slot.xy, Slot.xy + Slot.zw))
|
|
{
|
|
if (IsOnBorder(AtlasCoord, Slot.xy, Slot.xy + Slot.zw, BorderSize))
|
|
{
|
|
OutColor = float4(ColorMapViridis(HorizonIt / float(HorizonCount - 1)), 1);
|
|
OutColor = float4(1, 0, 0, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Free rects.
|
|
for (uint FreeIt = 0; FreeIt < FreeCount; ++FreeIt)
|
|
{
|
|
const uint4 Slot = FreeBuffer[FreeIt];
|
|
if (IsInSlot(AtlasCoord, Slot.xy, Slot.xy + Slot.zw))
|
|
{
|
|
OutColor = AtlasColor * TintScale_Invalid;
|
|
if (IsOnBorder(AtlasCoord, Slot.xy, Slot.xy + Slot.zw, BorderSize))
|
|
{
|
|
OutColor = float4(1, 1, 0, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
OutputTexture[DispatchThreadId.xy] = float4(OutColor.xyz, 1.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutputTexture[DispatchThreadId.xy] = 0.f;
|
|
}
|
|
}
|
|
|
|
#endif // SHADER_DEBUG
|