r.SkinCache.RecomputeTangentsParallelDispatch
r.SkinCache.RecomputeTangentsParallelDispatch
#Overview
name: r.SkinCache.RecomputeTangentsParallelDispatch
This variable is created as a Console Variable (cvar).
- type:
Var
- help:
This option enables parallel dispatches for recompute tangents.\n 0: off (default), triangle pass is interleaved with vertex pass, requires resource barriers in between. \n 1: on, batch triangle passes together, resource barrier, followed by vertex passes together, cost more memory. \n
It is referenced in 12
C++ source files.
#Summary
#Usage in the C++ source code
The purpose of r.SkinCache.RecomputeTangentsParallelDispatch is to control the parallel dispatching of recompute tangents operations in the GPU Skin Cache system of Unreal Engine 5.
This setting variable is primarily used in the GPU Skin Cache subsystem, which is part of the rendering system in Unreal Engine. It affects how the engine computes tangents for skinned meshes on the GPU.
The value of this variable is set through the console variable system in Unreal Engine. It’s defined as an integer with two possible values:
- 0: Off (default) - Triangle pass is interleaved with vertex pass, requiring resource barriers in between.
- 1: On - Batch triangle passes together, followed by a resource barrier, then vertex passes together. This costs more memory but potentially improves performance.
The associated variable GRecomputeTangentsParallelDispatch directly interacts with r.SkinCache.RecomputeTangentsParallelDispatch. They share the same value and are used interchangeably in the code.
Developers must be aware that enabling this option (setting it to 1) will change the order of operations in the skin cache system and may increase memory usage. It could potentially improve performance but at the cost of higher memory consumption.
Best practices when using this variable include:
- Test performance with both settings (0 and 1) to determine which works best for your specific use case.
- Monitor memory usage when enabling this option, especially on memory-constrained platforms.
- Consider the trade-off between potential performance gains and increased memory usage.
Regarding the associated variable GRecomputeTangentsParallelDispatch:
- It’s used throughout the GPU Skin Cache implementation to control the flow of tangent recomputation.
- It affects how intermediate buffers are allocated and used, particularly in the DispatchUpdateSkinTangents function.
- When enabled, it changes how resource transitions are handled between triangle and vertex passes.
- Developers should be aware that changing this variable will have a direct impact on the GPU Skin Cache’s behavior and performance characteristics.
#References in C++ code
#Callsites
This variable is referenced in the following C++ source code:
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:162
Scope: file
Source code excerpt:
int32 GRecomputeTangentsParallelDispatch = 0;
FAutoConsoleVariableRef CVarRecomputeTangentsParallelDispatch(
TEXT("r.SkinCache.RecomputeTangentsParallelDispatch"),
GRecomputeTangentsParallelDispatch,
TEXT("This option enables parallel dispatches for recompute tangents.\n")
TEXT(" 0: off (default), triangle pass is interleaved with vertex pass, requires resource barriers in between. \n")
TEXT(" 1: on, batch triangle passes together, resource barrier, followed by vertex passes together, cost more memory. \n"),
ECVF_RenderThreadSafe
);
#Associated Variable and Callsites
This variable is associated with another variable named GRecomputeTangentsParallelDispatch
. They share the same value. See the following C++ source code.
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:160
Scope: file
Source code excerpt:
);
int32 GRecomputeTangentsParallelDispatch = 0;
FAutoConsoleVariableRef CVarRecomputeTangentsParallelDispatch(
TEXT("r.SkinCache.RecomputeTangentsParallelDispatch"),
GRecomputeTangentsParallelDispatch,
TEXT("This option enables parallel dispatches for recompute tangents.\n")
TEXT(" 0: off (default), triangle pass is interleaved with vertex pass, requires resource barriers in between. \n")
TEXT(" 1: on, batch triangle passes together, resource barrier, followed by vertex passes together, cost more memory. \n"),
ECVF_RenderThreadSafe
);
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:928
Scope (from outer to inner):
file
class class FBaseRecomputeTangentsPerTriangleShader : public FGlobalShader
function void SetParameters
Source code excerpt:
// UAV
SetUAVParameter(BatchedParameters, IntermediateAccumBufferUAV, StagingBuffer.UAV);
SetShaderValue(BatchedParameters, IntermediateAccumBufferOffset, GRecomputeTangentsParallelDispatch * DispatchData.IntermediateAccumulatedTangentBufferOffset);
if (!GAllowDupedVertsForRecomputeTangents)
{
SetSRVParameter(BatchedParameters, DuplicatedIndices, DispatchData.DuplicatedIndices);
SetSRVParameter(BatchedParameters, DuplicatedIndicesIndices, DispatchData.DuplicatedIndicesIndices);
}
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:1044
Scope (from outer to inner):
file
class class FBaseRecomputeTangentsPerVertexShader : public FGlobalShader
function void SetParameters
Source code excerpt:
// UAVs
SetUAVParameter(BatchedParameters, IntermediateAccumBufferUAV, StagingBuffer.UAV);
SetShaderValue(BatchedParameters, IntermediateAccumBufferOffset, GRecomputeTangentsParallelDispatch * DispatchData.IntermediateAccumulatedTangentBufferOffset);
SetUAVParameter(BatchedParameters, TangentBufferUAV, DispatchData.GetTangentRWBuffer()->Buffer.UAV);
SetSRVParameter(BatchedParameters, TangentInputBuffer, DispatchData.IntermediateTangentBuffer ? DispatchData.IntermediateTangentBuffer->Buffer.SRV : nullptr);
SetSRVParameter(BatchedParameters, ColorInputBuffer, DispatchData.ColorBufferSRV);
}
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:1098
Scope (from outer to inner):
file
function void FGPUSkinCache::DispatchUpdateSkinTangents
Source code excerpt:
if (bTrianglePass)
{
if (!GRecomputeTangentsParallelDispatch)
{
if (StagingBuffers.Num() != GNumTangentIntermediateBuffers)
{
// Release extra buffers if shrinking
for (int32 Index = GNumTangentIntermediateBuffers; Index < StagingBuffers.Num(); ++Index)
{
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:1167
Scope (from outer to inner):
file
function void FGPUSkinCache::DispatchUpdateSkinTangents
Source code excerpt:
}
if (!GRecomputeTangentsParallelDispatch)
{
// When triangle & vertex passes are interleaved, resource transition is needed in between.
RHICmdList.Transition({
DispatchData.GetActiveTangentRWBuffer()->UpdateAccessState(ERHIAccess::SRVCompute),
StagingBuffer->UpdateAccessState(ERHIAccess::UAVCompute)
});
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:1178
Scope (from outer to inner):
file
function void FGPUSkinCache::DispatchUpdateSkinTangents
Source code excerpt:
INC_DWORD_STAT_BY(STAT_GPUSkinCache_NumTrianglesForRecomputeTangents, NumTriangles);
const FRWBuffer& ShaderStagingBuffer = GRecomputeTangentsParallelDispatch ? DispatchData.GetIntermediateAccumulatedTangentBuffer()->Buffer : StagingBuffer->Buffer;
FRHIComputeShader* ShaderRHI = Shader.GetComputeShader();
SetComputePipelineState(RHICmdList, Shader.GetComputeShader());
SetShaderParametersLegacyCS(RHICmdList, Shader, Entry, DispatchData, ShaderStagingBuffer);
DispatchComputeShader(RHICmdList, Shader.GetShader(), ThreadGroupCountValue, 1, 1);
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:1207
Scope (from outer to inner):
file
function void FGPUSkinCache::DispatchUpdateSkinTangents
Source code excerpt:
uint32 ThreadGroupCountValue = FMath::DivideAndRoundUp(VertexCount, ComputeShader->ThreadGroupSizeX);
if (!GRecomputeTangentsParallelDispatch)
{
// When triangle & vertex passes are interleaved, resource transition is needed in between.
RHICmdList.Transition({
DispatchData.GetTangentRWBuffer()->UpdateAccessState(ERHIAccess::UAVCompute),
StagingBuffer->UpdateAccessState(ERHIAccess::UAVCompute)
});
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:1218
Scope (from outer to inner):
file
function void FGPUSkinCache::DispatchUpdateSkinTangents
Source code excerpt:
SetComputePipelineState(RHICmdList, ComputeShader.GetComputeShader());
SetShaderParametersLegacyCS(RHICmdList, ComputeShader, Entry, DispatchData, GRecomputeTangentsParallelDispatch ? DispatchData.GetIntermediateAccumulatedTangentBuffer()->Buffer : StagingBuffer->Buffer);
DispatchComputeShader(RHICmdList, ComputeShader.GetShader(), ThreadGroupCountValue, 1, 1);
UnsetShaderParametersLegacyCS(RHICmdList, ComputeShader);
IncrementDispatchCounter(RHICmdList);
}
}
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:1512
Scope (from outer to inner):
file
function void FGPUSkinCache::DoDispatch
Source code excerpt:
BuffersToSRVForRecomputeTangents.Add(DispatchData.GetPositionRWBuffer());
BuffersToSRVForRecomputeTangents.Add(DispatchData.GetActiveTangentRWBuffer());
if (GRecomputeTangentsParallelDispatch)
{
IntermediateAccumulatedTangentBuffers.Add(DispatchData.GetIntermediateAccumulatedTangentBuffer());
}
BuffersToTransitionToRead.Add(DispatchData.GetPositionRWBuffer());
}
}
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:1534
Scope (from outer to inner):
file
function void FGPUSkinCache::DoDispatch
Source code excerpt:
{
DispatchUpdateSkinTangents(RHICmdList, DispatchItem.SkinCacheEntry, DispatchItem.Section, StagingBuffer, true);
if (!GRecomputeTangentsParallelDispatch)
{
// When parallel dispatching is off, triangle pass and vertex pass are dispatched interleaved.
DispatchUpdateSkinTangents(RHICmdList, DispatchItem.SkinCacheEntry, DispatchItem.Section, StagingBuffer, false);
}
}
}
if (GRecomputeTangentsParallelDispatch)
{
// Do necessary buffer transitions before vertex pass dispatches
TArray<FSkinCacheRWBuffer*> TangentBuffers;
for (int32 i = 0; i < BatchCount; ++i)
{
FDispatchEntry& DispatchItem = BatchDispatches[i];
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/GPUSkinCache.cpp:1631
Scope (from outer to inner):
file
function void FGPUSkinCache::DoDispatch
Source code excerpt:
DispatchData.GetActiveTangentRWBuffer()->UpdateAccessState(ERHIAccess::SRVCompute)
});
if (GRecomputeTangentsParallelDispatch)
{
RHICmdList.Transition(DispatchData.GetIntermediateAccumulatedTangentBuffer()->UpdateAccessState(ERHIAccess::UAVCompute));
}
BuffersToTransitionToRead.Add(DispatchData.GetPositionRWBuffer());
FSkinCacheRWBuffer* StagingBuffer = nullptr;
DispatchUpdateSkinTangents(RHICmdList, SkinCacheEntry, Section, StagingBuffer, true);
if (GRecomputeTangentsParallelDispatch)
{
RHICmdList.Transition({
DispatchData.GetTangentRWBuffer()->UpdateAccessState(ERHIAccess::UAVCompute),
DispatchData.GetIntermediateAccumulatedTangentBuffer()->UpdateAccessState(ERHIAccess::UAVCompute)
});
}