// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= DebugViewModePixelShader.usf: Ubershader for debug pixel modes. =============================================================================*/ #define TEX_COORD_SCALE_ANALYSIS 1 #define DEBUG_MATERIAL_PARAMETERS 1 // Don't allow scene texture as we use ESceneRenderTargetsMode::DontSet in FMaterialTexCoordScalePS::SetParameters // to avoid crashes in commandlet mode. #undef SCENE_TEXTURES_DISABLED #define SCENE_TEXTURES_DISABLED 1 #undef EYE_ADAPTATION_DISABLED #define EYE_ADAPTATION_DISABLED 1 #include "Common.ush" #include "SHCommon.ush" #if OUTPUT_QUAD_OVERDRAW #include "QuadOverdraw.ush" #endif #include "VirtualTextureCommon.ush" #define UNDEF_ACCURACY 1024 // If -1, output everything to the RW texture. float4 OneOverCPUTexCoordScales[MAX_NUM_TEXTURE_REGISTER / 4]; int4 TexCoordIndices[MAX_NUM_TEXTURE_REGISTER / 4]; float4 CPUTexelFactor; // In world space units. float4 NormalizedComplexity; int2 AnalysisParams; // (TextureAnalysisIndex, bOutputScales) or // (TextureAnalysisIndex, TextureResolution) float PrimitiveAlpha; int TexCoordAnalysisIndex; float CPULogDistance; uint bShowQuadOverdraw; uint bOutputQuadOverdraw; int LODIndex; float3 SkinCacheDebugColor; int VisualizeMode; // Analysis index will be in [0, 31] if analysing a single texture, otherwise -1 #define TextureAnalysisIndex (AnalysisParams.x) #define bOutputScales (AnalysisParams.y) // Texture resolution if we're analysing a single texture #define TextureResolution (AnalysisParams.y) // Updated MAX_NUM_TEXTURE_REGISTER to 256 float GetComponent(float4 V, int Index) { FLATTEN if (Index == 0) return V.x; FLATTEN if (Index == 1) return V.y; FLATTEN if (Index == 2) return V.z; return V.w; } struct FTexCoordScalesParams { // Global var used to reduce the number of param passed to the shaders. int2 PixelPosition; // ddU, ddV for each coord index (up to 4 currently) float4 OneOverDDU; float4 OneOverDDV; // Used when inspecting single elements. float MinScale; float MaxScale; float TexSample; float TexSampleAverage; // When computing the texture scales, this holds 4 scales for consecutive texture register indices. float4 ScalesPerIndex; float RequiredResolution; }; MaterialFloat StoreTexCoordScale(in out FTexCoordScalesParams Params, float2 UV, int TextureReferenceIndex) { // Take the minimum scale since high scale requires less resolution. float GPUScaleX = length(ddx(UV)); float GPUScaleY = length(ddy(UV)); if (TextureReferenceIndex >= 0 && TextureReferenceIndex < MAX_NUM_TEXTURE_REGISTER) { float OneOverCPUScale = OneOverCPUTexCoordScales[TextureReferenceIndex / 4][TextureReferenceIndex % 4]; int TexCoordIndex = TexCoordIndices[TextureReferenceIndex / 4][TextureReferenceIndex % 4]; float GPUScale = min(GPUScaleX * GetComponent(Params.OneOverDDU, TexCoordIndex), GPUScaleY * GetComponent(Params.OneOverDDV, TexCoordIndex)); // Code path when searching the worst scales. MinScale and MaxScale are only used when rendering for accuracies (!bOutputScales) const bool bUpdateMinMax = (OneOverCPUScale > 0 && (TextureAnalysisIndex == -1 || TextureAnalysisIndex == TextureReferenceIndex)); Params.MinScale = bUpdateMinMax ? min(Params.MinScale, GPUScale * OneOverCPUScale) : Params.MinScale; Params.MaxScale = bUpdateMinMax ? max(Params.MaxScale, GPUScale * OneOverCPUScale) : Params.MaxScale; // Code path when rendering the material in a render target. Each TILE_RESOLUTION X TILE_RESOLUTION maps to different texture. const bool bUpdateScale = (bOutputScales && Params.PixelPosition.x / TILE_RESOLUTION == TextureReferenceIndex / 4); Params.ScalesPerIndex[TextureReferenceIndex % 4] = bUpdateScale ? min(Params.ScalesPerIndex[TextureReferenceIndex % 4], GPUScale) : Params.ScalesPerIndex[TextureReferenceIndex % 4]; } #if FEATURE_LEVEL >= FEATURE_LEVEL_SM5 float2 CoordDDX = ddx_fine(UV); float2 CoordDDY = ddy_fine(UV); #else float2 CoordDDX = ddx(UV); float2 CoordDDY = ddy(UV); #endif if (TextureAnalysisIndex == TextureReferenceIndex) { float MinDelta = min(length(CoordDDX), length(CoordDDY)); float RequiredResolution = (1 / max(MinDelta, 0.0000000001f)); Params.RequiredResolution = max(Params.RequiredResolution, RequiredResolution); } return 1.f; } MaterialFloat StoreTexSample(in out FTexCoordScalesParams Params, float4 C, int TextureReferenceIndex) { // Alpha? if (TextureAnalysisIndex == TextureReferenceIndex) { Params.TexSample = TextureAnalysisIndex == TextureReferenceIndex ? lerp(.4f, 1.f, saturate(Luminance(C.rgb))) : Params.TexSample; } Params.TexSampleAverage = TextureAnalysisIndex == TextureReferenceIndex ? lerp(.4f, 1.f, saturate((C.r + C.g + C.b) / 3)) : Params.TexSample; return 1.f; } #include "/Engine/Generated/Material.ush" #include "DebugViewModeCommon.ush" float3 AccuracyColorLookup(float Accuracy) { Accuracy = clamp(Accuracy, -1.99, 1.99); int ColorIndex = floor(Accuracy) + 2; float3 Color0 = DebugViewModeStruct.AccuracyColors[ColorIndex].rgb; float3 Color1 = DebugViewModeStruct.AccuracyColors[ColorIndex + 1].rgb; float ColorLerp = frac(Accuracy); return lerp(Color0, Color1, ColorLerp); } float GetCPUTexelFactor(int CoordIndex) { FLATTEN if (CoordIndex == 0) return CPUTexelFactor.x; FLATTEN if (CoordIndex == 1) return CPUTexelFactor.y; FLATTEN if (CoordIndex == 2) return CPUTexelFactor.z; return CPUTexelFactor.w; } float2 GetTexCoord(in FDebugPSIn Inputs, int CoordIndex) { FLATTEN if (CoordIndex == 0) return Inputs.TexCoord01.xy; FLATTEN if (CoordIndex == 1) return Inputs.TexCoord01.zw; FLATTEN if (CoordIndex == 2) return Inputs.TexCoord23.xy; return Inputs.TexCoord23.zw; } float GetTexCoordSizeAccuracy(in FDebugPSIn Inputs, int CoordIndex, float CPUSize) { float3 WorldPosition = SvPositionToResolvedTranslatedWorld(Inputs.SvPosition); float2 TexCoord = GetTexCoord(Inputs, CoordIndex); #if FEATURE_LEVEL >= FEATURE_LEVEL_SM5 float2 CoordDDX = ddx_fine(TexCoord); float2 CoordDDY = ddy_fine(TexCoord); float3 WorldPosDDX = ddx_fine(WorldPosition); float3 WorldPosDDY = ddy_fine(WorldPosition); #else float2 CoordDDX = ddx(TexCoord); float2 CoordDDY = ddy(TexCoord); float3 WorldPosDDX = ddx(WorldPosition); float3 WorldPosDDY = ddy(WorldPosition); #endif float UVAera = abs(CoordDDX.x * CoordDDY.y - CoordDDX.y * CoordDDY.x); float WorldAera = length(cross(WorldPosDDX, WorldPosDDY)); float GPUTexelFactor = sqrt(WorldAera / max(UVAera, 0.0000000001f)); return clamp(log2(CPUSize) - log2(GPUTexelFactor), -1.99, 1.99); } float4 VisualizeMaterialTexCoordScales(in FDebugPSIn DebugInputs, in bool bIsFrontFace) { // This default value will make it dark grey when nothing updates the texSample. float3 Result = float3(UNDEFINED_ACCURACY, UNDEFINED_ACCURACY, UNDEFINED_ACCURACY); FTexCoordScalesParams Params; // Set global var used for outputting data Params.PixelPosition = DebugInputs.SvPosition.xy; Params.OneOverDDU = 1 / float4(length(ddx(DebugInputs.TexCoord01.xy)), length(ddx(DebugInputs.TexCoord01.zw)), length(ddx(DebugInputs.TexCoord23.xy)), length(ddx(DebugInputs.TexCoord23.zw))); Params.OneOverDDV = 1 / float4(length(ddy(DebugInputs.TexCoord01.xy)), length(ddy(DebugInputs.TexCoord01.zw)), length(ddy(DebugInputs.TexCoord23.xy)), length(ddy(DebugInputs.TexCoord23.zw))); Params.TexSample = 1.f; Params.TexSampleAverage = 1.f; Params.MinScale = INITIAL_GPU_SCALE; Params.MaxScale = 0; Params.ScalesPerIndex = INITIAL_GPU_SCALE; Params.RequiredResolution = 0; FMaterialPixelParameters MaterialParameters = GetMaterialPixelParameters(DebugInputs, DebugInputs.SvPosition); // This is used to patch invalid parameters when computing the scales with FMeshMaterialRenderItem::EnqueueMaterialRender FLATTEN if (bOutputScales) { #if NUM_MATERIAL_TEXCOORDS > 0 // Set all texcoord but one to 0. This allows the analysis to figure out which texcoord is driving the lookup. int CurrentCoordIndex = (Params.PixelPosition.y / TILE_RESOLUTION) % MAX_NUM_TEX_COORD; UNROLL for (int CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; ++CoordinateIndex) { // The quad material used only has a valid TexCoord for UV 0 MaterialParameters.TexCoords[CoordinateIndex] = CoordinateIndex != CurrentCoordIndex ? 0 : DebugInputs.TexCoord01.xy; } // The scale must also be computed to match the actual coord inspected. That would be required when rendering the mesh in world space // Although not required here as using the material quad only use texcoord0. // Params.OneOverDDU = GetComponent(Params.OneOverDDU, CurrentCoordIndex).xxxx; // Params.OneOverDDV = GetComponent(Params.OneOverDDV, CurrentCoordIndex).xxxx; #endif } half3 BaseColor; MaterialParameters.TexCoordScalesParams = Params; { FPixelMaterialInputs PixelMaterialInputs; CalcMaterialParameters(MaterialParameters, PixelMaterialInputs, DebugInputs.SvPosition, bIsFrontFace); // Sample material properties. The results are not used, but the calls to StoreTexCoordScale will still be made. BaseColor = GetMaterialBaseColorRaw(PixelMaterialInputs); half Metallic = GetMaterialMetallicRaw(PixelMaterialInputs); half Specular = GetMaterialSpecularRaw(PixelMaterialInputs); float Roughness = GetMaterialRoughnessRaw(PixelMaterialInputs); half3 Normal = GetMaterialNormalRaw(PixelMaterialInputs); half3 Emissive = GetMaterialEmissiveRaw(PixelMaterialInputs); half Opacity = GetMaterialOpacityRaw(PixelMaterialInputs); #if MATERIALBLENDING_MASKED half Mask = GetMaterialMask(PixelMaterialInputs); if (!bOutputScales) { clip(GetMaterialMask(PixelMaterialInputs)); } #endif half4 SSData = GetMaterialSubsurfaceDataRaw(PixelMaterialInputs); float Custom0 = GetMaterialCustomData0(PixelMaterialInputs); float Custom1 = GetMaterialCustomData1(PixelMaterialInputs); float MaterialAO = GetMaterialAmbientOcclusionRaw(PixelMaterialInputs); float PixelDepthOffset = GetMaterialPixelDepthOffset(PixelMaterialInputs); #if CLEAR_COAT_BOTTOM_NORMAL && NUM_MATERIAL_OUTPUTS_CLEARCOATBOTTOMNORMAL > 0 float3 BottomNormal = ClearCoatBottomNormal0(MaterialParameters); #endif } Params = MaterialParameters.TexCoordScalesParams; Result *= saturate(Luminance(BaseColor)); float4 OutColor; if (bOutputScales) { // Output the scales OutColor = Params.ScalesPerIndex; } else { float PixelScale = ((Params.PixelPosition.x & 0x08) == (Params.PixelPosition.y & 0x08) || TextureAnalysisIndex != -1) ? Params.MinScale : Params.MaxScale; // Output accuracy. If the GPU Scale is INITIAL_GPU_SCALE, then the texcoord was not updated. if (Params.MinScale != INITIAL_GPU_SCALE) { Params.TexSample = TextureAnalysisIndex == -1 ? lerp(.4f, 1.f, saturate(Luminance(BaseColor))) : Params.TexSample; float Accuracy = clamp(log2(PixelScale), -1.99, 1.99); int ColorIndex = floor(Accuracy) + 2; Result = Params.TexSample * lerp(DebugViewModeStruct.AccuracyColors[ColorIndex].rgb, DebugViewModeStruct.AccuracyColors[ColorIndex + 1].rgb, frac(Accuracy)); } OutColor = float4(Result, PrimitiveAlpha); } return OutColor; } float4 VisualizeMeshTexCoordSizeAccuracy(in FDebugPSIn DebugInputs, in bool bIsFrontFace) { float3 Result = float3(UNDEFINED_ACCURACY, UNDEFINED_ACCURACY, UNDEFINED_ACCURACY); if (TexCoordAnalysisIndex >= 0) { float CPUSize = GetCPUTexelFactor(TexCoordAnalysisIndex); if (CPUSize > 0) { float Accuracy = GetTexCoordSizeAccuracy(DebugInputs, TexCoordAnalysisIndex, CPUSize); int ColorIndex = floor(Accuracy) + 2; Result = lerp(DebugViewModeStruct.AccuracyColors[ColorIndex].rgb, DebugViewModeStruct.AccuracyColors[ColorIndex + 1].rgb, frac(Accuracy)); } } else { float MinAccuracy = UNDEF_ACCURACY; float MaxAccuracy = -UNDEF_ACCURACY; [unroll] for (int CoordIndex = 0; CoordIndex < 4; ++CoordIndex) { float CPUSize = GetCPUTexelFactor(CoordIndex); if (CPUSize > 0) { float Accuracy = GetTexCoordSizeAccuracy(DebugInputs, CoordIndex, CPUSize); MinAccuracy = min(Accuracy, MinAccuracy); MaxAccuracy = max(Accuracy, MaxAccuracy); } } int2 PixelPosition = DebugInputs.SvPosition.xy; float Accuracy = (PixelPosition.x & 0x08) == (PixelPosition.y & 0x08) ? MinAccuracy : MaxAccuracy; if (abs(Accuracy) != UNDEF_ACCURACY) { int ColorIndex = floor(Accuracy) + 2; Result = lerp(DebugViewModeStruct.AccuracyColors[ColorIndex].rgb, DebugViewModeStruct.AccuracyColors[ColorIndex + 1].rgb, frac(Accuracy)); } } return float4(Result, PrimitiveAlpha); } float4 VisualizePrimitiveDistanceAccuracy(in FDebugPSIn DebugInputs, in bool bIsFrontFace) { float3 Result = float3(UNDEFINED_ACCURACY, UNDEFINED_ACCURACY, UNDEFINED_ACCURACY); if (CPULogDistance >= 0) { float ViewDistance = length(SvPositionToResolvedTranslatedWorld(DebugInputs.SvPosition)); float GPULogDistance = log2(max(1, ViewDistance)); float Accuracy = clamp(GPULogDistance - CPULogDistance, -1.99, 1.99); int ColorIndex = floor(Accuracy) + 2; Result = lerp(DebugViewModeStruct.AccuracyColors[ColorIndex].rgb, DebugViewModeStruct.AccuracyColors[ColorIndex + 1].rgb, frac(Accuracy)); } return float4(Result, PrimitiveAlpha); } float4 VisualizeRequiredTextureResolution(in FDebugPSIn DebugInputs, in bool bIsFrontFace) { FMaterialPixelParameters MaterialParameters = GetMaterialPixelParameters(DebugInputs, DebugInputs.SvPosition); MaterialParameters.TexCoordScalesParams.RequiredResolution = 0; MaterialParameters.TexCoordScalesParams.TexSample = 0; half3 BaseColor; { FPixelMaterialInputs PixelMaterialInputs; CalcMaterialParameters(MaterialParameters, PixelMaterialInputs, DebugInputs.SvPosition, bIsFrontFace); // Sample material properties. The results are not used, but the calls to StoreTexCoordScale are still be made. BaseColor = GetMaterialBaseColorRaw(PixelMaterialInputs); half Metallic = GetMaterialMetallicRaw(PixelMaterialInputs); half Specular = GetMaterialSpecularRaw(PixelMaterialInputs); float Roughness = GetMaterialRoughnessRaw(PixelMaterialInputs); half3 Normal = GetMaterialNormalRaw(PixelMaterialInputs); half3 Emissive = GetMaterialEmissiveRaw(PixelMaterialInputs); half Opacity = GetMaterialOpacityRaw(PixelMaterialInputs); #if MATERIALBLENDING_MASKED half Mask = GetMaterialMask(PixelMaterialInputs); clip(GetMaterialMask(PixelMaterialInputs)); #endif half4 SSData = GetMaterialSubsurfaceDataRaw(PixelMaterialInputs); float Custom0 = GetMaterialCustomData0(PixelMaterialInputs); float Custom1 = GetMaterialCustomData1(PixelMaterialInputs); float MaterialAO = GetMaterialAmbientOcclusionRaw(PixelMaterialInputs); float PixelDepthOffset = GetMaterialPixelDepthOffset(PixelMaterialInputs); #if CLEAR_COAT_BOTTOM_NORMAL && NUM_MATERIAL_OUTPUTS_CLEARCOATBOTTOMNORMAL > 0 float3 BottomNormal = ClearCoatBottomNormal0(MaterialParameters); #endif } float3 Result = float3(UNDEFINED_ACCURACY, UNDEFINED_ACCURACY, UNDEFINED_ACCURACY); Result *= saturate(Luminance(BaseColor)); if (VisualizeMode == DVSM_RequiredTextureResolution) { if (MaterialParameters.TexCoordScalesParams.RequiredResolution > 0) { float Accuracy = log2(TextureResolution / MaterialParameters.TexCoordScalesParams.RequiredResolution); Result = AccuracyColorLookup(Accuracy); Result *= MaterialParameters.TexCoordScalesParams.TexSample; } } #if MATERIAL_VIRTUALTEXTURE_FEEDBACK || LIGHTMAP_VT_ENABLED FinalizeVirtualTextureFeedback( MaterialParameters.VirtualTextureFeedback, MaterialParameters.SvPosition, View.VTFeedbackBuffer ); #endif return float4(Result, PrimitiveAlpha); } float4 VisualizeShaderComplexityAccumulate(in FDebugPSIn DebugInputs, in bool bIsFrontFace) { float3 Result = float3(1, 0, 1); if (bOutputQuadOverdraw) { float3 FinalComplexity = NormalizedComplexity.xyz; #if !(COMPILER_METAL) && (OUTPUT_QUAD_OVERDRAW) [branch] if (bShowQuadOverdraw && NormalizedComplexity.x > 0) { uint Coverage = ComputeQuadCoverage(DebugInputs.SvPosition.xy, DebugInputs.SvPrimitiveID, 24, false, false, 0); // The extra cost from quad overdraw is assumed to be linear. FinalComplexity.x *= 4.f / (float)(Coverage); } #endif // !(COMPILER_METAL) && (OUTPUT_QUAD_OVERDRAW) // use the maximum range allowed for scene color Result = FinalComplexity; } else { Result = NormalizedComplexity.xyz; } // alpha channel needs to be 1 to have decals working where the framebuffer blend is setup to multiply with alpha return float4(Result, 1); } float4 ApplyDebugColor(in FDebugPSIn DebugInputs, in bool bIsFrontFace, in float3 DebugColor) { float3 Result = DebugColor; FMaterialPixelParameters MaterialParameters = GetMaterialPixelParameters(DebugInputs, DebugInputs.SvPosition); half3 BaseColor; half Opacity; { FPixelMaterialInputs PixelMaterialInputs; CalcMaterialParameters(MaterialParameters, PixelMaterialInputs, DebugInputs.SvPosition, bIsFrontFace); // Sample material properties. The results are not used, but the calls to StoreTexCoordScale are still be made. BaseColor = GetMaterialBaseColor(PixelMaterialInputs); Opacity = GetMaterialOpacity(PixelMaterialInputs); half3 Emissive = GetMaterialEmissive(PixelMaterialInputs); #if MATERIALBLENDING_ADDITIVE Opacity = (.05 + .95 * Luminance(Emissive)); #else BaseColor += Emissive; #endif #if MATERIALBLENDING_MASKED clip(GetMaterialMask(PixelMaterialInputs)); #endif } Result *= (.05 + .95 * Luminance(BaseColor)); #if MATERIAL_VIRTUALTEXTURE_FEEDBACK || LIGHTMAP_VT_ENABLED FinalizeVirtualTextureFeedback( MaterialParameters.VirtualTextureFeedback, MaterialParameters.SvPosition, View.VTFeedbackBuffer ); #endif return float4(Result, Opacity); } float4 VisualizeLODColoration(in FDebugPSIn DebugInputs, in bool bIsFrontFace) { float3 Color = DebugViewModeStruct.LODColors[LODIndex].rgb; return ApplyDebugColor(DebugInputs, bIsFrontFace, Color); } float4 VisualizeGPUSkinCache(in FDebugPSIn DebugInputs, in bool bIsFrontFace) { return ApplyDebugColor(DebugInputs, bIsFrontFace, SkinCacheDebugColor); } #if !OUTPUT_PIXEL_DEPTH_OFFSET #define PIXELSHADER_EARLYDEPTHSTENCIL EARLYDEPTHSTENCIL #else #define PIXELSHADER_EARLYDEPTHSTENCIL #endif PIXELSHADER_EARLYDEPTHSTENCIL void Main( in FDebugPSIn DebugInputs OPTIONAL_IsFrontFace, out float4 OutColor : SV_Target0 ) { StereoSetupPS(DebugInputs.StereoInput); // If we see pink something went wrong. float4 VisualizeColor = float4(1, 0, 1, 1); BRANCH if (VisualizeMode == DVSM_ShaderComplexity || VisualizeMode == DVSM_ShaderComplexityContainedQuadOverhead || VisualizeMode == DVSM_ShaderComplexityBleedingQuadOverhead || VisualizeMode == DVSM_QuadComplexity || VisualizeMode == DVSM_LWCComplexity) { VisualizeColor = VisualizeShaderComplexityAccumulate(DebugInputs, bIsFrontFace); } else if (VisualizeMode == DVSM_PrimitiveDistanceAccuracy) { VisualizeColor = VisualizePrimitiveDistanceAccuracy(DebugInputs, bIsFrontFace); } else if (VisualizeMode == DVSM_MeshUVDensityAccuracy) { VisualizeColor = VisualizeMeshTexCoordSizeAccuracy(DebugInputs, bIsFrontFace); } else if (VisualizeMode == DVSM_MaterialTextureScaleAccuracy || VisualizeMode == DVSM_OutputMaterialTextureScales) { VisualizeColor = VisualizeMaterialTexCoordScales(DebugInputs, bIsFrontFace); } else if (VisualizeMode == DVSM_RequiredTextureResolution) { VisualizeColor = VisualizeRequiredTextureResolution(DebugInputs, bIsFrontFace); } else if (VisualizeMode == DVSM_LODColoration) { VisualizeColor = VisualizeLODColoration(DebugInputs, bIsFrontFace); } else if (VisualizeMode == DVSM_VisualizeGPUSkinCache) { VisualizeColor = VisualizeGPUSkinCache(DebugInputs, bIsFrontFace); } OutColor = RETURN_COLOR(VisualizeColor); }