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

1486 lines
58 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ShaderPrintCommon.ush:
Include this to be able to call ShaderPrint() from arbitrary shaders.
=============================================================================*/
#pragma once
#ifndef SHADER_PRINT_ALLOW
#define SHADER_PRINT_ALLOW 1
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
// Description
// Shader Print allows to print value & text from shaders.
//
// 0. Contex-based state API
// -------------------------
// FShaderPrintContext Ctx = InitShaderPrintContext(all(SvPosition.xy == float2(100, 100)), uint2(50,50));
// --------------------------------- ------------
// Pixel/thread to filter Coord at which
// to draw
//
// 1. How to print value?
// ----------------------
// First create a context, then print scalar/vector/matrix/text...
//
// FShaderPrintContext Ctx = InitShaderPrintContext(all(PixelPos.xy == uint2(100, 100)), uint2(50,50));
//
// Print(Ctx, MyFloat);
// Newline(Ctx);
//
// Print(Ctx, TEXT("My string for labbelling a float :"), FontOrange);
// Print(Ctx, MyFloatInYellow, FontYellow);
// Newline(Ctx);
//
// Numerical values can be further formatted, by configuring the number of digits spanned by a number
// (i.e. the number of written&non-written digits), and the number of wanted decimal for floating value.
//
// Print(Ctx, 123, FontYellow, 5); // Spanning is show as: .....
// Print(Ctx, 456, FontYellow, 4); // Spanning is show as: xxxx
// Print(Ctx, 789, FontYellow, 3); // Spanning is show as: ---
//
// Will be displayed as follow:
// 123 456 789
// ..... xxxx ---
//
// Print(Ctx, 12.3456, FontYellow, 5, 2); // Spanning is show as: ...
// Print(Ctx, 12.3456, FontYellow, 5, 1); // Spanning is show as: xxx
//
// Will be displayed as follow:
// 12.34 12.3
// ..... xxxxx
//
// 2. How to add widget?
// ---------------------
// Widget can be added from shader.
// For slider:
//
// const float MySliderValue = AddSlider(Ctx, TEXT("My Slider"), DefaultValue, GetDefaultFontColor());
//
// For checkbox:
//
// const bool MyCheckboxValue = AddCheckbox(Ctx, TEXT("My Checkbox"), DefaultValue, GetDefaultFontColor());
//
// The value of these widgets can be retrieved/evaluated from *any* thread/pixels, but the
// drawing need to be done from a single thread/pixel. This is done through the ShaderPrintContext,
// which setup which pixel/thread is active/filtered. Example with a compute shader:
//
// [numthreads(8, 8, 1)]
// void MainCS(uint2 DispatchThreadId : SV_DispatchThreadID)
// {
// // Create context which 'add' the widget only on thread (0,0)
// FShaderPrintContext Ctx = InitShaderPrintContext(all(DispatchThreadId == uint2(0, 0)), uint2(50,50));
//
// // Any threads can get the slider value with this call
// const float ScaleValue = AddSlider(Ctx, TEXT("Scale"), 1.f, GetDefaultFontColor());
// ...
// }
//
///////////////////////////////////////////////////////////////////////////////////////////////////
// Primitive & Symbols layout in RWEntryBuffer
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// || Line | Triangle | Symbol | Empty || Symb0 | Symb1 | Symb2 | Symb3 | ... | Line0 | Line1 | Line2 | Line3 | ... | Triangle0 | Triangle1 | Triangle2 | Triangle3 | ...
// || Counter | Counter | Counter | Counter || | | | | ... | | | | | ... | | | | | ...
// || (1uint) | (1uint) | (1uint) | (1uint) || (4uint) | (4uint) | (4uint) | (4uint) | ... | (8uint) | (8uint) | (8uint) | (8uint) | ... | (12uint) | (12uint) | (12uint) | (12uint) | ...
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////////////////////////
// Input types
// Content of FShaderPrintItem.Type defines what to cast FShaderPrintItem.Value as
// Type of primitives
#define SHADER_PRINT_TYPE_SYMBOL 0
#define SHADER_PRINT_TYPE_FLOAT 1
#define SHADER_PRINT_TYPE_INT 2
#define SHADER_PRINT_TYPE_UINT 3
#define SHADER_PRINT_TYPE_HEX 4
#define SHADER_PRINT_TYPE_SLIDER 5
#define SHADER_PRINT_TYPE_CHECK 6
// Type of primitives (to be merged with the list above once all buffer are merged)
#define SHADER_PRINT_TYPE_LINE 0
#define SHADER_PRINT_TYPE_TRIANGLE 1
// Size of packed data, in uint
#define SHADER_PRINT_UINT_COUNT_SYMBOL 4
#define SHADER_PRINT_UINT_COUNT_LINE 8
#define SHADER_PRINT_UINT_COUNT_TRIANGLE 12
// Counter offset
#define SHADER_PRINT_COUNTER_OFFSET_LINE 0
#define SHADER_PRINT_COUNTER_OFFSET_TRIANGLE 1
#define SHADER_PRINT_COUNTER_OFFSET_SYMBOL 2
#define SHADER_PRINT_COUNTER_OFFSET_FREE 3
#define SHADER_PRINT_COUNTER_COUNT 4
uint GetSymbolOffset(uint InIndex)
{
return SHADER_PRINT_COUNTER_COUNT + SHADER_PRINT_UINT_COUNT_SYMBOL * InIndex;
}
uint GetPrimitiveLineOffset(uint InIndex, uint MaxCharacterCount)
{
return SHADER_PRINT_COUNTER_COUNT + SHADER_PRINT_UINT_COUNT_SYMBOL * MaxCharacterCount + SHADER_PRINT_UINT_COUNT_LINE * InIndex;
}
uint GetPrimitiveTriangleOffset(uint InIndex, uint MaxCharacterCount, uint MaxLineCount)
{
return SHADER_PRINT_COUNTER_COUNT + SHADER_PRINT_UINT_COUNT_SYMBOL * MaxCharacterCount + SHADER_PRINT_UINT_COUNT_LINE * MaxLineCount + SHADER_PRINT_UINT_COUNT_TRIANGLE * InIndex;
}
void ClearCounters(RWStructuredBuffer<uint> InRWBuffer)
{
// Symbol/print Counter
InRWBuffer[SHADER_PRINT_COUNTER_OFFSET_LINE] = 0;
InRWBuffer[SHADER_PRINT_COUNTER_OFFSET_TRIANGLE] = 0;
InRWBuffer[SHADER_PRINT_COUNTER_OFFSET_SYMBOL] = 0;
InRWBuffer[SHADER_PRINT_COUNTER_OFFSET_FREE] = 0;
}
void ClearCounters(RWBuffer<uint> InRWBuffer)
{
// Symbol/print Counter
InRWBuffer[SHADER_PRINT_COUNTER_OFFSET_LINE] = 0;
InRWBuffer[SHADER_PRINT_COUNTER_OFFSET_TRIANGLE] = 0;
InRWBuffer[SHADER_PRINT_COUNTER_OFFSET_SYMBOL] = 0;
InRWBuffer[SHADER_PRINT_COUNTER_OFFSET_FREE] = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// State
#define SHADER_PRINT_STATE_COUNT_OFFSET 0
#define SHADER_PRINT_STATE_VALUE_OFFSET 1
#define SHADER_PRINT_STATE_STRIDE 3
#define SHADER_PRINT_STATE_INDEX_METADATA 0
#define SHADER_PRINT_STATE_INDEX_VALUE 1
#define SHADER_PRINT_STATE_INDEX_FRAME 2
#define SHADER_PRINT_STATE_INVALID_INDEX 0xFF
#define SHADER_PRINT_STATE_HASH_MASK 0xFFFFFF
///////////////////////////////////////////////////////////////////////////////////////////////////
// Buffers/Resources accessors
#if !defined(SHADER_PRINT_RWENTRYBUFFER)
#define SHADER_PRINT_RWENTRYBUFFER(Ctx, Idx) Ctx.Buffers.RWEntryBuffer[Idx]
#define SHADER_PRINT_RWSTATEBUFFER(Ctx, Idx) Ctx.Buffers.StateBuffer[Idx]
#endif
// Currently a bug Metal shader translation cause compilation to fails due to atomic operation.
// As a workaround, disabling interlock add on Metal
#if COMPILER_METAL && FEATURE_LEVEL < FEATURE_LEVEL_SM6
#define SHADER_PRINT_INTERLOCKEDADD(InBuffer, InIncrement, OutOldValue)
#else
#define SHADER_PRINT_INTERLOCKEDADD(InBuffer, InIncrement, OutOldValue) InterlockedAdd(InBuffer, InIncrement, OutOldValue)
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
// Font Color
struct FFontColor
{
float3 Color;
};
FFontColor InitFontColor(float InX, float InY, float InZ) { FFontColor Out; Out.Color = float3(InX, InY, InZ); return Out; }
FFontColor InitFontColor(float3 In) { FFontColor Out; Out.Color = In; return Out; }
FFontColor GetDefaultFontColor() { FFontColor Out; Out.Color = float3(1,1,1); return Out; }
FFontColor Select(bool Condition, FFontColor A, FFontColor B)
{
if (Condition)
{
return A;
}
return B;
}
// Predefined colors
#define FontWhite InitFontColor(1, 1, 1)
#define FontGrey InitFontColor(0.5, 0.5, 0.5)
#define FontDarkGrey InitFontColor(0.25, 0.25, 0.25)
#define FontBlack InitFontColor(0, 0, 0)
#define FontRed InitFontColor(1, 0, 0)
#define FontGreen InitFontColor(0, 1, 0)
#define FontBlue InitFontColor(0, 0, 1)
#define FontYellow InitFontColor(1, 1, 0)
#define FontCyan InitFontColor(0, 1, 1)
#define FontMagenta InitFontColor(1, 0, 1)
#define FontOrange InitFontColor(243.f / 255.f, 156.f / 255.f, 18.f / 255.f)
#define FontPurple InitFontColor(169.f / 255.f, 7.f / 255.f, 228.f / 255.f)
#define FontTurquoise InitFontColor(26.f / 255.f, 188.f / 255.f, 156.f / 255.f)
#define FontSilver InitFontColor(189.f / 255.f, 195.f / 255.f, 199.f / 255.f)
#define FontEmerald InitFontColor(46.f / 255.f, 204.f / 255.f, 113.f / 255.f)
#define FontLightRed InitFontColor(0.75f, 0.50f, 0.50f)
#define FontLightGreen InitFontColor(0.50f, 0.75f, 0.50f)
#define FontLightBlue InitFontColor(0.50f, 0.50f, 0.75f)
#define FontLightYellow InitFontColor(0.75f, 0.75f, 0.50f)
#define FontDarkRed InitFontColor(0.50f, 0.25f, 0.25f)
#define FontDarkGreen InitFontColor(0.25f, 0.50f, 0.25f)
#define FontDarkBlue InitFontColor(0.25f, 0.25f, 0.50f)
///////////////////////////////////////////////////////////////////////////////////////////////////
// Primitive colors
#define ColorWhite float4(1, 1, 1, 1)
#define ColorBlack float4(0, 0, 0, 1)
#define ColorRed float4(1, 0, 0, 1)
#define ColorGreen float4(0, 1, 0, 1)
#define ColorBlue float4(0, 0, 1, 1)
#define ColorYellow float4(1, 1, 0, 1)
#define ColorCyan float4(0, 1, 1, 1)
#define ColorMagenta float4(1, 0, 1, 1)
#define ColorOrange float4(243.f / 255.f, 156.f / 255.f, 18.f / 255.f, 1)
#define ColorPurple float4(169.f / 255.f, 7.f / 255.f, 228.f / 255.f, 1)
#define ColorTurquoise float4( 26.f / 255.f, 188.f / 255.f, 156.f / 255.f, 1)
#define ColorSilver float4(189.f / 255.f, 195.f / 255.f, 199.f / 255.f, 1)
#define ColorEmerald float4( 46.f / 255.f, 204.f / 255.f, 113.f / 255.f, 1)
#define ColorLightRed float4(0.75f, 0.50f, 0.50f, 1.0f)
#define ColorLightGreen float4(0.50f, 0.75f, 0.50f, 1.0f)
#define ColorLightBlue float4(0.50f, 0.50f, 0.75f, 1.0f)
#define ColorDarkRed float4(0.50f, 0.25f, 0.25f, 1.0f)
#define ColorDarkGreen float4(0.25f, 0.50f, 0.25f, 1.0f)
#define ColorDarkBlue float4(0.25f, 0.25f, 0.50f, 1.0f)
#define ColorDarkOrange float4(ColorOrange.xyz * 0.5f,1.0f)
///////////////////////////////////////////////////////////////////////////////////////////////////
// Util pack/unpack functions
struct FShaderPrintItem
{
float2 ScreenPos; // Position in normalized coordinates
int Value; // Cast to value or symbol
int Type; // SHADER_PRINT_TYPE_* defines how to read Value
float3 Color; // Color
uint Metadata; // Metadata (used for widget to store extra state data
};
// Needs to match C++ code in ShaderPrint.cpp
struct FPackedShaderPrintItem
{
uint ScreenPos16bits; // Position in normalized coordinates
uint Value; // Cast to value or symbol
uint TypeAndColor; //
uint Metadata; //
};
FPackedShaderPrintItem PackShaderPrintItem(FShaderPrintItem In)
{
const uint3 Color8bits = saturate(In.Color) * 0xFF;
const uint2 ScreenPos16bit = f32tof16(In.ScreenPos);
FPackedShaderPrintItem Out;
Out.ScreenPos16bits = ScreenPos16bit.x | (ScreenPos16bit.y<<16);
Out.Value = asuint(In.Value);
Out.TypeAndColor = (Color8bits.z << 24) | (Color8bits.y << 16) | (Color8bits.x << 8) | (In.Type & 0xFF);
Out.Metadata = In.Metadata;
return Out;
}
FShaderPrintItem UnpackShaderPrintItem(FPackedShaderPrintItem In)
{
const uint2 ScreenPos16bits = uint2(In.ScreenPos16bits & 0xFFFF, (In.ScreenPos16bits>>16) & 0xFFFF);
FShaderPrintItem Out;
Out.ScreenPos = f16tof32(ScreenPos16bits);
Out.Value = asint(In.Value);
Out.Type = (In.TypeAndColor) & 0xFF;
Out.Color.x = float((In.TypeAndColor >> 8) & 0xFF) / 255.f;
Out.Color.y = float((In.TypeAndColor >> 16) & 0xFF) / 255.f;
Out.Color.z = float((In.TypeAndColor >> 24) & 0xFF) / 255.f;
Out.Metadata = In.Metadata;
return Out;
}
// /!\ If you update this function, update the WriteSymbol() version without explicit buffer, below in this file
void WriteSymbol(uint Offset, FShaderPrintItem In, RWBuffer<uint> InRWBuffer)
{
const uint Offset4 = GetSymbolOffset(Offset);
const FPackedShaderPrintItem Packed = PackShaderPrintItem(In);
InRWBuffer[Offset4 + 0] = Packed.ScreenPos16bits;
InRWBuffer[Offset4 + 1] = Packed.Value;
InRWBuffer[Offset4 + 2] = Packed.TypeAndColor;
InRWBuffer[Offset4 + 3] = Packed.Metadata;
}
FShaderPrintItem ReadSymbol(uint Offset, StructuredBuffer<uint> InBuffer)
{
const uint Offset4 = GetSymbolOffset(Offset);
FPackedShaderPrintItem Packed = (FPackedShaderPrintItem)0;
Packed.ScreenPos16bits = InBuffer[Offset4 + 0];
Packed.Value = InBuffer[Offset4 + 1];
Packed.TypeAndColor = InBuffer[Offset4 + 2];
Packed.Metadata = InBuffer[Offset4 + 3];
return UnpackShaderPrintItem(Packed);
}
FShaderPrintItem ReadSymbol(uint Offset, Buffer<uint> InBuffer)
{
const uint Offset4 = GetSymbolOffset(Offset);
FPackedShaderPrintItem Packed = (FPackedShaderPrintItem)0;
Packed.ScreenPos16bits = InBuffer[Offset4 + 0];
Packed.Value = InBuffer[Offset4 + 1];
Packed.TypeAndColor = InBuffer[Offset4 + 2];
Packed.Metadata = InBuffer[Offset4 + 3];
return UnpackShaderPrintItem(Packed);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Context structures.
// Stores the current dynamic state, and the constant data describing config and buffer bindings.
struct FShaderPrintConfig
{
int2 Resolution;
int2 CursorCoord;
float3 TranslatedWorldOffset;
float2 FontSize;
float2 FontSpacing;
uint MaxCharacterCount;
uint MaxSymbolCount;
uint MaxStateCount;
uint MaxLineCount;
uint MaxTriangleCount;
bool bIsDrawLocked;
};
struct FShaderPrintBuffers
{
Buffer<uint> StateBuffer;
RWBuffer<uint> RWEntryBuffer;
};
struct FShaderPrintContext
{
bool bIsActive;
float2 StartPos;
float2 Pos;
FShaderPrintConfig Config;
bool IsDrawLocked() { return Config.bIsDrawLocked; }
#if !SHADER_PRINT_USE_GLOBAL_RESOURCE
FShaderPrintBuffers Buffers;
#endif
};
// Version using SHADER_PRINT_RWENTRYBUFFER in order to provide offset value
void WriteSymbol(uint Offset, FShaderPrintItem In, FShaderPrintContext Ctx)
{
const uint Offset4 = GetSymbolOffset(Offset);
const FPackedShaderPrintItem Packed = PackShaderPrintItem(In);
SHADER_PRINT_RWENTRYBUFFER(Ctx, Offset4 + 0) = Packed.ScreenPos16bits;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Offset4 + 1) = Packed.Value;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Offset4 + 2) = Packed.TypeAndColor;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Offset4 + 3) = Packed.Metadata;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Internal helpers
void ShaderPrint_Internal(FShaderPrintContext Ctx, in FShaderPrintItem Item)
{
// If MaxCharacterCount is 0 then we don't reset the buffer counter so early out here
if (!Ctx.bIsActive || Ctx.Config.MaxCharacterCount == 0)
{
return;
}
// Buffer counter is stored in first element .Value
int IndexToStore = 0;
SHADER_PRINT_INTERLOCKEDADD(SHADER_PRINT_RWENTRYBUFFER(Ctx,SHADER_PRINT_COUNTER_OFFSET_SYMBOL), 1, IndexToStore);
// Prevent writing off the buffer
// Note that counter still increases so need clamp when reading it in later passes
if (uint(IndexToStore) >= Ctx.Config.MaxCharacterCount)
{
return;
}
WriteSymbol(IndexToStore, Item, Ctx);
}
void ShaderPrint_Internal(FShaderPrintContext Ctx, in float2 ScreenPos, in int Value, in FFontColor FontColor, in uint Metadata, in int Type)
{
FShaderPrintItem Item;
Item.ScreenPos = ScreenPos;
Item.Value = Value;
Item.Type = Type;
Item.Color = FontColor.Color;
Item.Metadata = Metadata;
ShaderPrint_Internal(Ctx, Item);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Symbol printing
void PrintSymbol(inout FShaderPrintContext Ctx, in int Symbol, in FFontColor Color)
{
if (Ctx.bIsActive)
{
ShaderPrint_Internal(Ctx, Ctx.Pos, Symbol, Color, 0u, SHADER_PRINT_TYPE_SYMBOL);
Ctx.Pos.x += Ctx.Config.FontSpacing.x;
}
}
void PrintSymbol(inout FShaderPrintContext Ctx, in int Symbol) { PrintSymbol(Ctx, Symbol, GetDefaultFontColor()); }
void Newline(inout FShaderPrintContext Ctx)
{
if (Ctx.bIsActive)
{
Ctx.Pos.x = Ctx.StartPos.x;
Ctx.Pos.y += Ctx.Config.FontSpacing.y;
}
}
int2 GetCursorPos(FShaderPrintContext Ctx)
{
return Ctx.Config.CursorCoord;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Text & hash
struct FShaderPrintText
{
uint Index;
};
FShaderPrintText InitShaderPrintText(uint InIndex)
{
FShaderPrintText Out;
Out.Index = InIndex;
return Out;
}
// Forward declarations for text accessors
uint ShaderPrintGetChar(uint InIndex);
uint ShaderPrintGetOffset(FShaderPrintText InTextEntry);
uint ShaderPrintGetHash(FShaderPrintText InTextEntry);
// Function for reading global TEXT string
float2 ShaderPrintText_Internal(FShaderPrintContext Ctx, bool bIsActive, float2 InPos, FShaderPrintText InTextEntry, FFontColor InColor)
{
// If MaxCharacterCount is 0 then we don't reset the buffer counter so early out here
if (bIsActive && Ctx.Config.MaxCharacterCount > 0)
{
const uint Begin = ShaderPrintGetOffset(InTextEntry);
const uint End = ShaderPrintGetOffset(InitShaderPrintText(InTextEntry.Index + 1));
const uint Count = End - Begin;
// Buffer counter is stored in first element .Value
// Prevent writing off the buffer
// Note that counter still increases so need clamp when reading it in later passes
int IndexToStore = 0;
SHADER_PRINT_INTERLOCKEDADD(SHADER_PRINT_RWENTRYBUFFER(Ctx,SHADER_PRINT_COUNTER_OFFSET_SYMBOL), Count, IndexToStore);
if (uint(IndexToStore + Count) < Ctx.Config.MaxCharacterCount)
{
for (uint i = Begin; i < End; ++i)
{
FShaderPrintItem Item;
Item.ScreenPos = InPos;
Item.Value = ShaderPrintGetChar(i);
Item.Type = SHADER_PRINT_TYPE_SYMBOL;
Item.Color = InColor.Color;
Item.Metadata = 0u;
WriteSymbol(IndexToStore, Item, Ctx);
++IndexToStore;
InPos.x += Ctx.Config.FontSpacing.x;
}
}
}
return InPos;
}
void Print(inout FShaderPrintContext Ctx, FShaderPrintText InTextEntry, FFontColor InColor) { Ctx.Pos = ShaderPrintText_Internal(Ctx, Ctx.bIsActive, Ctx.Pos, InTextEntry, InColor); }
void Print(inout FShaderPrintContext Ctx, FShaderPrintText InTextEntry) { Ctx.Pos = ShaderPrintText_Internal(Ctx, Ctx.bIsActive, Ctx.Pos, InTextEntry, GetDefaultFontColor()); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Value printing (common value printing)
#define MAX_DECIMAL_COUNT 5u
#define MAX_DIGIT_COUNT 12u
float2 ShaderPrintValue_Internal(FShaderPrintContext Ctx, float2 ScreenPos, float Value, FFontColor Color, uint MaxDigit, uint MaxDecimal) { ShaderPrint_Internal(Ctx, ScreenPos, asint(Value), Color, MaxDecimal, SHADER_PRINT_TYPE_FLOAT); ScreenPos.x += Ctx.Config.FontSpacing.x * MaxDigit; return ScreenPos; }
float2 ShaderPrintValue_Internal(FShaderPrintContext Ctx, float2 ScreenPos, int Value, FFontColor Color, uint MaxDigit, uint MaxDecimal) { ShaderPrint_Internal(Ctx, ScreenPos, Value, Color, MaxDecimal, SHADER_PRINT_TYPE_INT); ScreenPos.x += Ctx.Config.FontSpacing.x * MaxDigit; return ScreenPos; }
float2 ShaderPrintValue_Internal(FShaderPrintContext Ctx, float2 ScreenPos, uint Value, FFontColor Color, uint MaxDigit, uint MaxDecimal) { ShaderPrint_Internal(Ctx, ScreenPos, asint(Value), Color, MaxDecimal, SHADER_PRINT_TYPE_UINT); ScreenPos.x += Ctx.Config.FontSpacing.x * MaxDigit; return ScreenPos; }
float2 ShaderPrintValue_Internal(FShaderPrintContext Ctx, float2 ScreenPos, bool Value, FFontColor Color, uint MaxDigit, uint MaxDecimal) { ShaderPrint_Internal(Ctx, ScreenPos, asint(Value ? 1u : 0u), Color, MaxDecimal, SHADER_PRINT_TYPE_UINT); ScreenPos.x += Ctx.Config.FontSpacing.x * MaxDigit; return ScreenPos; }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Scalar type printing
#define SHADER_PRINT_OVERLOAD_1(InNumComponent, InType) \
void Print(inout FShaderPrintContext Ctx, InType Value, FFontColor Color, uint MaxDigit=MAX_DIGIT_COUNT, uint MaxDecimal=MAX_DECIMAL_COUNT) { if (Ctx.bIsActive) { Ctx.Pos = ShaderPrintValue_Internal(Ctx, Ctx.Pos, Value, Color, MaxDigit, MaxDecimal); } } \
void Print(inout FShaderPrintContext Ctx, InType Value) { if (Ctx.bIsActive) { Ctx.Pos = ShaderPrintValue_Internal(Ctx, Ctx.Pos, Value, GetDefaultFontColor(), MAX_DIGIT_COUNT, MAX_DECIMAL_COUNT); } }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Vector type printing
#define SHADER_PRINT_OVERLOAD_N(InNumComponent, InType) \
void Print(inout FShaderPrintContext Ctx, InType Value, FFontColor Color, uint MaxDigit=MAX_DIGIT_COUNT, uint MaxDecimal=MAX_DECIMAL_COUNT) { if (Ctx.bIsActive) { UNROLL for (uint CompIt=0;CompIt<InNumComponent;++CompIt) { Ctx.Pos = ShaderPrintValue_Internal(Ctx, Ctx.Pos, Value[CompIt], Color, MaxDigit, MaxDecimal); } } } \
void Print(inout FShaderPrintContext Ctx, InType Value) { if (Ctx.bIsActive) { UNROLL for (uint CompIt=0;CompIt<InNumComponent;++CompIt) { Ctx.Pos = ShaderPrintValue_Internal(Ctx, Ctx.Pos, Value[CompIt], GetDefaultFontColor(), MAX_DIGIT_COUNT, MAX_DECIMAL_COUNT); } } }
// Print(X) version
SHADER_PRINT_OVERLOAD_1(1, float)
SHADER_PRINT_OVERLOAD_N(2, float2)
SHADER_PRINT_OVERLOAD_N(3, float3)
SHADER_PRINT_OVERLOAD_N(4, float4)
SHADER_PRINT_OVERLOAD_1(1, uint)
SHADER_PRINT_OVERLOAD_N(2, uint2)
SHADER_PRINT_OVERLOAD_N(3, uint3)
SHADER_PRINT_OVERLOAD_N(4, uint4)
SHADER_PRINT_OVERLOAD_1(1, int)
SHADER_PRINT_OVERLOAD_N(2, int2)
SHADER_PRINT_OVERLOAD_N(3, int3)
SHADER_PRINT_OVERLOAD_N(4, int4)
SHADER_PRINT_OVERLOAD_1(1, bool)
///////////////////////////////////////////////////////////////////////////////////////////////////
// Matrix type printing
#define SHADER_PRINT_OVERLOAD_NN(InNumComponentX, InNumComponentY, InType) \
void Print(inout FShaderPrintContext Ctx, InType Value, FFontColor Color, uint MaxDigit=MAX_DIGIT_COUNT, uint MaxDecimal=MAX_DECIMAL_COUNT) { if (Ctx.bIsActive) { UNROLL for (uint CompY=0;CompY<InNumComponentY;++CompY) { Print(Ctx, Value[CompY], Color, MaxDigit, MaxDecimal); Ctx.Pos += float2(0.f, Ctx.Config.FontSpacing.y); } } } \
void Print(inout FShaderPrintContext Ctx, InType Value) { if (Ctx.bIsActive) { UNROLL for (uint CompY=0;CompY<InNumComponentY;++CompY) { Print(Ctx, Value[CompY], GetDefaultFontColor(), MAX_DIGIT_COUNT, MAX_DECIMAL_COUNT); Ctx.Pos += float2(0.f, Ctx.Config.FontSpacing.y); } } }
// Print(X) version
SHADER_PRINT_OVERLOAD_NN(3, 3, float3x3)
SHADER_PRINT_OVERLOAD_NN(4, 3, float4x3)
SHADER_PRINT_OVERLOAD_NN(4, 4, float4x4)
///////////////////////////////////////////////////////////////////////////////////////////////////
// Bool printing
// Note: Use a define rather than a function to avoid unecessary TEXT() parsing during shader compilation, even for shader not using ShaderPrint.
#define PrintBool(Ctx, In) { if (In) Print(Ctx, TEXT("Yes "), FontGreen); else Print(Ctx, TEXT("No "), FontRed); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Widgets (experimental)
uint2 GetCheckboxWidgetSize(float2 FontSpacing, int2 Resolution)
{
return uint(FontSpacing.x * Resolution.x).xx;
}
uint2 GetSliderWidgetSize(float2 FontSpacing, int2 Resolution)
{
return uint2(10,1) * uint(FontSpacing.x * Resolution.x);
}
struct FShaderPrintMetadata
{
uint Hash;
uint Index;
};
uint ShaderPrintPackMetadata(uint InHash, uint InIndex)
{
return ((InIndex & 0xFFu) << 24) | (InHash & SHADER_PRINT_STATE_HASH_MASK);
}
uint ShaderPrintPackMetadata(FShaderPrintMetadata In) { return ShaderPrintPackMetadata(In.Hash, In.Index); }
FShaderPrintMetadata ShaderPrintUnpackMetadata(uint Metadata)
{
FShaderPrintMetadata Out;
Out.Hash = (Metadata & SHADER_PRINT_STATE_HASH_MASK);
Out.Index= (Metadata >> 24) & 0xFF;
return Out;
}
// Return true if found, false otherwise
bool ShaderPrintGetStateValue(FShaderPrintContext Ctx, in uint InHash, inout uint OutIndex, inout uint OutValue)
{
OutIndex = SHADER_PRINT_STATE_INVALID_INDEX;
OutValue = 0;
// Slow linear search among all widget states
const uint MaxCount = min(SHADER_PRINT_RWSTATEBUFFER(Ctx, SHADER_PRINT_STATE_COUNT_OFFSET), Ctx.Config.MaxStateCount);
for (uint Index = 0; Index < MaxCount; ++Index)
{
const uint Index3 = Index * SHADER_PRINT_STATE_STRIDE + SHADER_PRINT_STATE_VALUE_OFFSET;
const FShaderPrintMetadata Metadata = ShaderPrintUnpackMetadata(SHADER_PRINT_RWSTATEBUFFER(Ctx, Index3 + SHADER_PRINT_STATE_INDEX_METADATA));
if (Metadata.Hash == InHash)
{
OutIndex = Metadata.Index;
OutValue = SHADER_PRINT_RWSTATEBUFFER(Ctx, Index3 + SHADER_PRINT_STATE_INDEX_VALUE);
return true;
}
}
return false;
}
// Add a checkbox widget
bool AddCheckbox(inout FShaderPrintContext Ctx, FShaderPrintText InTextEntry, bool bDefault, in FFontColor InColor)
{
uint Index = 0;
uint RawValue = 0;
const uint Hash = ShaderPrintGetHash(InTextEntry) & SHADER_PRINT_STATE_HASH_MASK; // Trunk hask since metadata has limited storage
const bool bIsValid = ShaderPrintGetStateValue(Ctx, Hash, Index, RawValue);
RawValue = bIsValid ? RawValue : (bDefault ? 0x1 : 0x0);
if (Ctx.bIsActive)
{
const uint2 Coord = Ctx.Pos * Ctx.Config.Resolution;
const uint2 Cusor = Ctx.Config.CursorCoord;
const uint2 WidgetSize = GetCheckboxWidgetSize(Ctx.Config.FontSpacing, Ctx.Config.Resolution);
const uint2 WidgetMax = uint2(Coord.x + WidgetSize.x, Coord.y + WidgetSize.y / 2);
const uint2 WidgetMin = uint2(Coord.x, Coord.y - WidgetSize.y / 2);
const bool bIsInside = all(Cusor >= WidgetMin) && all(Cusor <= WidgetMax);
const bool bWasInside = RawValue & 0x2;
// Update checkbox value
// Track if the cursor was already within the box or not
if (bIsInside)
{
if (!bWasInside)
{
// First time the cursor is within the checkbox
RawValue = ((RawValue & 0x1) == 1 ? 0x0 : 0x1) | 0x2;
}
}
else
{
RawValue = (RawValue & 0x1);
}
FShaderPrintItem E;
E.ScreenPos = Ctx.Pos;
E.Value = RawValue;
E.Type = SHADER_PRINT_TYPE_CHECK;
E.Color = bIsInside ? FontYellow.Color : FontWhite.Color;// InColor.Color;
E.Metadata = ShaderPrintPackMetadata(Hash, Index);
ShaderPrint_Internal(Ctx, E);
Ctx.Pos.x += float(WidgetSize.x) / float(Ctx.Config.Resolution.x) + Ctx.Config.FontSpacing.x;
// Text
Print(Ctx, InTextEntry, InColor);
Ctx.Pos.x += Ctx.Config.FontSpacing.x;
}
return RawValue & 0x1;
}
// Add a slider widget
float AddSlider(inout FShaderPrintContext Ctx, FShaderPrintText InTextEntry, float bDefault, in FFontColor InColor, float InMin = 0.f, float InMax = 1.f)
{
// The value is stored as normalized, for displaying slide value correctly. This is not idealy
// as it implies some precision lost due to back&forth conversion between normalized/non-normalized value.
uint Index = 0;
uint RawValue = 0;
const uint Hash = ShaderPrintGetHash(InTextEntry) & SHADER_PRINT_STATE_HASH_MASK; // Trunk hask since metadata has limited storage
const bool bIsValid = ShaderPrintGetStateValue(Ctx, Hash, Index, RawValue);
float NormalizedValue = bIsValid ? asfloat(RawValue) : bDefault;
float Value = NormalizedValue * (InMax - InMin) + InMin;
if (Ctx.bIsActive)
{
const uint2 Coord = Ctx.Pos * Ctx.Config.Resolution;
const uint2 Cusor = Ctx.Config.CursorCoord;
const uint2 WidgetSize = GetSliderWidgetSize(Ctx.Config.FontSpacing, Ctx.Config.Resolution);
const uint2 WidgetMax = uint2(Coord.x + WidgetSize.x, Coord.y + WidgetSize.y / 2);
const uint2 WidgetMin = uint2(Coord.x, Coord.y - WidgetSize.y / 2);
const bool bIsInside = all(Cusor >= WidgetMin) && all(Cusor <= WidgetMax);
// Update slider value
if (bIsInside)
{
const float S = saturate(float(Cusor.x - WidgetMin.x) / float(WidgetMax.x - WidgetMin.x));
Value = lerp(InMin, InMax, S);
}
NormalizedValue = saturate((Value - InMin) / (InMax - InMin));
FShaderPrintItem E;
E.ScreenPos = Ctx.Pos;
E.Value = asint(NormalizedValue);
E.Type = SHADER_PRINT_TYPE_SLIDER;
E.Color = bIsInside ? FontYellow.Color : FontWhite.Color; // InColor.Color;
E.Metadata = ShaderPrintPackMetadata(Hash, Index);
ShaderPrint_Internal(Ctx, E);
Ctx.Pos.x += float(WidgetSize.x) / float(Ctx.Config.Resolution.x) + Ctx.Config.FontSpacing.x;
// Text
Print(Ctx, InTextEntry, InColor);
Ctx.Pos.x += Ctx.Config.FontSpacing.x;
}
return Value;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Primitives types
///////////////////////////////////////////////////////////////////////////////////////////////////
// Triangle-based Primitives (Triangle, Point, ...)
struct FTriangleElement
{
float3 Pos0;
float3 Pos1;
float3 Pos2;
float4 Color;
bool bIsScreenSpace;
};
struct FPackedTriangleElement
{
uint4 Packed0;
uint4 Packed1;
uint4 Packed2;
};
FTriangleElement UnpackTriangleElement(FPackedTriangleElement In)
{
FTriangleElement Out = (FTriangleElement)0;
{
Out.Pos0.x = asfloat(In.Packed0.x);
Out.Pos0.y = asfloat(In.Packed0.y);
Out.Pos0.z = asfloat(In.Packed0.z);
Out.Color = float4((In.Packed0.w >> 24) & 0xFF, (In.Packed0.w >> 16) & 0xFF, (In.Packed0.w >> 8) & 0xFF, (In.Packed0.w) & 0xFF) / 255.0f;
}
{
Out.Pos1.x = asfloat(In.Packed1.x);
Out.Pos1.y = asfloat(In.Packed1.y);
Out.Pos1.z = asfloat(In.Packed1.z);
// The 'space' info is stored into the alpha's LSB
Out.bIsScreenSpace = (In.Packed1.w & 0x1) > 0u;
}
{
Out.Pos2.x = asfloat(In.Packed2.x);
Out.Pos2.y = asfloat(In.Packed2.y);
Out.Pos2.z = asfloat(In.Packed2.z);
}
return Out;
}
FPackedTriangleElement PackTriangleElement(FTriangleElement In)
{
uint4 PackedC = uint4(255.0f * saturate(In.Color));
FPackedTriangleElement Out = (FPackedTriangleElement)0;
Out.Packed0.x = asuint(In.Pos0.x);
Out.Packed0.y = asuint(In.Pos0.y);
Out.Packed0.z = asuint(In.Pos0.z);
Out.Packed0.w = (PackedC.x << 24) | (PackedC.y << 16) | (PackedC.z << 8) | (PackedC.w);
Out.Packed1.x = asuint(In.Pos1.x);
Out.Packed1.y = asuint(In.Pos1.y);
Out.Packed1.z = asuint(In.Pos1.z);
Out.Packed1.w = In.bIsScreenSpace ? 0x1 : 0x0;
Out.Packed2.x = asuint(In.Pos2.x);
Out.Packed2.y = asuint(In.Pos2.y);
Out.Packed2.z = asuint(In.Pos2.z);
Out.Packed2.w = 0;
return Out;
}
FTriangleElement UnpackTriangleElement(Buffer<uint> InPrimitiveBuffer, uint InIndex, uint MaxCharacterCount, uint MaxLineCount)
{
const uint Index12 = GetPrimitiveTriangleOffset(InIndex, MaxCharacterCount, MaxLineCount);
FPackedTriangleElement Out = (FPackedTriangleElement)0;
Out.Packed0.x = InPrimitiveBuffer[Index12 + 0];
Out.Packed0.y = InPrimitiveBuffer[Index12 + 1];
Out.Packed0.z = InPrimitiveBuffer[Index12 + 2];
Out.Packed0.w = InPrimitiveBuffer[Index12 + 3];
Out.Packed1.x = InPrimitiveBuffer[Index12 + 4];
Out.Packed1.y = InPrimitiveBuffer[Index12 + 5];
Out.Packed1.z = InPrimitiveBuffer[Index12 + 6];
Out.Packed1.w = InPrimitiveBuffer[Index12 + 7];
Out.Packed2.x = InPrimitiveBuffer[Index12 + 8];
Out.Packed2.y = InPrimitiveBuffer[Index12 + 9];
Out.Packed2.z = InPrimitiveBuffer[Index12 + 10];
Out.Packed2.w = InPrimitiveBuffer[Index12 + 11];
return UnpackTriangleElement(Out);
}
bool AllocateTriangleElement(FShaderPrintContext Ctx, uint Count, inout uint OutIndex)
{
OutIndex = 0;
if (Ctx.Config.MaxTriangleCount == 0)
{
return false;
}
SHADER_PRINT_INTERLOCKEDADD(SHADER_PRINT_RWENTRYBUFFER(Ctx,SHADER_PRINT_COUNTER_OFFSET_TRIANGLE), Count, OutIndex);
return (OutIndex + Count) < Ctx.Config.MaxTriangleCount;
}
void AddTriangleElement(FShaderPrintContext Ctx, FTriangleElement In, uint Index)
{
const uint Index12 = GetPrimitiveTriangleOffset(Index, Ctx.Config.MaxCharacterCount, Ctx.Config.MaxLineCount);
FPackedTriangleElement Out = PackTriangleElement(In);
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 0) = Out.Packed0.x;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 1) = Out.Packed0.y;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 2) = Out.Packed0.z;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 3) = Out.Packed0.w;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 4) = Out.Packed1.x;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 5) = Out.Packed1.y;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 6) = Out.Packed1.z;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 7) = Out.Packed1.w;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 8) = Out.Packed2.x;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 9) = Out.Packed2.y;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 10) = Out.Packed2.z;
SHADER_PRINT_RWENTRYBUFFER(Ctx, Index12 + 11) = Out.Packed2.w;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Filled Triangle
void AddFilledTriangleTWS(FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float3 Pos2, float4 Color)
{
if (Ctx.bIsActive)
{
uint Offset = 0;
if (AllocateTriangleElement(Ctx, 1, Offset))
{
FTriangleElement Element;
Element.bIsScreenSpace = false;
Element.Color = Color;
Element.Pos0 = Pos0;
Element.Pos1 = Pos1;
Element.Pos2 = Pos2;
AddTriangleElement(Ctx, Element, Offset + 0);
}
}
}
void AddFilledTriangleWS (FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float3 Pos2, float4 Color) { AddFilledTriangleTWS(Ctx, Pos0 + Ctx.Config.TranslatedWorldOffset, Pos1 + Ctx.Config.TranslatedWorldOffset, Pos2 + Ctx.Config.TranslatedWorldOffset, Color); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Filled Quad
void AddFilledQuadTWS(FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float3 Pos2, float3 Pos3, float4 Color)
{
if (Ctx.bIsActive)
{
uint Offset = 0;
if (AllocateTriangleElement(Ctx, 2, Offset))
{
FTriangleElement Element;
Element.bIsScreenSpace = false;
Element.Color = Color;
Element.Pos0 = Pos0;
Element.Pos1 = Pos1;
Element.Pos2 = Pos2;
AddTriangleElement(Ctx, Element, Offset + 0);
Element.Pos0 = Pos0;
Element.Pos1 = Pos2;
Element.Pos2 = Pos3;
AddTriangleElement(Ctx, Element, Offset + 1);
}
}
}
void AddFilledQuadWS (FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float3 Pos2, float3 Pos3, float4 Color) { AddFilledQuadTWS(Ctx, Pos0 + Ctx.Config.TranslatedWorldOffset, Pos1 + Ctx.Config.TranslatedWorldOffset, Pos2 + Ctx.Config.TranslatedWorldOffset, Pos3 + Ctx.Config.TranslatedWorldOffset, Color); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Filled Quad (Screen-Space)
void AddFilledQuadSS(FShaderPrintContext Ctx, float2 InPosInPixel0, float2 InPosInPixel2, float4 Color)
{
if (Ctx.bIsActive)
{
const float2 InPosInPixel1 = float2(InPosInPixel2.x, InPosInPixel0.y);
const float2 InPosInPixel3 = float2(InPosInPixel0.x, InPosInPixel2.y);
const float Depth = 0.5f;
const float3 Pos0 = float3((InPosInPixel0) / float2(Ctx.Config.Resolution), Depth);
const float3 Pos1 = float3((InPosInPixel1) / float2(Ctx.Config.Resolution), Depth);
const float3 Pos2 = float3((InPosInPixel2) / float2(Ctx.Config.Resolution), Depth);
const float3 Pos3 = float3((InPosInPixel3) / float2(Ctx.Config.Resolution), Depth);
uint Offset = 0;
if (AllocateTriangleElement(Ctx, 2, Offset))
{
FTriangleElement Element;
Element.bIsScreenSpace = true;
Element.Color = Color;
Element.Pos0 = Pos0;
Element.Pos1 = Pos1;
Element.Pos2 = Pos2;
AddTriangleElement(Ctx, Element, Offset + 0);
Element.Pos0 = Pos0;
Element.Pos1 = Pos2;
Element.Pos2 = Pos3;
AddTriangleElement(Ctx, Element, Offset + 1);
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Line-based Primitives (Line, AABB, OBB, Referentials, ...)
struct FLineElement
{
float3 Pos0;
float3 Pos1;
float4 Color0;
float4 Color1;
bool bIsScreenSpace;
};
struct FPackedLineElement
{
uint4 Packed0;
uint4 Packed1;
};
FLineElement UnpackLineElement(FPackedLineElement In)
{
FLineElement Out = (FLineElement)0;
{
Out.Pos0.x = asfloat(In.Packed0.x);
Out.Pos0.y = asfloat(In.Packed0.y);
Out.Pos0.z = asfloat(In.Packed0.z);
Out.Color0 = float4((In.Packed0.w >> 24) & 0xFF, (In.Packed0.w >> 16) & 0xFF, (In.Packed0.w >> 8) & 0xFF, (In.Packed0.w) & 0xFF) / 255.0f;
}
{
Out.Pos1.x = asfloat(In.Packed1.x);
Out.Pos1.y = asfloat(In.Packed1.y);
Out.Pos1.z = asfloat(In.Packed1.z);
Out.Color1 = float4((In.Packed1.w >> 24) & 0xFF, (In.Packed1.w >> 16) & 0xFF, (In.Packed1.w >> 8) & 0xFF, (In.Packed1.w) & 0xFF) / 255.0f;
// The 'space' info is stored into the alpha's LSB
Out.bIsScreenSpace = (In.Packed1.w & 0x1) > 0u;
}
return Out;
}
FPackedLineElement PackLineElement(FLineElement In)
{
uint4 PackedC0 = uint4(255.0f * saturate(In.Color0));
uint4 PackedC1 = uint4(255.0f * saturate(In.Color1));
// Store 'space' info into the alpha's LSB
PackedC1 = PackedC1 & 0xFE;
PackedC1 = PackedC1 | (In.bIsScreenSpace ? 0x1 : 0x0);
FPackedLineElement Out = (FPackedLineElement)0;
Out.Packed0.x = asuint(In.Pos0.x);
Out.Packed0.y = asuint(In.Pos0.y);
Out.Packed0.z = asuint(In.Pos0.z);
Out.Packed0.w = (PackedC0.x << 24) | (PackedC0.y << 16) | (PackedC0.z << 8) | (PackedC0.w);
Out.Packed1.x = asuint(In.Pos1.x);
Out.Packed1.y = asuint(In.Pos1.y);
Out.Packed1.z = asuint(In.Pos1.z);
Out.Packed1.w = (PackedC1.x << 24) | (PackedC1.y << 16) | (PackedC1.z << 8) | (PackedC1.w);
return Out;
}
FLineElement UnpackLineElement(Buffer<uint> InPrimitiveBuffer, uint InIndex, uint MaxCharacterCount)
{
const uint Index8 = GetPrimitiveLineOffset(InIndex, MaxCharacterCount);
FPackedLineElement Out = (FPackedLineElement)0;
Out.Packed0.x = InPrimitiveBuffer[Index8 + 0];
Out.Packed0.y = InPrimitiveBuffer[Index8 + 1];
Out.Packed0.z = InPrimitiveBuffer[Index8 + 2];
Out.Packed0.w = InPrimitiveBuffer[Index8 + 3];
Out.Packed1.x = InPrimitiveBuffer[Index8 + 4];
Out.Packed1.y = InPrimitiveBuffer[Index8 + 5];
Out.Packed1.z = InPrimitiveBuffer[Index8 + 6];
Out.Packed1.w = InPrimitiveBuffer[Index8 + 7];
return UnpackLineElement(Out);
}
bool AllocateLineElement(FShaderPrintContext Ctx, uint Count, inout uint OutIndex)
{
OutIndex = 0;
if (Ctx.Config.MaxLineCount == 0)
{
return false;
}
SHADER_PRINT_INTERLOCKEDADD(SHADER_PRINT_RWENTRYBUFFER(Ctx,SHADER_PRINT_COUNTER_OFFSET_LINE), Count, OutIndex);
return (OutIndex + Count) < Ctx.Config.MaxLineCount;
}
void AddLineElement(FShaderPrintContext Ctx, FLineElement In, uint Index)
{
const uint Index8 = GetPrimitiveLineOffset(Index, Ctx.Config.MaxCharacterCount);
FPackedLineElement Out = PackLineElement(In);
SHADER_PRINT_RWENTRYBUFFER(Ctx,Index8 + 0) = Out.Packed0.x;
SHADER_PRINT_RWENTRYBUFFER(Ctx,Index8 + 1) = Out.Packed0.y;
SHADER_PRINT_RWENTRYBUFFER(Ctx,Index8 + 2) = Out.Packed0.z;
SHADER_PRINT_RWENTRYBUFFER(Ctx,Index8 + 3) = Out.Packed0.w;
SHADER_PRINT_RWENTRYBUFFER(Ctx,Index8 + 4) = Out.Packed1.x;
SHADER_PRINT_RWENTRYBUFFER(Ctx,Index8 + 5) = Out.Packed1.y;
SHADER_PRINT_RWENTRYBUFFER(Ctx,Index8 + 6) = Out.Packed1.z;
SHADER_PRINT_RWENTRYBUFFER(Ctx,Index8 + 7) = Out.Packed1.w;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Line
void AddLineTWS(FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float4 Color0, float4 Color1)
{
if (Ctx.bIsActive)
{
uint Offset = 0;
if (AllocateLineElement(Ctx, 1, Offset))
{
FLineElement Element;
Element.Pos0 = Pos0;
Element.Pos1 = Pos1;
Element.Color0 = Color0;
Element.Color1 = Color1;
Element.bIsScreenSpace = false;
AddLineElement(Ctx, Element, Offset);
}
}
}
void AddLineTWS(FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float4 Color) { AddLineTWS(Ctx, Pos0, Pos1, Color, Color); }
void AddLineWS (FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float4 Color) { AddLineTWS(Ctx, Pos0+Ctx.Config.TranslatedWorldOffset, Pos1+Ctx.Config.TranslatedWorldOffset, Color, Color); }
void AddLineWS (FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float4 Color0, float4 Color1) { AddLineTWS(Ctx, Pos0+Ctx.Config.TranslatedWorldOffset, Pos1+Ctx.Config.TranslatedWorldOffset, Color0, Color1); }
void AddLine (FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float4 Color) { AddLineTWS(Ctx, Pos0+Ctx.Config.TranslatedWorldOffset, Pos1+Ctx.Config.TranslatedWorldOffset, Color, Color); }
void AddLine (FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float4 Color0, float4 Color1) { AddLineTWS(Ctx, Pos0+Ctx.Config.TranslatedWorldOffset, Pos1+Ctx.Config.TranslatedWorldOffset, Color0, Color1); }
// Draw 2D line. Input positions are expressed in pixel coordinate
void AddLineSS(FShaderPrintContext Ctx, float2 InPosInPixel0, float2 InPosInPixel1, float4 Color0, float4 Color1)
{
if (Ctx.bIsActive)
{
const float2 Pos0 = float2(InPosInPixel0) / float2(Ctx.Config.Resolution);
const float2 Pos1 = float2(InPosInPixel1) / float2(Ctx.Config.Resolution);
uint Offset = 0;
if (AllocateLineElement(Ctx, 1, Offset))
{
FLineElement Element;
Element.Pos0 = float3(Pos0, 0);
Element.Pos1 = float3(Pos1, 0);
Element.Color0 = Color0;
Element.Color1 = Color1;
Element.bIsScreenSpace = true;
AddLineElement(Ctx, Element, Offset);
}
}
}
void AddLineSS(FShaderPrintContext Ctx, float2 Pos0, float2 Pos1, float4 Color) { AddLineSS(Ctx, Pos0, Pos1, Color, Color); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Quad
void AddQuadTWS(FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float3 Pos2, float3 Pos3, float4 Color)
{
if (Ctx.bIsActive)
{
uint Offset = 0;
if (AllocateLineElement(Ctx, 4, Offset))
{
FLineElement Element;
Element.bIsScreenSpace = false;
Element.Color0 = Element.Color1 = Color;
Element.Pos0 = Pos0; Element.Pos1 = Pos1; AddLineElement(Ctx, Element, Offset + 0);
Element.Pos0 = Pos1; Element.Pos1 = Pos2; AddLineElement(Ctx, Element, Offset + 1);
Element.Pos0 = Pos2; Element.Pos1 = Pos3; AddLineElement(Ctx, Element, Offset + 2);
Element.Pos0 = Pos3; Element.Pos1 = Pos0; AddLineElement(Ctx, Element, Offset + 3);
}
}
}
void AddQuadWS (FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float3 Pos2, float3 Pos3, float4 Color) { AddQuadTWS(Ctx, Pos0+Ctx.Config.TranslatedWorldOffset, Pos1+Ctx.Config.TranslatedWorldOffset, Pos2+Ctx.Config.TranslatedWorldOffset, Pos3+Ctx.Config.TranslatedWorldOffset, Color); }
void AddQuad (FShaderPrintContext Ctx, float3 Pos0, float3 Pos1, float3 Pos2, float3 Pos3, float4 Color) { AddQuadTWS(Ctx, Pos0+Ctx.Config.TranslatedWorldOffset, Pos1+Ctx.Config.TranslatedWorldOffset, Pos2+Ctx.Config.TranslatedWorldOffset, Pos3+Ctx.Config.TranslatedWorldOffset, Color); }
// Draw 2D quad line
void AddQuadSS(FShaderPrintContext Ctx, float2 MinPos, float2 MaxPos, float4 Color)
{
if (Ctx.bIsActive)
{
uint Offset = 0;
if (AllocateLineElement(Ctx, 4, Offset))
{
MinPos = float2(MinPos) / float2(Ctx.Config.Resolution);
MaxPos = float2(MaxPos) / float2(Ctx.Config.Resolution);
float3 Pos0 = float3(MinPos.x, MinPos.y, 0);
float3 Pos1 = float3(MaxPos.x, MinPos.y, 0);
float3 Pos2 = float3(MaxPos.x, MaxPos.y, 0);
float3 Pos3 = float3(MinPos.x, MaxPos.y, 0);
FLineElement Element;
Element.bIsScreenSpace = true;
Element.Color0 = Element.Color1 = Color;
Element.Pos0 = Pos0; Element.Pos1 = Pos1; AddLineElement(Ctx, Element, Offset + 0);
Element.Pos0 = Pos1; Element.Pos1 = Pos2; AddLineElement(Ctx, Element, Offset + 1);
Element.Pos0 = Pos2; Element.Pos1 = Pos3; AddLineElement(Ctx, Element, Offset + 2);
Element.Pos0 = Pos3; Element.Pos1 = Pos0; AddLineElement(Ctx, Element, Offset + 3);
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// AABB
void AddAABBTWS(FShaderPrintContext Ctx, float3 Min, float3 Max, float4 Color)
{
if (Ctx.bIsActive)
{
uint Offset = 0;
if (AllocateLineElement(Ctx, 12, Offset))
{
float3 P0 = float3(Min.x, Min.y, Min.z);
float3 P1 = float3(Max.x, Min.y, Min.z);
float3 P2 = float3(Max.x, Max.y, Min.z);
float3 P3 = float3(Min.x, Max.y, Min.z);
float3 P4 = float3(Min.x, Min.y, Max.z);
float3 P5 = float3(Max.x, Min.y, Max.z);
float3 P6 = float3(Max.x, Max.y, Max.z);
float3 P7 = float3(Min.x, Max.y, Max.z);
FLineElement Element;
Element.bIsScreenSpace = false;
Element.Color0 = Element.Color1 = Color;
Element.Pos0 = P0; Element.Pos1 = P1; AddLineElement(Ctx, Element, Offset + 0);
Element.Pos0 = P1; Element.Pos1 = P2; AddLineElement(Ctx, Element, Offset + 1);
Element.Pos0 = P2; Element.Pos1 = P3; AddLineElement(Ctx, Element, Offset + 2);
Element.Pos0 = P3; Element.Pos1 = P0; AddLineElement(Ctx, Element, Offset + 3);
Element.Pos0 = P4; Element.Pos1 = P5; AddLineElement(Ctx, Element, Offset + 4);
Element.Pos0 = P5; Element.Pos1 = P6; AddLineElement(Ctx, Element, Offset + 5);
Element.Pos0 = P6; Element.Pos1 = P7; AddLineElement(Ctx, Element, Offset + 6);
Element.Pos0 = P7; Element.Pos1 = P4; AddLineElement(Ctx, Element, Offset + 7);
Element.Pos0 = P0; Element.Pos1 = P4; AddLineElement(Ctx, Element, Offset + 8);
Element.Pos0 = P1; Element.Pos1 = P5; AddLineElement(Ctx, Element, Offset + 9);
Element.Pos0 = P2; Element.Pos1 = P6; AddLineElement(Ctx, Element, Offset +10);
Element.Pos0 = P3; Element.Pos1 = P7; AddLineElement(Ctx, Element, Offset +11);
}
}
}
void AddAABBWS (FShaderPrintContext Ctx, float3 Min, float3 Max, float4 Color) { AddAABBTWS(Ctx, Min + Ctx.Config.TranslatedWorldOffset, Max + Ctx.Config.TranslatedWorldOffset, Color); }
void AddAABB (FShaderPrintContext Ctx, float3 Min, float3 Max, float4 Color) { AddAABBTWS(Ctx, Min + Ctx.Config.TranslatedWorldOffset, Max + Ctx.Config.TranslatedWorldOffset, Color); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Cross
void AddCrossTWS(FShaderPrintContext Ctx, float3 Pos, float Size, float4 Color)
{
if (Ctx.bIsActive)
{
AddLineTWS(Ctx, Pos - float3(Size,0,0), Pos + float3(Size,0,0), Color, Color);
AddLineTWS(Ctx, Pos - float3(0,Size,0), Pos + float3(0,Size,0), Color, Color);
AddLineTWS(Ctx, Pos - float3(0,0,Size), Pos + float3(0,0,Size), Color, Color);
}
}
void AddCrossWS (FShaderPrintContext Ctx, float3 Pos, float Size, float4 Color) { AddCrossTWS(Ctx, Pos + Ctx.Config.TranslatedWorldOffset, Size, Color); }
void AddCross (FShaderPrintContext Ctx, float3 Pos, float Size, float4 Color) { AddCrossTWS(Ctx, Pos + Ctx.Config.TranslatedWorldOffset, Size, Color); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Circles
void AddCircleSS(FShaderPrintContext Ctx, float2 Center, float Radius, float4 Color, uint SegmentCount = 16)
{
if (Ctx.bIsActive)
{
const float AngleStep = 2.0f * PI * rcp(float(SegmentCount-1));
float Angle = 0.0f;
float2 PrevP = Center + float2(Radius, 0.0f);
for (uint SegIt = 0; SegIt < SegmentCount; ++SegIt)
{
Angle += AngleStep;
float S, C;
sincos(Angle, S, C);
float2 P = Center + float2(C, S) * Radius;
AddLineSS(Ctx, PrevP, P, Color, Color);
PrevP = P;
}
}
}
void AddCircleTWS(FShaderPrintContext Ctx, float3 Center, float3 Normal, float Radius, float4 Color, uint SegmentCount = 16)
{
if (Ctx.bIsActive)
{
// Calculate two perpendicular unit vectors to the normal
const float3 RefUp = abs(Normal.z) < 0.999f ? float3(0, 0, 1) : float3(1, 0, 0);
float3 X = normalize(cross(RefUp, Normal));
float3 Y = cross(Normal, X);
X *= Radius;
Y *= Radius;
const float AngleStep = 2.0f * PI * rcp(float(SegmentCount-1));
float Angle = 0.0f;
float3 PrevP = Center + X;
for (uint SegIt = 0; SegIt < SegmentCount; ++SegIt)
{
Angle += AngleStep;
float S, C;
sincos(Angle, S, C);
float3 P = Center + X * C + Y * S;
AddLineTWS(Ctx, PrevP, P, Color, Color);
PrevP = P;
}
}
}
void AddCircleWS (FShaderPrintContext Ctx, float3 Center, float3 Normal, float Radius, float4 Color, uint SegmentCount = 16) { AddCircleTWS(Ctx, Center + Ctx.Config.TranslatedWorldOffset, Normal, Radius, Color, SegmentCount); }
void AddCircle (FShaderPrintContext Ctx, float3 Center, float3 Normal, float Radius, float4 Color, uint SegmentCount = 16) { AddCircleTWS(Ctx, Center + Ctx.Config.TranslatedWorldOffset, Normal, Radius, Color, SegmentCount); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Spheres
void AddSphereTWS(FShaderPrintContext Ctx, float3 Center, float Radius, float4 Color, uint SegmentCount = 16)
{
AddCircleTWS(Ctx, Center, float3(1, 0, 0), Radius, Color, SegmentCount);
AddCircleTWS(Ctx, Center, float3(0, 1, 0), Radius, Color, SegmentCount);
AddCircleTWS(Ctx, Center, float3(0, 0, 1), Radius, Color, SegmentCount);
}
void AddSphereWS (FShaderPrintContext Ctx, float3 Center, float Radius, float4 Color, uint SegmentCount = 16) { AddSphereTWS(Ctx, Center + Ctx.Config.TranslatedWorldOffset, Radius, Color, SegmentCount); }
void AddSphere (FShaderPrintContext Ctx, float3 Center, float Radius, float4 Color, uint SegmentCount = 16) { AddSphereTWS(Ctx, Center + Ctx.Config.TranslatedWorldOffset, Radius, Color, SegmentCount); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Referential
void AddReferentialTWS(FShaderPrintContext Ctx, float3 Pos, float3 T, float3 B, float3 N, float Scale = 1)
{
if (Ctx.bIsActive)
{
AddLineTWS(Ctx, Pos, Pos + normalize(T)*Scale, ColorRed, ColorRed);
AddLineTWS(Ctx, Pos, Pos + normalize(B)*Scale, ColorGreen, ColorGreen);
AddLineTWS(Ctx, Pos, Pos + normalize(N)*Scale, ColorBlue, ColorBlue);
}
}
void AddReferentialTWS(FShaderPrintContext Ctx, float3 Pos, float3x3 InM, float Scale = 1)
{
AddReferentialTWS(Ctx, Pos, InM[0].xyz, InM[1].xyz, InM[2].xyz, Scale);
}
void AddReferentialTWS(FShaderPrintContext Ctx, float4x4 InM, float Scale = 1)
{
AddReferentialTWS(Ctx, InM[3].xyz, InM[0].xyz, InM[1].xyz, InM[2].xyz, Scale);
}
void AddReferentialTWS(FShaderPrintContext Ctx, float3 Pos, float3 TangentZ, float Scale = 1)
{
if (Ctx.bIsActive)
{
const float Sign = TangentZ.z >= 0 ? 1 : -1;
const float a = -rcp(Sign + TangentZ.z);
const float b = TangentZ.x * TangentZ.y * a;
const float3 TangentX = { 1 + Sign * a * Pow2(TangentZ.x), Sign * b, -Sign * TangentZ.x };
const float3 TangentY = { b, Sign + a * Pow2(TangentZ.y), -TangentZ.y };
AddReferentialTWS(Ctx, Pos, TangentX, TangentY, TangentZ, Scale);
}
}
void AddReferentialWS (FShaderPrintContext Ctx, float4x4 InM, float Scale = 1) { AddReferentialTWS(Ctx, InM[3].xyz + Ctx.Config.TranslatedWorldOffset, InM[0].xyz, InM[1].xyz, InM[2].xyz, Scale); }
void AddReferentialWS (FShaderPrintContext Ctx, float3 Pos, float3x3 InM, float Scale = 1) { AddReferentialTWS(Ctx, Pos + Ctx.Config.TranslatedWorldOffset, InM[0].xyz, InM[1].xyz, InM[2].xyz, Scale); }
void AddReferentialWS (FShaderPrintContext Ctx, float3 Pos, float3 TangentZ, float Scale = 1) { AddReferentialTWS(Ctx, Pos + Ctx.Config.TranslatedWorldOffset, TangentZ, Scale); }
void AddReferentialWS (FShaderPrintContext Ctx, float3 Pos, float3 T, float3 B, float3 N, float Scale = 1) { AddReferentialTWS(Ctx, Pos + Ctx.Config.TranslatedWorldOffset, T, B, N, Scale); }
void AddReferential (FShaderPrintContext Ctx, float4x4 InM, float Scale = 1) { AddReferentialTWS(Ctx, InM[3].xyz + Ctx.Config.TranslatedWorldOffset, InM[0].xyz, InM[1].xyz, InM[2].xyz, Scale); }
void AddReferential (FShaderPrintContext Ctx, float3 Pos, float3x3 InM, float Scale = 1) { AddReferentialTWS(Ctx, Pos + Ctx.Config.TranslatedWorldOffset, InM[0].xyz, InM[1].xyz, InM[2].xyz, Scale); }
void AddReferential (FShaderPrintContext Ctx, float3 Pos, float3 TangentZ, float Scale = 1) { AddReferentialTWS(Ctx, Pos + Ctx.Config.TranslatedWorldOffset, TangentZ, Scale); }
void AddReferential (FShaderPrintContext Ctx, float3 Pos, float3 T, float3 B, float3 N, float Scale = 1) { AddReferentialTWS(Ctx, Pos + Ctx.Config.TranslatedWorldOffset, T, B, N, Scale); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Triangle
void AddLineTriangleTWS(FShaderPrintContext Ctx, float3 P0, float3 P1, float3 P2, float4 Color)
{
if (Ctx.bIsActive)
{
AddLineTWS(Ctx, P0, P1, Color, Color);
AddLineTWS(Ctx, P1, P2, Color, Color);
AddLineTWS(Ctx, P2, P0, Color, Color);
}
}
void AddLineTriangleWS (FShaderPrintContext Ctx, float3 P0, float3 P1, float3 P2, float4 Color) { AddLineTriangleTWS(Ctx, P0 + Ctx.Config.TranslatedWorldOffset, P1 + Ctx.Config.TranslatedWorldOffset, P2 + Ctx.Config.TranslatedWorldOffset, Color); }
void AddLineTriangle (FShaderPrintContext Ctx, float3 P0, float3 P1, float3 P2, float4 Color) { AddLineTriangleTWS(Ctx, P0 + Ctx.Config.TranslatedWorldOffset, P1 + Ctx.Config.TranslatedWorldOffset, P2 + Ctx.Config.TranslatedWorldOffset, Color); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// OBB
void AddOBBTWS(FShaderPrintContext Ctx, float3 Min, float3 Max, float4 Color, float4x4 Transform, bool bHomogeneousDivide = false)
{
if (Ctx.bIsActive)
{
uint Offset = 0;
if (AllocateLineElement(Ctx, 12, Offset))
{
float4 P0 = mul(float4(Min.x, Min.y, Min.z, 1.0f), Transform);
float4 P1 = mul(float4(Max.x, Min.y, Min.z, 1.0f), Transform);
float4 P2 = mul(float4(Max.x, Max.y, Min.z, 1.0f), Transform);
float4 P3 = mul(float4(Min.x, Max.y, Min.z, 1.0f), Transform);
float4 P4 = mul(float4(Min.x, Min.y, Max.z, 1.0f), Transform);
float4 P5 = mul(float4(Max.x, Min.y, Max.z, 1.0f), Transform);
float4 P6 = mul(float4(Max.x, Max.y, Max.z, 1.0f), Transform);
float4 P7 = mul(float4(Min.x, Max.y, Max.z, 1.0f), Transform);
if (bHomogeneousDivide)
{
P0.xyz /= P0.w;
P1.xyz /= P1.w;
P2.xyz /= P2.w;
P3.xyz /= P3.w;
P4.xyz /= P4.w;
P5.xyz /= P5.w;
P6.xyz /= P6.w;
P7.xyz /= P7.w;
}
FLineElement Element;
Element.bIsScreenSpace = false;
Element.Color0 = Element.Color1 = Color;
Element.Pos0 = P0.xyz; Element.Pos1 = P1.xyz; AddLineElement(Ctx, Element, Offset + 0);
Element.Pos0 = P1.xyz; Element.Pos1 = P2.xyz; AddLineElement(Ctx, Element, Offset + 1);
Element.Pos0 = P2.xyz; Element.Pos1 = P3.xyz; AddLineElement(Ctx, Element, Offset + 2);
Element.Pos0 = P3.xyz; Element.Pos1 = P0.xyz; AddLineElement(Ctx, Element, Offset + 3);
Element.Pos0 = P4.xyz; Element.Pos1 = P5.xyz; AddLineElement(Ctx, Element, Offset + 4);
Element.Pos0 = P5.xyz; Element.Pos1 = P6.xyz; AddLineElement(Ctx, Element, Offset + 5);
Element.Pos0 = P6.xyz; Element.Pos1 = P7.xyz; AddLineElement(Ctx, Element, Offset + 6);
Element.Pos0 = P7.xyz; Element.Pos1 = P4.xyz; AddLineElement(Ctx, Element, Offset + 7);
Element.Pos0 = P0.xyz; Element.Pos1 = P4.xyz; AddLineElement(Ctx, Element, Offset + 8);
Element.Pos0 = P1.xyz; Element.Pos1 = P5.xyz; AddLineElement(Ctx, Element, Offset + 9);
Element.Pos0 = P2.xyz; Element.Pos1 = P6.xyz; AddLineElement(Ctx, Element, Offset + 10);
Element.Pos0 = P3.xyz; Element.Pos1 = P7.xyz; AddLineElement(Ctx, Element, Offset + 11);
}
}
}
void AddOBBWS (FShaderPrintContext Ctx, float3 Min, float3 Max, float4 Color, float4x4 Transform, bool bHomogeneousDivide = false) { Transform[3].xyz += Ctx.Config.TranslatedWorldOffset; AddOBBTWS(Ctx, Min, Max, Color, Transform, bHomogeneousDivide); }
void AddOBB (FShaderPrintContext Ctx, float3 Min, float3 Max, float4 Color, float4x4 Transform, bool bHomogeneousDivide = false) { Transform[3].xyz += Ctx.Config.TranslatedWorldOffset; AddOBBTWS(Ctx, Min, Max, Color, Transform, bHomogeneousDivide); }
///////////////////////////////////////////////////////////////////////////////////////////////////
// Strings
struct FStringInfo
{
uint EntryID;
uint Length;
uint Offset;
uint Pad;
};
FStringInfo UnpackStringInfo(uint2 In)
{
FStringInfo Out = (FStringInfo)0;
Out.EntryID = In.x;
Out.Offset = (In.y) & 0xFFFF;
Out.Length = (In.y >> 16) & 0xFF;
return Out;
}
FStringInfo FindStringInfo(uint InEntryID, uint InInfoCount, StructuredBuffer<uint2> InInfoBuffer)
{
FStringInfo Out = (FStringInfo)0;
Out.EntryID = ~0;
for (uint It = 0; It < InInfoCount; ++It)
{
if (InInfoBuffer[It].x == InEntryID)
{
return UnpackStringInfo(InInfoBuffer[It]);
}
}
return Out;
}
void PrintString(inout FShaderPrintContext Ctx, uint InEntryID, FFontColor InColor, uint InInfoCount, uint InCharCount, StructuredBuffer<uint2> InInfoBuffer, Buffer<uint> InCharBuffer)
{
const FStringInfo Info = FindStringInfo(InEntryID, InInfoCount, InInfoBuffer);
if (Info.EntryID != ~0 && (Info.Length + Info.Offset) <= InCharCount)
{
for (uint It = 0; It < Info.Length; ++It)
{
const uint Char = InCharBuffer[It + Info.Offset];
PrintSymbol(Ctx, Char, InColor);
}
}
}
uint LenString(uint InEntryID, uint InInfoCount, StructuredBuffer<uint2> InInfoBuffer)
{
const FStringInfo Info = FindStringInfo(InEntryID, InInfoCount, InInfoBuffer);
return Info.EntryID != ~0 ? Info.Length : 0;
}
#define FSTRINGS(N) \
uint N##_InfoCount;\
uint N##_CharCount;\
StructuredBuffer<uint2> N##_InfoBuffer;\
Buffer<uint> N##_CharBuffer;\
void Print##N(inout FShaderPrintContext Ctx, uint InEntryID, FFontColor InColor)\
{\
PrintString(Ctx, InEntryID, InColor, N##_InfoCount, N##_CharCount, N##_InfoBuffer, N##_CharBuffer);\
}\
uint Len##N(uint InEntryID)\
{\
return LenString(InEntryID, N##_InfoCount, N##_InfoBuffer);\
}\
uint Num##N()\
{\
return N##_InfoCount;\
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Current pixel
// Store the current pixel coordinates, which allow to retrieve it in any shaders once set.
// This data is only visible for the current thread/pixel, not available for neighbors
static uint2 ShaderPrintCurrentPixelCoord;
// Set current pixel coordinates
void SetShaderPrintCurrentPixelCoord(uint2 In)
{
ShaderPrintCurrentPixelCoord = In;
}
// Get current pixel coordinates
uint2 GetShaderPrintCurrentPixelCoord()
{
return ShaderPrintCurrentPixelCoord;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Helper functions
// Print a value onto a single line
#define PrintLineN(Ctx, Value) Print(Ctx, TEXT(#Value), FontWhite); Print(Ctx, TEXT(" : "), FontWhite); Print(Ctx, Value, FontYellow); Newline(Ctx);
#define PrintLine(Ctx, Value) Print(Ctx, TEXT(#Value), FontWhite); Print(Ctx, TEXT(" : "), FontWhite); Print(Ctx, Value, FontYellow);