// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= VirtualTextureCompress.usf: Compression pass for runtime virtual texture =============================================================================*/ #include "Common.ush" #include "BlockCompressionCommon.ush" #include "BCCompressionCommon.ush" #include "ASTCCompressionCommon.ush" #include "ETCCompressionCommon.ush" #ifndef ETC_PROFILE #define ETC_PROFILE 0 #endif #ifndef ASTC_PROFILE #define ASTC_PROFILE 0 #endif #ifndef PACK_RG32_RGBA16 #define PACK_RG32_RGBA16 0 #endif #if PACK_RG32_RGBA16 #define TYPE_64BIT uint4 #else #define TYPE_64BIT uint2 #endif #ifndef USE_SRC_TEXTURE_ARRAY #define USE_SRC_TEXTURE_ARRAY 0 #endif #ifndef USE_DST_TEXTURE_ARRAY #define USE_DST_TEXTURE_ARRAY 0 #endif #if USE_SRC_TEXTURE_ARRAY #define SourceTextureType Texture2DArray #else #define SourceTextureType Texture2D #endif #if USE_DST_TEXTURE_ARRAY #define DestTextureType RWTexture2DArray #else #define DestTextureType RWTexture2D #endif SourceTextureType RenderTexture0; SamplerState TextureSampler0; SourceTextureType RenderTexture1; SamplerState TextureSampler1; SourceTextureType RenderTexture2; SamplerState TextureSampler2; // Outputs can be 64bit or 128 bit depending on formats. // The 64 bit type can be uint2 or uint4 depending on platform support. DestTextureType OutCompressTexture0_64bit; DestTextureType OutCompressTexture1_64bit; DestTextureType OutCompressTexture2_64bit; DestTextureType OutCompressTexture0_128bit; DestTextureType OutCompressTexture1_128bit; DestTextureType OutCompressTexture2_128bit; uint4 SourceRect; DECLARE_SCALAR_ARRAY(uint, DestPos, MAX_BATCH_SIZE * MAX_DST_LAYERS * 2); #if PACK_RG32_RGBA16 /** Pack RG32 to RGBA16 for output. */ uint4 PackRG32(in uint2 u2) { uint x0 = (u2.x >> 16) & 0x0000ffff; uint y0 = (u2.y >> 16) & 0x0000ffff; uint x1 = (u2.x) & 0x0000ffff; uint y1 = (u2.y) & 0x0000ffff; return uint4(x1, x0, y1, y0); } #else uint2 PackRG32(in uint2 u2) { return u2; } #endif // PACK_RG32_RGBA16 #if USE_SRC_TEXTURE_ARRAY float3 GetSourceUV(float2 UV, uint3 ThreadId) { return float3(UV, ThreadId.z); } #else float2 GetSourceUV(float2 UV, uint3 ThreadId) { return UV; } #endif #if USE_DST_TEXTURE_ARRAY uint3 GetDestPos(uint LayerIndex, uint3 ThreadId) { return ThreadId; } #else uint2 GetDestPos(uint LayerIndex, uint3 ThreadId) { uint Index = (ThreadId.z * MAX_DST_LAYERS + LayerIndex) * 2; uint2 BasePos = uint2(GET_SCALAR_ARRAY_ELEMENT(DestPos, Index), GET_SCALAR_ARRAY_ELEMENT(DestPos, (Index + 1))); return BasePos + ThreadId.xy; } #endif #if VERTEXSHADER /** Trivial vertex shader for copy shaders. */ void CopyVS(in uint VertexId : SV_VertexID, out float4 OutPosition : SV_POSITION, out float2 OutTexCoord : TEXCOORD0) { OutTexCoord = float2(((VertexId + 1) / 3) & 1, VertexId & 1); OutPosition.xy = float2(OutTexCoord.x, 1.f - OutTexCoord.y) * 2.f - 1.f; OutPosition.zw = float2(0, 1); } #endif // VERTEXSHADER #if COMPUTESHADER /** Compress base color to a single BC1 target. */ [numthreads(8,8,1)] void CompressBaseColorCS( uint3 ThreadId : SV_DispatchThreadID) { if (any(ThreadId.xy * 4 >= SourceRect.zw)) { return; } uint2 SamplePos = SourceRect.xy + ThreadId.xy * 4; float2 TexelUVSize = 1.f / float2(SourceRect.zw); float2 SampleUV = (float2(SamplePos) + 0.5f) * TexelUVSize; float3 BlockBaseColor[16]; ReadBlockRGB(RenderTexture0, TextureSampler0, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockBaseColor); #if ASTC_PROFIlE OutCompressTexture0_64bit[GetDestPos(0, ThreadId)] = PackRG32(CompressBlock_ETC2_SRGB(BlockBaseColor)); #elif ETC_PROFILE OutCompressTexture0_64bit[GetDestPos(0, ThreadId)] = PackRG32(CompressBlock_ETC2_SRGB(BlockBaseColor)); #else OutCompressTexture0_64bit[GetDestPos(0, ThreadId)] = PackRG32(CompressBC1Block_SRGB(BlockBaseColor)); #endif } #endif // COMPUTESHADER #if PIXELSHADER /** Simple copy of base color used for thumbnails. */ void CopyBaseColorPS( in float4 InPosition : SV_POSITION, in noperspective float2 InTexCoord : TEXCOORD0, out float4 OutColor0 : SV_Target0) { float3 BaseColor = RenderTexture0.SampleLevel(TextureSampler0, InTexCoord, 0).xyz; OutColor0 = float4(BaseColor, 1.f); } #endif // PIXELSHADER #if COMPUTESHADER /** Compress base color, normal, roughness and specular to BC3 target pair. */ [numthreads(8,8,1)] void CompressBaseColorNormalSpecularCS( uint3 ThreadId : SV_DispatchThreadID) { if (any(ThreadId.xy * 4 >= SourceRect.zw)) { return; } uint2 SamplePos = SourceRect.xy + ThreadId.xy * 4; float2 TexelUVSize = 1.f / float2(SourceRect.zw); float2 SampleUV = (float2(SamplePos) + 0.5f) * TexelUVSize; float3 BlockBaseColor[16]; ReadBlockRGB(RenderTexture0, TextureSampler0, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockBaseColor); float BlockNormalX[16]; float BlockNormalY[16]; ReadBlockXY(RenderTexture1, TextureSampler1, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockNormalX, BlockNormalY); float3 BlockSpecular[16]; ReadBlockRGB(RenderTexture2, TextureSampler2, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockSpecular); for (int i=0; i<16; i++) { BlockSpecular[i].z = round(BlockSpecular[i].z); } #if ASTC_PROFILE OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBlock_ASTC_SRGBA(BlockBaseColor, BlockNormalX); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBlock_ASTC_RGBA(BlockSpecular, BlockNormalY); #elif ETC_PROFILE OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBlock_ETC2_SRGBA(BlockBaseColor, BlockNormalX); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBlock_ETC2_RGBA(BlockSpecular, BlockNormalY); #else OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBC3Block_SRGB(BlockBaseColor, BlockNormalX); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBC3Block(BlockSpecular, BlockNormalY); #endif } #endif // COMPUTESHADER #if COMPUTESHADER /** Compress base color, normal, roughness to BC1 & BC3. */ [numthreads(8, 8, 1)] void CompressBaseColorNormalRoughnessCS( uint3 ThreadId : SV_DispatchThreadID) { if (any(ThreadId.xy * 4 >= SourceRect.zw)) { return; } uint2 SamplePos = SourceRect.xy + ThreadId.xy * 4; float2 TexelUVSize = 1.f / float2(SourceRect.zw); float2 SampleUV = (float2(SamplePos) + 0.5f) * TexelUVSize; // Note that this format uses non sRGB BaseColor format with manual encode/decode. // So that we don't use _SRGB forms of the block compression functions below. float3 BlockBaseColor[16]; ReadBlockRGB(RenderTexture0, TextureSampler0, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockBaseColor); float3 BlockNormalXRougnnessY[16]; const float BlockNormalZ[16]={ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; ReadBlockRGB(RenderTexture1, TextureSampler2, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockNormalXRougnnessY); #if ASTC_PROFILE OutCompressTexture0_64bit[GetDestPos(0, ThreadId)] = PackRG32(CompressBlock_ETC2_RGB(BlockBaseColor)); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBlock_ASTC_RGBA(BlockNormalXRougnnessY, BlockNormalZ); #elif ETC_PROFILE OutCompressTexture0_64bit[GetDestPos(0, ThreadId)] = PackRG32(CompressBlock_ETC2_RGB(BlockBaseColor)); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBlock_ETC2_RGBA(BlockNormalXRougnnessY, BlockNormalZ); #else OutCompressTexture0_64bit[GetDestPos(0, ThreadId)] = PackRG32(CompressBC1Block(BlockBaseColor)); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBC3Block(BlockNormalXRougnnessY, BlockNormalZ); #endif } #endif // COMPUTESHADER #if PIXELSHADER /** Copy path used when we disable texture compression, because we need to keep the same final channel layout. */ void CopyBaseColorNormalSpecularPS( in float4 InPosition : SV_POSITION, in noperspective float2 InTexCoord : TEXCOORD0, out float4 OutColor0 : SV_Target0, out float4 OutColor1 : SV_Target1) { float3 BaseColor = RenderTexture0.SampleLevel(TextureSampler0, InTexCoord, 0).xyz; float2 NormalXY = RenderTexture1.SampleLevel(TextureSampler1, InTexCoord, 0).xy; float3 RoughnessSpecularNormalZ = RenderTexture2.SampleLevel(TextureSampler2, InTexCoord, 0).xyz; RoughnessSpecularNormalZ.z = round(RoughnessSpecularNormalZ.z); OutColor0 = float4(BaseColor, NormalXY.x); OutColor1 = float4(RoughnessSpecularNormalZ, NormalXY.y); } #endif // PIXELSHADER #if COMPUTESHADER /** Compress base color, normal, roughness and specular to BC3, BC5, BC1 target set. */ [numthreads(8,8,1)] void CompressBaseColorNormalSpecularYCoCgCS( uint3 ThreadId : SV_DispatchThreadID) { if (any(ThreadId.xy * 4 >= SourceRect.zw)) { return; } uint2 SamplePos = SourceRect.xy + ThreadId.xy * 4; float2 TexelUVSize = 1.f / float2(SourceRect.zw); float2 SampleUV = (float2(SamplePos) + 0.5f) * TexelUVSize; float3 BlockBaseColor[16]; ReadBlockRGB(RenderTexture0, TextureSampler0, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockBaseColor); float BlockNormalX[16]; float BlockNormalY[16]; ReadBlockXY(RenderTexture1, TextureSampler1, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockNormalX, BlockNormalY); float3 BlockSpecular[16]; ReadBlockRGB(RenderTexture2, TextureSampler2, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockSpecular); for (int i=0; i<16; i++) { BlockSpecular[i].z = round(BlockSpecular[i].z); } #if ASTC_PROFILE OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBlock_ASTC_YCoCg(BlockBaseColor); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBlock_ETC2_RG(BlockNormalX, BlockNormalY); OutCompressTexture2_64bit[GetDestPos(2, ThreadId)] = PackRG32(CompressBlock_ETC2_RGB(BlockSpecular)); #elif ETC_PROFILE OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBlock_ETC2_YCoCg(BlockBaseColor); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBlock_ETC2_RG(BlockNormalX, BlockNormalY); OutCompressTexture2_64bit[GetDestPos(2, ThreadId)] = PackRG32(CompressBlock_ETC2_RGB(BlockSpecular)); #else OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBC3BlockYCoCg(BlockBaseColor); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBC5Block(BlockNormalX, BlockNormalY); OutCompressTexture2_64bit[GetDestPos(2, ThreadId)] = PackRG32(CompressBC1Block(BlockSpecular)); #endif } #endif // COMPUTESHADER #if PIXELSHADER /** Copy path used when we disable texture compression, because we need to keep the same final channel layout. */ void CopyBaseColorNormalSpecularYCoCgPS( in float4 InPosition : SV_POSITION, in noperspective float2 InTexCoord : TEXCOORD0, out float4 OutColor0 : SV_Target0, out float4 OutColor1 : SV_Target1, out float4 OutColor2 : SV_Target2) { float3 YCoCg = RGB2YCoCg(RenderTexture0.SampleLevel(TextureSampler0, InTexCoord, 0).xyz); float2 NormalXY = RenderTexture1.SampleLevel(TextureSampler1, InTexCoord, 0).xy; float3 RoughnessSpecularNormalZ = RenderTexture2.SampleLevel(TextureSampler2, InTexCoord, 0).xyz; RoughnessSpecularNormalZ.z = round(RoughnessSpecularNormalZ.z); OutColor0 = float4(YCoCg.yz, 0, YCoCg.x); OutColor1 = float4(NormalXY, 0, 1); OutColor2 = float4(RoughnessSpecularNormalZ, 1); } #endif // PIXELSHADER #if COMPUTESHADER /** Compress base color, normal, roughness, specular and mask to BC3, BC5, BC3 target set. */ [numthreads(8,8,1)] void CompressBaseColorNormalSpecularMaskYCoCgCS( uint3 ThreadId : SV_DispatchThreadID) { if (any(ThreadId.xy * 4 >= SourceRect.zw)) return; uint2 SamplePos = SourceRect.xy + ThreadId.xy * 4; float2 TexelUVSize = 1.f / float2(SourceRect.zw); float2 SampleUV = (float2(SamplePos) + 0.5f) * TexelUVSize; float3 BlockBaseColor[16]; ReadBlockRGB(RenderTexture0, TextureSampler0, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockBaseColor); float BlockNormalX[16]; float BlockNormalY[16]; float BlockMask[16]; ReadBlockXYA(RenderTexture1, TextureSampler1, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockNormalX, BlockNormalY, BlockMask); float3 BlockSpecular[16]; ReadBlockRGB(RenderTexture2, TextureSampler2, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockSpecular); for (int i=0; i<16; i++) { BlockSpecular[i].z = round(BlockSpecular[i].z); } #if ASTC_PROFILE OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBlock_ASTC_YCoCg(BlockBaseColor); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBlock_ETC2_RG(BlockNormalX, BlockNormalY); OutCompressTexture2_128bit[GetDestPos(2, ThreadId)] = CompressBlock_ASTC_RGBA(BlockSpecular, BlockMask); #elif ETC_PROFILE OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBlock_ETC2_YCoCg(BlockBaseColor); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBlock_ETC2_RG(BlockNormalX, BlockNormalY); OutCompressTexture2_128bit[GetDestPos(2, ThreadId)] = CompressBlock_ETC2_RGBA(BlockSpecular, BlockMask); #else OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBC3BlockYCoCg(BlockBaseColor); OutCompressTexture1_128bit[GetDestPos(1, ThreadId)] = CompressBC5Block(BlockNormalX, BlockNormalY); OutCompressTexture2_128bit[GetDestPos(2, ThreadId)] = CompressBC3Block(BlockSpecular, BlockMask); #endif } #endif // COMPUTESHADER #if PIXELSHADER /** Copy path used when we disable texture compression, because we need to keep the same final channel layout. */ void CopyBaseColorNormalSpecularMaskYCoCgPS( in float4 InPosition : SV_POSITION, in noperspective float2 InTexCoord : TEXCOORD0, out float4 OutColor0 : SV_Target0, out float4 OutColor1 : SV_Target1, out float4 OutColor2 : SV_Target2) { float3 YCoCg = RGB2YCoCg(RenderTexture0.SampleLevel(TextureSampler0, InTexCoord, 0).xyz); float3 NormalXYMask = RenderTexture1.SampleLevel(TextureSampler1, InTexCoord, 0).xyz; float3 RoughnessSpecularNormalZ = RenderTexture2.SampleLevel(TextureSampler2, InTexCoord, 0).xyz; RoughnessSpecularNormalZ.z = round(RoughnessSpecularNormalZ.z); OutColor0 = float4(YCoCg.yz, 0, YCoCg.x); OutColor1 = float4(NormalXYMask.xy, 0, 1); OutColor2 = float4(RoughnessSpecularNormalZ, NormalXYMask.z); } #endif // PIXELSHADER #if COMPUTESHADER /** Compress 4 channel mask to BC3. */ [numthreads(8,8,1)] void CompressMask4CS( uint3 ThreadId : SV_DispatchThreadID) { if (any(ThreadId.xy * 4 >= SourceRect.zw)) { return; } uint2 SamplePos = SourceRect.xy + ThreadId.xy * 4; float2 TexelUVSize = 1.f / float2(SourceRect.zw); float2 SampleUV = (float2(SamplePos) + 0.5f) * TexelUVSize; float3 BlockXYZ[16]; ReadBlockRGB(RenderTexture0, TextureSampler0, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockXYZ); float BlockW[16]; ReadBlockX(RenderTexture1, TextureSampler0, GetSourceUV(SampleUV, ThreadId), TexelUVSize, BlockW); #if ASTC_PROFILE OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBlock_ASTC_RGBA(BlockXYZ, BlockW); #elif ETC_PROFILE OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBlock_ETC2_RGBA(BlockXYZ, BlockW); #else OutCompressTexture0_128bit[GetDestPos(0, ThreadId)] = CompressBC3Block(BlockXYZ, BlockW); #endif } #endif // COMPUTESHADER #if PIXELSHADER /** Copy path used when we disable texture compression, because we need to keep the same final channel layout. */ void CopyMask4PS( in float4 InPosition : SV_POSITION, in noperspective float2 InTexCoord : TEXCOORD0, out float4 OutColor0 : SV_Target0) { float3 MaskXYZ = RenderTexture0.SampleLevel(TextureSampler0, InTexCoord, 0).xyz; float MaskW = RenderTexture1.SampleLevel(TextureSampler1, InTexCoord, 0).x; OutColor0 = float4(MaskXYZ, MaskW); } #endif // PIXELSHADER #if COMPUTESHADER /** Compress displacement to a single BC4 target. */ [numthreads(8, 8, 1)] void CompressDisplacementCS( uint3 ThreadId : SV_DispatchThreadID) { if (any(ThreadId.xy * 4 >= SourceRect.zw)) { return; } uint2 SamplePos = SourceRect.xy + ThreadId.xy * 4; float2 TexelUVSize = 1.f / float2(SourceRect.zw); float2 SampleUV = (float2(SamplePos) + 0.5f) * TexelUVSize; float Block[16]; ReadBlockX(RenderTexture0, TextureSampler0, GetSourceUV(SampleUV, ThreadId), TexelUVSize, Block); #if ASTC_PROFILE OutCompressTexture0_64bit[GetDestPos(0, ThreadId)] = PackRG32(CompressBlock_ETC2_Alpha(Block)); #elif ETC_PROFILE OutCompressTexture0_64bit[GetDestPos(0, ThreadId)] = PackRG32(CompressBlock_ETC2_Alpha(Block)); #else OutCompressTexture0_64bit[GetDestPos(0, ThreadId)] = PackRG32(CompressBC4Block(Block)); #endif } #endif // COMPUTESHADER #if PIXELSHADER /** Copy world height or displacement (without compression) * This path is used when want to copy the world height or displacement data into a thumbnail. */ void CopyWorldHeightPS( in float4 InPosition : SV_POSITION, in noperspective float2 InTexCoord : TEXCOORD0, out float4 OutColor0 : SV_Target0) { float WorldHeight = RenderTexture0.SampleLevel(TextureSampler0, InTexCoord, 0).r; OutColor0 = float4(WorldHeight.xxx, 1.f); } #endif // PIXELSHADER