Files
UnrealEngine/Engine/Shaders/Private/ShaderPrint.ush
2025-05-18 13:04:45 +08:00

406 lines
16 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ShaderPrint.ush:
Include this to be able to use ShaderPrint with the global uniforms and buffers.
=============================================================================*/
#pragma once
// ES3.1 doesn't support buffers in structs.
// FXC compiler can't unroll the Print loops correctly when the buffers are in structs.
// FXC compiler can't use buffers which are conditionally initialized.
#if ES3_1_PROFILE || (COMPILER_HLSL && !COMPILER_DXC)
#define SHADER_PRINT_USE_GLOBAL_RESOURCE 1
#else
#define SHADER_PRINT_USE_GLOBAL_RESOURCE 0
#endif
#ifndef SHADER_PRINT_EXPLICIT_BINDING
#define SHADER_PRINT_EXPLICIT_BINDING 0
#endif
// Shader print data can be stored with 3 differents methods:
// * ShaderPrintData binding: Shader print data are explicity setup on the CPP side, and the ShaderPrintData buffer are used for storing the data
// * Custom binding : Shader print data are explicity setup on the CPP side, and the ShaderPrintData buffer are stored into a custom buffer)
// * Diagnostic buffer : Shader print data & buffer are stored into the Diagnostic buffer, which is always bound and does not require any explicit binding. This is only supported on certain platforms
// Experimental feature, disabled by default
#define SHADER_PRINT_EXPERIMENTAL_DIAGNOSTIC_BUFFER 0
// On platform supporting diagnostic buffer, shader print data are stored into the diagnostic buffer which is always
// bound. This allows to access shader print functionality in any shader without explicit shader binding
#define SHADER_PRINT_USE_DIAGNOSTIC_BUFFER (PLATFORM_SUPPORTS_DIAGNOSTIC_BUFFER && (PIXELSHADER || COMPUTESHADER || RAYGENSHADER) && !SHADER_PRINT_USE_GLOBAL_RESOURCE && SHADER_PRINT_EXPERIMENTAL_DIAGNOSTIC_BUFFER)
// Offset to the ShaderPrint parameters (see FD3D12DiagnosticBuffer in D3D12Device.h , 6 uint x 64 lanes + 2 uints for BreadCrumbMarker) in Uints
#define SHADER_PRINT_DIAGNOSTIC_BUFFER_PARAMETERS_OFFSET (6u * 64u + 2u)
// Offset to the ShaderPrint entries in Uints (12 uints for storing constant parameters)
#define SHADER_PRINT_DIAGNOSTIC_BUFFER_ENTRY_OFFSET (SHADER_PRINT_DIAGNOSTIC_BUFFER_PARAMETERS_OFFSET + 12u)
// Global buffers for ShaderPrint collection.
#if SHADER_PRINT_USE_DIAGNOSTIC_BUFFER
// When a shader pass use explicit ShaderPrint parameter binding, it is possible that the only output of that pass is the shader print entry buffer.
// When using Diagnostic buffer as output, the regular shader entry output won't be used, and the pass will be culled.
// To prevent this we add a dummy write, so that the pass does get culled
#if SHADER_PRINT_EXPLICIT_BINDING
RWBuffer<uint> ShaderPrint_RWEntryBuffer;
#endif
#define SHADER_PRINT_RWENTRYBUFFER(Ctx, Idx) UEDiagnosticBuffer[SHADER_PRINT_DIAGNOSTIC_BUFFER_ENTRY_OFFSET + (Idx)]
#define SHADER_PRINT_RWSTATEBUFFER(Ctx, Idx) UEDiagnosticBuffer[SHADER_PRINT_DIAGNOSTIC_BUFFER_ENTRY_OFFSET + (Idx)]
#else
Buffer<uint> ShaderPrint_StateBuffer;
RWBuffer<uint> ShaderPrint_RWEntryBuffer;
// Use global buffers directly instead of using FShaderPrintContext::Buffers.
#if SHADER_PRINT_USE_GLOBAL_RESOURCE
#define SHADER_PRINT_RWENTRYBUFFER(Ctx, Idx) ShaderPrint_RWEntryBuffer[Idx]
#define SHADER_PRINT_RWSTATEBUFFER(Ctx, Idx) ShaderPrint_StateBuffer[Idx]
#endif
#endif
#include "ShaderPrintCommon.ush"
#include "MiniFontCommon.ush"
#include "PackUnpack.ush"
#if SHADER_PRINT_USE_DIAGNOSTIC_BUFFER
void PackShaderPrintConfig(FShaderPrintConfig In)
{
const uint Offset = SHADER_PRINT_DIAGNOSTIC_BUFFER_PARAMETERS_OFFSET;
UEDiagnosticBuffer[Offset+0] = PackUInt2ToUInt(In.Resolution.x, In.Resolution.y);
UEDiagnosticBuffer[Offset+1] = PackFloat2ToUInt(In.CursorCoord);
UEDiagnosticBuffer[Offset+2] = asuint(In.TranslatedWorldOffset.x);
UEDiagnosticBuffer[Offset+3] = asuint(In.TranslatedWorldOffset.y);
UEDiagnosticBuffer[Offset+4] = asuint(In.TranslatedWorldOffset.z);
UEDiagnosticBuffer[Offset+5] = PackFloat2ToUInt(In.FontSize);
UEDiagnosticBuffer[Offset+6] = PackFloat2ToUInt(In.FontSpacing);
UEDiagnosticBuffer[Offset+7] = In.MaxCharacterCount;
UEDiagnosticBuffer[Offset+8] = In.MaxSymbolCount;
UEDiagnosticBuffer[Offset+9] = In.MaxStateCount;
UEDiagnosticBuffer[Offset+10]= (In.MaxLineCount << 1) | (In.bIsDrawLocked ? 1 : 0);
UEDiagnosticBuffer[Offset+11]= In.MaxTriangleCount;
}
FShaderPrintConfig UnpackShaderPrintConfig()
{
const uint Offset = SHADER_PRINT_DIAGNOSTIC_BUFFER_PARAMETERS_OFFSET;
FShaderPrintConfig Out;
Out.Resolution = UnpackUInt2FromUInt(UEDiagnosticBuffer[Offset+0]);
Out.CursorCoord = UnpackFloat2FromUInt(UEDiagnosticBuffer[Offset+1]);
Out.TranslatedWorldOffset.x = asfloat(UEDiagnosticBuffer[Offset+2]);
Out.TranslatedWorldOffset.y = asfloat(UEDiagnosticBuffer[Offset+3]);
Out.TranslatedWorldOffset.z = asfloat(UEDiagnosticBuffer[Offset+4]);
Out.FontSize = UnpackFloat2FromUInt(UEDiagnosticBuffer[Offset+5]);
Out.FontSpacing = UnpackFloat2FromUInt(UEDiagnosticBuffer[Offset+6]);
Out.MaxCharacterCount = UEDiagnosticBuffer[Offset+7];
Out.MaxSymbolCount = UEDiagnosticBuffer[Offset+8];
Out.MaxStateCount = UEDiagnosticBuffer[Offset+9];
Out.MaxLineCount = UEDiagnosticBuffer[Offset+10] >> 1;
Out.bIsDrawLocked = (UEDiagnosticBuffer[Offset+10] & 1) != 0;
Out.MaxTriangleCount = UEDiagnosticBuffer[Offset+11];
// Dummy write to avoid the pass to be culled when using explicit ShaderPrint parameter bindings
#if SHADER_PRINT_EXPLICIT_BINDING
if (Out.Resolution.x > 15999)
{
ShaderPrint_RWEntryBuffer[0] = 0;
}
#endif
return Out;
}
#endif
FShaderPrintBuffers InitShaderPrintContextBuffers()
{
FShaderPrintBuffers Buffers;
#if SHADER_PRINT_USE_DIAGNOSTIC_BUFFER
// Nothing to do as Buffers.RWEntryBuffer is never used when using diagnostic buffer.
// This is ensured by the macro SHADER_PRINT_RWENTRYBUFFER, which defines how writing is handled.
#else
Buffers.StateBuffer = ShaderPrint_StateBuffer;
Buffers.RWEntryBuffer = ShaderPrint_RWEntryBuffer;
#endif
return Buffers;
}
// Initialise FShaderPrintConfig from the ShaderPrintData uniform buffer.
FShaderPrintConfig InitShaderPrintContextConfig()
{
#if SHADER_PRINT_USE_DIAGNOSTIC_BUFFER
return UnpackShaderPrintConfig();
#else
FShaderPrintConfig Config;
Config.Resolution = ShaderPrintData.Resolution;
Config.CursorCoord = ShaderPrintData.CursorCoord;
Config.TranslatedWorldOffset = ShaderPrintData.TranslatedWorldOffset;
Config.FontSize = ShaderPrintData.FontSize;
Config.FontSpacing = ShaderPrintData.FontSpacing;
Config.MaxCharacterCount = ShaderPrintData.MaxCharacterCount;
Config.MaxSymbolCount = ShaderPrintData.MaxSymbolCount;
Config.MaxStateCount = ShaderPrintData.MaxStateCount;
Config.MaxLineCount = ShaderPrintData.MaxLineCount;
Config.MaxTriangleCount = ShaderPrintData.MaxTriangleCount;
Config.bIsDrawLocked = ShaderPrintData.IsDrawLocked;
return Config;
#endif
}
// Initialise FShaderPrintContext.
FShaderPrintContext InitShaderPrintContext(bool bActive, float2 InStartPos)
{
FShaderPrintContext Out;
Out.bIsActive = bActive && SHADER_PRINT_ALLOW != 0;
Out.StartPos = InStartPos;
Out.Pos = InStartPos;
Out.Config = InitShaderPrintContextConfig();
#if !SHADER_PRINT_USE_GLOBAL_RESOURCE
Out.Buffers = InitShaderPrintContextBuffers();
#endif
return Out;
}
FShaderPrintContext InitShaderPrintContext(bool bActive, uint2 InStartCoord)
{
FShaderPrintConfig Config = InitShaderPrintContextConfig();
return InitShaderPrintContext(bActive, float2(InStartCoord) / float2(Config.Resolution));
}
FShaderPrintContext InitShaderPrintContextAtCursor(uint2 InCurrentCoord, uint2 InStartCoord)
{
FShaderPrintConfig Config = InitShaderPrintContextConfig();
const bool bActive = all(int2(InCurrentCoord) == Config.CursorCoord);
return InitShaderPrintContext(bActive, float2(InStartCoord) / float2(Config.Resolution));
}
FShaderPrintContext InitShaderPrintContextAtCursor(uint2 InStartCoord)
{
return InitShaderPrintContextAtCursor(GetShaderPrintCurrentPixelCoord(), InStartCoord);
}
FShaderPrintContext InitShaderPrintContext()
{
return InitShaderPrintContext(true, float2(0, 0));
}
// Backwards compatibility for client code that didn't use FShaderPrintContext.
int2 GetCursorPos()
{
return GetCursorPos(InitShaderPrintContext());
}
void AddFilledTriangleTWS(float3 Pos0, float3 Pos1, float3 Pos2, float4 Color)
{
AddFilledTriangleTWS(InitShaderPrintContext(), Pos0, Pos1, Pos2, Color);
}
void AddFilledTriangleWS(float3 Pos0, float3 Pos1, float3 Pos2, float4 Color)
{
AddFilledTriangleWS(InitShaderPrintContext(), Pos0, Pos1, Pos2, Color);
}
void AddFilledQuadTWS(float3 Pos0, float3 Pos1, float3 Pos2, float3 Pos3, float4 Color)
{
AddFilledQuadTWS(InitShaderPrintContext(), Pos0, Pos1, Pos2, Pos3, Color);
}
void AddFilledQuadWS(float3 Pos0, float3 Pos1, float3 Pos2, float3 Pos3, float4 Color)
{
AddFilledQuadWS(InitShaderPrintContext(), Pos0, Pos1, Pos2, Pos3, Color);
}
void AddFilledQuadSS(float2 Pos0, float2 Pos1, float4 Color)
{
AddFilledQuadSS(InitShaderPrintContext(), Pos0, Pos1, Color);
}
void AddLineTWS(float3 Pos0, float3 Pos1, float4 Color)
{
AddLineTWS(InitShaderPrintContext(), Pos0, Pos1, Color);
}
void AddLineTWS(float3 Pos0, float3 Pos1, float4 Color0, float4 Color1)
{
AddLineTWS(InitShaderPrintContext(), Pos0, Pos1, Color0, Color1);
}
void AddLineWS(float3 Pos0, float3 Pos1, float4 Color)
{
AddLineWS(InitShaderPrintContext(), Pos0, Pos1, Color);
}
void AddLineWS(float3 Pos0, float3 Pos1, float4 Color0, float4 Color1)
{
AddLineWS(InitShaderPrintContext(), Pos0, Pos1, Color0, Color1);
}
void AddLine(float3 Pos0, float3 Pos1, float4 Color)
{
AddLine(InitShaderPrintContext(), Pos0, Pos1, Color);
}
void AddLine(float3 Pos0, float3 Pos1, float4 Color0, float4 Color1)
{
AddLine(InitShaderPrintContext(), Pos0, Pos1, Color0, Color1);
}
void AddLineSS(float2 Pos0, float2 Pos1, float4 Color)
{
AddLineSS(InitShaderPrintContext(), Pos0, Pos1, Color);
}
void AddQuadWS(float3 Pos0, float3 Pos1, float3 Pos2, float3 Pos3, float4 Color)
{
AddQuadWS(InitShaderPrintContext(), Pos0, Pos1, Pos2, Pos3, Color);
}
void AddQuadTWS(float3 Pos0, float3 Pos1, float3 Pos2, float3 Pos3, float4 Color)
{
AddQuadTWS(InitShaderPrintContext(), Pos0, Pos1, Pos2, Pos3, Color);
}
void AddQuad(float3 Pos0, float3 Pos1, float3 Pos2, float3 Pos3, float4 Color)
{
AddQuad(InitShaderPrintContext(), Pos0, Pos1, Pos2, Pos3, Color);
}
void AddQuadSS(float2 MinPos, float2 MaxPos, float4 Color)
{
AddQuadSS(InitShaderPrintContext(), MinPos, MaxPos, Color);
}
void AddAABBTWS(float3 Min, float3 Max, float4 Color)
{
AddAABBTWS(InitShaderPrintContext(), Min, Max, Color);
}
void AddAABBWS(float3 Min, float3 Max, float4 Color)
{
AddAABBWS(InitShaderPrintContext(), Min, Max, Color);
}
void AddAABB(float3 Min, float3 Max, float4 Color)
{
AddAABB(InitShaderPrintContext(), Min, Max, Color);
}
void AddCrossWS(float3 Pos, float Size, float4 Color)
{
AddCrossWS(InitShaderPrintContext(), Pos, Size, Color);
}
void AddCrossTWS(float3 Pos, float Size, float4 Color)
{
AddCrossTWS(InitShaderPrintContext(), Pos, Size, Color);
}
void AddCross(float3 Pos, float Size, float4 Color)
{
AddCross(InitShaderPrintContext(), Pos, Size, Color);
}
void AddReferentialWS(float3 Pos, float3x3 InM, float Scale = 1)
{
AddReferentialWS(InitShaderPrintContext(), Pos, InM, Scale);
}
void AddReferentialWS(float4x4 InM, float Scale = 1)
{
AddReferentialWS(InitShaderPrintContext(), InM, Scale);
}
void AddReferentialWS(float3 Pos, float3 TangentZ, float Scale = 1)
{
AddReferentialWS(InitShaderPrintContext(), Pos, TangentZ, Scale);
}
void AddReferentialWS(float3 Pos, float3 T, float3 B, float3 N, float Scale = 1)
{
AddReferentialWS(InitShaderPrintContext(), Pos, T, B, N, Scale);
}
void AddReferentialTWS(float3 Pos, float3x3 InM, float Scale = 1)
{
AddReferentialTWS(InitShaderPrintContext(), Pos, InM, Scale);
}
void AddReferentialTWS(float4x4 InM, float Scale = 1)
{
AddReferentialTWS(InitShaderPrintContext(), InM, Scale);
}
void AddReferentialTWS(float3 Pos, float3 TangentZ, float Scale = 1)
{
AddReferentialTWS(InitShaderPrintContext(), Pos, TangentZ, Scale);
}
void AddReferentialTWS(float3 Pos, float3 T, float3 B, float3 N, float Scale = 1)
{
AddReferentialTWS(InitShaderPrintContext(), Pos, T, B, N, Scale);
}
void AddReferential(float3 Pos, float3x3 InM, float Scale = 1)
{
AddReferential(InitShaderPrintContext(), Pos, InM, Scale);
}
void AddReferential(float4x4 InM, float Scale = 1)
{
AddReferential(InitShaderPrintContext(), InM, Scale);
}
void AddReferential(float3 Pos, float3 TangentZ, float Scale = 1)
{
AddReferential(InitShaderPrintContext(), Pos, TangentZ, Scale);
}
void AddReferential(float3 Pos, float3 T, float3 B, float3 N, float Scale = 1)
{
AddReferential(InitShaderPrintContext(), Pos, T, B, N, Scale);
}
void AddLineTriangleWS(float3 P0, float3 P1, float3 P2, float4 Color)
{
AddLineTriangleWS(InitShaderPrintContext(), P0, P1, P2, Color);
}
void AddLineTriangleTWS(float3 P0, float3 P1, float3 P2, float4 Color)
{
AddLineTriangleTWS(InitShaderPrintContext(), P0, P1, P2, Color);
}
void AddLineTriangle(float3 P0, float3 P1, float3 P2, float4 Color)
{
AddLineTriangle(InitShaderPrintContext(), P0, P1, P2, Color);
}
void AddOBBWS(float3 Min, float3 Max, float4 Color, float4x4 Transform)
{
AddOBBWS(InitShaderPrintContext(), Min, Max, Color, Transform);
}
void AddOBBTWS(float3 Min, float3 Max, float4 Color, float4x4 Transform)
{
AddOBBTWS(InitShaderPrintContext(), Min, Max, Color, Transform);
}
void AddOBB(float3 Min, float3 Max, float4 Color, float4x4 Transform)
{
AddOBB(InitShaderPrintContext(), Min, Max, Color, Transform);
}
// Verify/Assert (experimental)
// Can be used in any shader binding ShaderPrint uniform buffer. It will print the
// assert for all pixels/threads not complying to the condition. All print will be
// stacked correctly.
//
// Example:
// verify(DispatchThreadId < MaxThread);
// verifyf(DispatchThreadId < MaxThread, TEXT("Error: ThreadId higher than MaxThread");
//
// This function also returns a FShaderPrintContext so that one can add more details if needed.
//
// Example:
// FShaderPrintContext Ctx = verify(DispatchThreadId < MaxThread, TEXT("Error: thread higher MaxThread");
// Print(Ctx, DispatchThreadId, FontWhite);
//
FShaderPrintContext Assert(bool InCondition, FShaderPrintText InText, uint2 InWritePos = uint2(50, 150))
{
FShaderPrintContext OutCtx = InitShaderPrintContext(false, InWritePos);
if (!InCondition)
{
uint LineOffset = 0;
SHADER_PRINT_INTERLOCKEDADD(SHADER_PRINT_RWENTRYBUFFER(OutCtx, 3) /* Free counter */, 1, LineOffset);
InWritePos.y += LineOffset * 12;
OutCtx = InitShaderPrintContext(true, InWritePos);
Print(OutCtx, InText, FontRed);
}
return OutCtx;
}
FShaderPrintContext Assert(bool InCondition, FShaderPrintText InText, FShaderPrintText InFile, uint InLine, FShaderPrintText InSeparator, uint2 InWritePos = uint2(50, 150))
{
FShaderPrintContext OutCtx = InitShaderPrintContext(false, InWritePos);
if (!InCondition)
{
uint LineOffset = 0;
SHADER_PRINT_INTERLOCKEDADD(SHADER_PRINT_RWENTRYBUFFER(OutCtx, 3) /* Free counter */, 1, LineOffset);
InWritePos.y += LineOffset * 12;
OutCtx = InitShaderPrintContext(true, InWritePos);
Print(OutCtx, InText, FontRed);
Print(OutCtx, InSeparator, FontRed);
Print(OutCtx, InFile, FontRed);
Print(OutCtx, InSeparator, FontRed);
Print(OutCtx, InLine, FontRed);
}
return OutCtx;
}
// Non-blocking shader check (unlike check()/checkf() which terminate the GPU execution)
#define verify(Condition) Assert(Condition, TEXT(#Condition), TEXT(__FILE__), __LINE__, TEXT(" - "));
#define verifyf(Condition, Message) Assert(Condition, Message, TEXT(__FILE__), __LINE__, TEXT(" - "));