300 lines
16 KiB
HLSL
300 lines
16 KiB
HLSL
#ifndef SHADER_DEBUG_PRINT_INCLUDED
|
|
#define SHADER_DEBUG_PRINT_INCLUDED
|
|
|
|
// NOTE: For URP - set ENABLE_SHADER_DEBUG_PRINT in the project to enable CPU-side integration.
|
|
// NOTE: Currently works in game view/play mode.
|
|
//
|
|
// Include this header to any shader to enable debug printing values from shader code to console.
|
|
//
|
|
// Select threads/pixels to print using plain 'if'.
|
|
//
|
|
// Example:
|
|
// float4 colorRGBA = float4(0.1, 0.2, 0.3, 0.4);
|
|
// if(all(int2(pixel.xy) == int2(100, 100)))
|
|
// ShaderDebugPrint(ShaderDebugTag('C','o','l'), colorRGBA);
|
|
// ----
|
|
// Output:
|
|
// Frame #270497: Col float4(0.1f, 0.2f, 0.3f, 0.4f)
|
|
// ----
|
|
//
|
|
// Example:
|
|
// #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ShaderDebugPrint.hlsl"
|
|
//
|
|
// Print pixel at mouse position.
|
|
// ShaderDebugPrintMouseOver(int2(thisPixel.xy), pixelColor);
|
|
//
|
|
// Print pixel at mouse position on button press.
|
|
// ShaderDebugPrintMouseButtonOver(int2(thisPixel.xy), pixelColor);
|
|
|
|
// Output buffer bound with cmd.SetGlobalTexture().
|
|
RWStructuredBuffer<uint> shaderDebugOutputData;
|
|
|
|
static const uint MaxShaderDebugOutputElements = 1024 * 16; // Must match the C# side buffer size (16K elems / 6 (header+tag+payload) ~= 2730 uint4s)
|
|
|
|
// Input Constants
|
|
CBUFFER_START(ShaderDebugPrintInput)
|
|
float4 _ShaderDebugPrintInputMouse;
|
|
int _ShaderDebugPrintInputFrame;
|
|
CBUFFER_END
|
|
|
|
// Mouse coordinates in pixels
|
|
// Relative to game view surface/rendertarget
|
|
// (Typically (0,0) is bottom-left in Unity. TIP: print mouse coords to check if unsure.)
|
|
int2 ShaderDebugMouseCoords() { return _ShaderDebugPrintInputMouse.xy; }
|
|
|
|
// Mouse buttons
|
|
// Returns true on button down.
|
|
int ShaderDebugMouseButtonLeft() { return _ShaderDebugPrintInputMouse.z; }
|
|
int ShaderDebugMouseButtonRight() { return _ShaderDebugPrintInputMouse.w; }
|
|
int ShaderDebugMouseButton(int button) { return button == 0 ? ShaderDebugMouseButtonLeft() : ShaderDebugMouseButtonRight(); }
|
|
|
|
int ShaderDebugFrameNumber() { return _ShaderDebugPrintInputFrame; }
|
|
|
|
// Output Data type encodings
|
|
// Must match C# side decoding
|
|
static const uint ValueTypeUint = 1;
|
|
static const uint ValueTypeInt = 2;
|
|
static const uint ValueTypeFloat = 3;
|
|
static const uint ValueTypeUint2 = 4;
|
|
static const uint ValueTypeInt2 = 5;
|
|
static const uint ValueTypeFloat2 = 6;
|
|
static const uint ValueTypeUint3 = 7;
|
|
static const uint ValueTypeInt3 = 8;
|
|
static const uint ValueTypeFloat3 = 9;
|
|
static const uint ValueTypeUint4 = 10;
|
|
static const uint ValueTypeInt4 = 11;
|
|
static const uint ValueTypeFloat4 = 12;
|
|
static const uint ValueTypeBool = 13;
|
|
static const uint ValueTypeHasTag = 128;
|
|
|
|
// Data-buffer format
|
|
// 1 uint header
|
|
// 1 uint tag (optional)
|
|
// 1-4 uint value (type dependent)
|
|
//
|
|
// Header format
|
|
// 1 byte Type id + tag flag
|
|
// bits 0..6 value type id/enum
|
|
// bit 7 has tag flag
|
|
// 3 bytes (empty)
|
|
#define PRINT1(TYPE, VALUE, HASTAG, TAG) \
|
|
{ \
|
|
if (shaderDebugOutputData[0] < MaxShaderDebugOutputElements) \
|
|
{ \
|
|
uint index; \
|
|
uint elements = 2; \
|
|
if (HASTAG) elements++; \
|
|
InterlockedAdd(shaderDebugOutputData[0], elements, index); \
|
|
index++; \
|
|
if (index < MaxShaderDebugOutputElements) \
|
|
{ \
|
|
shaderDebugOutputData[index++] = TYPE | HASTAG; \
|
|
if (HASTAG) shaderDebugOutputData[index++] = TAG; \
|
|
shaderDebugOutputData[index++] = VALUE; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
#define PRINT2(TYPE, VALUE, HASTAG, TAG) \
|
|
{ \
|
|
if (shaderDebugOutputData[0] < MaxShaderDebugOutputElements) \
|
|
{ \
|
|
uint index; \
|
|
uint elements = 3; \
|
|
if (HASTAG) elements++; \
|
|
InterlockedAdd(shaderDebugOutputData[0], elements, index); \
|
|
index++; \
|
|
if (index < MaxShaderDebugOutputElements) \
|
|
{ \
|
|
shaderDebugOutputData[index++] = TYPE | HASTAG; \
|
|
if (HASTAG) shaderDebugOutputData[index++] = TAG; \
|
|
shaderDebugOutputData[index++] = VALUE.x; \
|
|
shaderDebugOutputData[index++] = VALUE.y; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
#define PRINT3(TYPE, VALUE, HASTAG, TAG) \
|
|
{ \
|
|
if (shaderDebugOutputData[0] < MaxShaderDebugOutputElements) \
|
|
{ \
|
|
uint index; \
|
|
uint elements = 4; \
|
|
if (HASTAG) elements++; \
|
|
InterlockedAdd(shaderDebugOutputData[0], elements, index); \
|
|
index++; \
|
|
if (index < MaxShaderDebugOutputElements) \
|
|
{ \
|
|
shaderDebugOutputData[index++] = TYPE | HASTAG; \
|
|
if (HASTAG) shaderDebugOutputData[index++] = TAG; \
|
|
shaderDebugOutputData[index++] = VALUE.x; \
|
|
shaderDebugOutputData[index++] = VALUE.y; \
|
|
shaderDebugOutputData[index++] = VALUE.z; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
#define PRINT4(TYPE, VALUE, HASTAG, TAG) \
|
|
{ \
|
|
if (shaderDebugOutputData[0] < MaxShaderDebugOutputElements) \
|
|
{ \
|
|
uint index; \
|
|
uint elements = 5; \
|
|
if (HASTAG) elements++; \
|
|
InterlockedAdd(shaderDebugOutputData[0], elements, index); \
|
|
index++; \
|
|
if (index < MaxShaderDebugOutputElements) \
|
|
{ \
|
|
shaderDebugOutputData[index++] = TYPE | HASTAG; \
|
|
if (HASTAG) shaderDebugOutputData[index++] = TAG; \
|
|
shaderDebugOutputData[index++] = VALUE.x; \
|
|
shaderDebugOutputData[index++] = VALUE.y; \
|
|
shaderDebugOutputData[index++] = VALUE.z; \
|
|
shaderDebugOutputData[index++] = VALUE.w; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
static const uint ShaderDebugNoTag;
|
|
|
|
// Create 1-4 letter tags encoded into a uint
|
|
// For example
|
|
// ShaderDebugTag( 'M', 'y', 'I', 'd' );
|
|
uint ShaderDebugTag(uint a, uint b, uint c, uint d) { return a | (b << 8) | (c << 16) | (d << 24); }
|
|
uint ShaderDebugTag(uint a, uint b, uint c) { return ShaderDebugTag( a, b, c, 0); }
|
|
uint ShaderDebugTag(uint a, uint b) { return ShaderDebugTag( a, b, 0); }
|
|
uint ShaderDebugTag(uint a) { return ShaderDebugTag( a, 0); }
|
|
|
|
// Print value to (Unity) console
|
|
// Be careful to not print all N threads (thousands). Use if statements and thread ids to pick values only from a few threads.
|
|
// (tag), an optional text tag for the print. Use ShaderDebugTag() helper to create.
|
|
// value, to be printed
|
|
void ShaderDebugPrint(uint tag, bool value) PRINT1(ValueTypeBool, uint(value), ValueTypeHasTag, tag);
|
|
void ShaderDebugPrint(uint tag, uint value) PRINT1(ValueTypeUint, value, ValueTypeHasTag, tag);
|
|
void ShaderDebugPrint(uint tag, int value) PRINT1(ValueTypeInt, asuint(value), ValueTypeHasTag, tag);
|
|
void ShaderDebugPrint(uint tag, float value) PRINT1(ValueTypeFloat, asuint(value), ValueTypeHasTag, tag);
|
|
void ShaderDebugPrint(uint tag, uint2 value) PRINT2(ValueTypeUint2, value, ValueTypeHasTag, tag)
|
|
void ShaderDebugPrint(uint tag, int2 value) PRINT2(ValueTypeInt2, asuint(value), ValueTypeHasTag, tag)
|
|
void ShaderDebugPrint(uint tag, float2 value) PRINT2(ValueTypeFloat2, asuint(value), ValueTypeHasTag, tag)
|
|
void ShaderDebugPrint(uint tag, uint3 value) PRINT3(ValueTypeUint3, value, ValueTypeHasTag, tag)
|
|
void ShaderDebugPrint(uint tag, int3 value) PRINT3(ValueTypeInt3, asuint(value), ValueTypeHasTag, tag)
|
|
void ShaderDebugPrint(uint tag, float3 value) PRINT3(ValueTypeFloat3, asuint(value), ValueTypeHasTag, tag)
|
|
void ShaderDebugPrint(uint tag, uint4 value) PRINT4(ValueTypeUint4, value, ValueTypeHasTag, tag)
|
|
void ShaderDebugPrint(uint tag, int4 value) PRINT4(ValueTypeInt4, asuint(value), ValueTypeHasTag, tag)
|
|
void ShaderDebugPrint(uint tag, float4 value) PRINT4(ValueTypeFloat4, asuint(value), ValueTypeHasTag, tag)
|
|
void ShaderDebugPrint(bool value) PRINT1(ValueTypeBool, uint(value), 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(uint value) PRINT1(ValueTypeUint, value, 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(int value) PRINT1(ValueTypeInt, asuint(value), 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(float value) PRINT1(ValueTypeFloat, asuint(value), 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(uint2 value) PRINT2(ValueTypeUint2, value, 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(int2 value) PRINT2(ValueTypeInt2, asuint(value), 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(float2 value) PRINT2(ValueTypeFloat2, asuint(value), 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(uint3 value) PRINT3(ValueTypeUint3, value, 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(int3 value) PRINT3(ValueTypeInt3, asuint(value), 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(float3 value) PRINT3(ValueTypeFloat3, asuint(value), 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(uint4 value) PRINT4(ValueTypeUint4, value, 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(int4 value) PRINT4(ValueTypeInt4, asuint(value), 0, ShaderDebugNoTag)
|
|
void ShaderDebugPrint(float4 value) PRINT4(ValueTypeFloat4, asuint(value), 0, ShaderDebugNoTag)
|
|
|
|
#undef PRINT1
|
|
#undef PRINT2
|
|
#undef PRINT3
|
|
#undef PRINT4
|
|
|
|
#define PRINT_MOUSE(VALUE) \
|
|
{ \
|
|
if(all(pixelPos == ShaderDebugMouseCoords())) \
|
|
ShaderDebugPrint(VALUE); \
|
|
}
|
|
|
|
#define PRINT_MOUSE_WITH_TAG(VALUE, TAG) \
|
|
{ \
|
|
if(all(pixelPos == ShaderDebugMouseCoords())) \
|
|
ShaderDebugPrint(TAG, VALUE); \
|
|
}
|
|
|
|
// Print value for pixel under mouse cursor
|
|
// pixelPos, screen pixel coordinates for this fragment shader thread. Typically .xy of fragment shader input parameter with SV_Position semantic.
|
|
// NOTE: Any render target scaling (or offset) is NOT taken into account as that can be arbitrary. You must correct scaling manually.
|
|
// (For example: fragment.xy * _ScreenParams.xy / _ScaledScreenParams.xy or similar)
|
|
// TIP: Color the pixel (or a box) at mouse coords to debug scaling/offset.
|
|
// (tag), an optional text tag for the print. Use ShaderDebugTag() helper to create.
|
|
// value, to be printed
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, bool value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, uint value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, int value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, float value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, uint2 value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, int2 value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, float2 value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, uint3 value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, int3 value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, float3 value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, uint4 value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, int4 value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint tag, float4 value) PRINT_MOUSE_WITH_TAG(value, tag);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, bool value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, int value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, float value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint2 value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, int2 value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, float2 value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint3 value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, int3 value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, float3 value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, uint4 value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, int4 value) PRINT_MOUSE(value);
|
|
void ShaderDebugPrintMouseOver(int2 pixelPos, float4 value) PRINT_MOUSE(value);
|
|
|
|
#undef PRINT_MOUSE
|
|
#undef PRINT_MOUSE_WITH_TAG
|
|
|
|
#define PRINT_MOUSE_BUTTON(BUTTON, VALUE) \
|
|
{ \
|
|
if(ShaderDebugMouseButton(BUTTON) && all(pixelPos == ShaderDebugMouseCoords())) \
|
|
ShaderDebugPrint(VALUE); \
|
|
}
|
|
|
|
#define PRINT_MOUSE_BUTTON_WITH_TAG(BUTTON, VALUE, TAG) \
|
|
{ \
|
|
if(ShaderDebugMouseButton(BUTTON) && all(pixelPos == ShaderDebugMouseCoords())) \
|
|
ShaderDebugPrint(TAG, VALUE); \
|
|
}
|
|
|
|
// Print value for pixel under mouse cursor when mouse left button is pressed
|
|
// pixelPos, screen pixel coordinates for this fragment shader thread. Typically .xy of fragment shader input parameter with SV_Position semantic.
|
|
// (tag), an optional text tag for the print. Use ShaderDebugTag() helper to create.
|
|
// value, to be printed
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, bool value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, uint value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, int value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, float value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, uint2 value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, int2 value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, float2 value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, uint3 value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, int3 value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, float3 value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, uint4 value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, int4 value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint tag, float4 value) PRINT_MOUSE_BUTTON_WITH_TAG(0, value, tag);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, bool value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, int value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, float value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint2 value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, int2 value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, float2 value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint3 value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, int3 value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, float3 value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, uint4 value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, int4 value) PRINT_MOUSE_BUTTON(0, value);
|
|
void ShaderDebugPrintMouseButtonOver(int2 pixelPos, float4 value) PRINT_MOUSE_BUTTON(0, value);
|
|
|
|
#undef PRINT_MOUSE_BUTTON
|
|
#undef PRINT_MOUSE_BUTTON_WITH_TAG
|
|
|
|
#endif // SHADER_DEBUG_PRINT_INCLUDED
|