Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autodesk: Skip use of geometry shader for patch tessellation when possible #3422

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions pxr/imaging/hdSt/codeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ HdSt_CodeGen::HdSt_CodeGen(
, _hasCS(false)
, _hasPTCS(false)
, _hasPTVS(false)
, _hasTESWithNoGS(false)
, _hasClipPlanes(false)
{
TF_VERIFY(geometricShader);
Expand All @@ -187,6 +188,7 @@ HdSt_CodeGen::HdSt_CodeGen(
, _hasCS(false)
, _hasPTCS(false)
, _hasPTVS(false)
, _hasTESWithNoGS(false)
, _hasClipPlanes(false)
{
TF_VERIFY(_metaData,
Expand Down Expand Up @@ -1902,6 +1904,7 @@ HdSt_CodeGen::Compile(HdStResourceRegistry*const registry)
_hasGS = (!geometryShader.empty()) && !metalTessellationEnabled;
_hasFS = (!fragmentShader.empty());
_hasCS = (!computeShader.empty());
_hasTESWithNoGS = _hasTES && !_hasGS;

// Initialize source buckets
_genDefines.str(""); _genDecl.str(""); _genAccessors.str("");
Expand Down Expand Up @@ -2186,13 +2189,14 @@ HdSt_CodeGen::Compile(HdStResourceRegistry*const registry)
}
}

// We plumb the evaluated position in patch from PTVS to FS since this
// We plumb the evaluated position in patch from PTVS or TES to FS since this
// is more consistent than using built-in barycentric coords and can be
// used even when builtin barycentric coords are not available. We pass
// only the first two components between stages and provide an accessor
// which can reconstruct the full three component barycentric form.
if (_hasPTVS) {
_AddInterstageElement(&_resPTVS,
if (_hasPTVS || _hasTESWithNoGS) {
auto& resTess = _hasPTVS ? _resPTVS : _resTES;
_AddInterstageElement(&resTess,
HioGlslfxResourceLayout::InOut::STAGE_OUT,
/*name=*/_tokens->hd_tessCoord,
/*dataType=*/_tokens->vec2);
Expand Down Expand Up @@ -2326,8 +2330,9 @@ HdSt_CodeGen::Compile(HdStResourceRegistry*const registry)
}
}

if (_hasPTVS) {
_procPTVSOut << " hd_tessCoord = gl_TessCoord.xy;\n";
if (_hasPTVS || _hasTESWithNoGS) {
auto& procTess = _hasPTVS ? _procPTVSOut : _procTES;
procTess << " hd_tessCoord = gl_TessCoord.xy;\n";
}

if (requiresPrimitiveIdEmulation) {
Expand Down Expand Up @@ -2375,6 +2380,9 @@ HdSt_CodeGen::Compile(HdStResourceRegistry*const registry)
}
if (_hasTES) {
_genTES << shader->GetSource(HdShaderTokens->tessEvalShader);
if (_hasTESWithNoGS) {
_genTES << shader->GetSource(HdShaderTokens->displacementShader);
}
}
if (_hasPTCS) {
_genPTCS << shader->GetSource(
Expand Down Expand Up @@ -2690,6 +2698,14 @@ HdSt_CodeGen::_CompileWithGeneratedGLSLResources(
resourceGen._GenerateGLSLResources(&desc, resDecl,
HdShaderTokens->tessEvalShader, _resTES, _GetMetaData());

// material in TES
if (_hasTESWithNoGS) {
resourceGen._GenerateGLSLResources(&desc, resDecl,
HdShaderTokens->tessEvalShader, _resMaterial, _GetMetaData());
resourceGen._GenerateGLSLTextureResources(resDecl,
HdShaderTokens->tessEvalShader, _resTextures, _GetMetaData());
}

std::string const declarations =
_genDefines.str() + _osd.str();
std::string const source =
Expand Down Expand Up @@ -2901,6 +2917,14 @@ HdSt_CodeGen::_CompileWithGeneratedHgiResources(
resourceGen._GenerateHgiResources(registry->GetHgi(), &tesDesc,
HdShaderTokens->tessEvalShader, _resTES, _GetMetaData());

// material in TES
if (_hasTESWithNoGS) {
resourceGen._GenerateHgiResources(registry->GetHgi(), &tesDesc,
HdShaderTokens->tessEvalShader, _resMaterial, _GetMetaData());
resourceGen._GenerateHgiTextureResources(&tesDesc,
HdShaderTokens->tessEvalShader, _resTextures, _GetMetaData());
}

std::string const declarations =
_genDefines.str() + _genDecl.str() + _osd.str();
std::string const source = _genAccessors.str() + _genTES.str();
Expand Down Expand Up @@ -6857,6 +6881,9 @@ HdSt_CodeGen::_GenerateShaderParameters(bool bindlessTextureEnabled)

_genGS << accessors.str();
_genFS << accessors.str();
if (_hasTESWithNoGS) {
_genTES << accessors.str();
}
_genPTCS << accessors.str();
_genPTVS << accessors.str();
}
Expand Down
1 change: 1 addition & 0 deletions pxr/imaging/hdSt/codeGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ class HdSt_CodeGen
bool _hasCS;
bool _hasPTCS;
bool _hasPTVS;
bool _hasTESWithNoGS;

bool _hasClipPlanes;
};
Expand Down
88 changes: 73 additions & 15 deletions pxr/imaging/hdSt/meshShaderKey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,10 @@ TF_DEFINE_PRIVATE_TOKENS(
((mainPatchCommonTCS, "Mesh.TessControl.PatchCommon"))
((mainBSplineQuadTCS, "Mesh.TessControl.BSplineQuad"))
((mainBezierQuadTES, "Mesh.TessEval.BezierQuad"))
((mainBezierQuadNoGSTES, "Mesh.TessEval.BezierQuadNoGS"))
((mainBoxSplineTriangleTCS, "Mesh.TessControl.BoxSplineTriangle"))
((mainBezierTriangleTES, "Mesh.TessEval.BezierTriangle"))
((mainBezierTriangleNoGSTES, "Mesh.TessEval.BezierTriangleNoGS"))
((mainVaryingInterpTES, "Mesh.TessEval.VaryingInterpolation"))
((mainTrianglePTVS, "Mesh.PostTessVertex.Triangle"))
((mainQuadPTVS, "Mesh.PostTessVertex.Quad"))
Expand Down Expand Up @@ -123,6 +125,8 @@ TF_DEFINE_PRIVATE_TOKENS(
((instancing, "Instancing.Transform"))

// terminals
((customDisplacementTES, "TessEval.CustomDisplacement"))
((noCustomDisplacementTES, "TessEval.NoCustomDisplacement"))
((customDisplacementGS, "Geometry.CustomDisplacement"))
((noCustomDisplacementGS, "Geometry.NoCustomDisplacement"))
((commonFS, "Fragment.CommonTerminals"))
Expand Down Expand Up @@ -346,28 +350,82 @@ HdSt_MeshShaderKey::HdSt_MeshShaderKey(
}

bool const ptvsStageEnabled = !PTVS[0].IsEmpty();
bool finalTesStageEnabled = false;

// tessellation control shader
if (isPrimTypePatches && !ptvsStageEnabled) {
TCS[0] = _tokens->instancing;
TCS[1] = _tokens->mainPatchCommonTCS;
TCS[2] = isPrimTypePatchesBSpline

// Tessellation control shader.
uint8_t tcsIndex = 0;
TCS[tcsIndex++] = _tokens->instancing;
TCS[tcsIndex++] = _tokens->mainPatchCommonTCS;
TCS[tcsIndex++] = isPrimTypePatchesBSpline
? _tokens->mainBSplineQuadTCS
: _tokens->mainBoxSplineTriangleTCS;
TCS[3] = TfToken();

// tessellation evaluation shader
TES[0] = _tokens->instancing;
TES[1] = isPrimTypePatchesBSpline
? _tokens->mainBezierQuadTES
: _tokens->mainBezierTriangleTES;
TES[2] = _tokens->mainVaryingInterpTES;
TES[3] = TfToken();
TCS[tcsIndex++] = TfToken();

// Tessellation evaluation shader
uint8_t tesIndex = 0;
TES[tesIndex++] = _tokens->instancing;

if (geomStyle == HdMeshGeomStyleEdgeOnSurf) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems not quite right. Minimally, this should also test hasBuiltinBarycentrics, which is the only thing that edges need from GS.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right, when the fragment shader has barycentrics, it can render edges.


// TES configuration with a geometry shader.
TES[tesIndex++] = isPrimTypePatchesBSpline ? _tokens->mainBezierQuadTES
: _tokens->mainBezierTriangleTES;
TES[tesIndex++] = _tokens->mainVaryingInterpTES;
TES[tesIndex++] = TfToken();
}
else {

// TES configuration without a geometry shader.
if (ptvsGeometricNormals) {
TES[tesIndex++] = _tokens->normalsGeometryFlat;
}
else {
TES[tesIndex++] = _tokens->normalsGeometryNoFlat;
}

// Now handle the vs style normals
if (normalsSource == NormalSourceFlat) {
TES[tesIndex++] = _tokens->normalsFlat;
}
else if (normalsSource == NormalSourceSmooth) {
TES[tesIndex++] = _tokens->normalsSmooth;
}
else if (vsSceneNormals) {
TES[tesIndex++] = _tokens->normalsScene;
}
else if (gsSceneNormals && isPrimTypePatches) {
TES[tesIndex++] = _tokens->normalsScenePatches;
}
else {
TES[tesIndex++] = _tokens->normalsPass;
}

TES[tesIndex++] = _tokens->mainVaryingInterpTES;

if (hasCustomDisplacement) {
TES[tesIndex++] = _tokens->customDisplacementTES;
}
else {
TES[tesIndex++] = _tokens->noCustomDisplacementTES;
}

TES[tesIndex++] = isPrimTypePatchesBSpline
? _tokens->mainBezierQuadNoGSTES
: _tokens->mainBezierTriangleNoGSTES;
TES[tesIndex++] = TfToken();
finalTesStageEnabled = true;
}

} else {

TCS[0] = TfToken();
TES[0] = TfToken();
}

bool const tessOnlyEnabled = finalTesStageEnabled || ptvsStageEnabled;

// geometry shader
uint8_t gsIndex = 0;
GS[gsIndex++] = _tokens->instancing;
Expand Down Expand Up @@ -398,7 +456,7 @@ HdSt_MeshShaderKey::HdSt_MeshShaderKey(

// Optimization : See if we can skip the geometry shader.
bool const canSkipGS =
ptvsStageEnabled ||
tessOnlyEnabled ||
// Whether we can skip executing the displacement shading terminal
(!hasCustomDisplacement
&& (normalsSource != NormalSourceLimit)
Expand Down Expand Up @@ -603,7 +661,7 @@ HdSt_MeshShaderKey::HdSt_MeshShaderKey(
FS[fsIndex++] = _tokens->mainPatchCoordTriQuadFS;

// Patches
} else if (isPrimTypePatches && ptvsStageEnabled) {
} else if (isPrimTypePatches && tessOnlyEnabled) {
FS[fsIndex++] = _tokens->mainPatchCoordTessFS;
// Points/No GS
} else if (isPrimTypePoints || canSkipGS) {
Expand Down
2 changes: 1 addition & 1 deletion pxr/imaging/hdSt/meshShaderKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ struct HdSt_MeshShaderKey : public HdSt_ShaderKey
TfToken glslfx;
TfToken VS[7];
TfToken TCS[4];
TfToken TES[4];
TfToken TES[12];
TfToken PTCS[4];
TfToken PTVS[12];
TfToken GS[10];
Expand Down
122 changes: 122 additions & 0 deletions pxr/imaging/hdSt/shaders/mesh.glslfx
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,67 @@ void main(void)
ProcessPrimvarsOut(basis, 5, 6, 9, 10, UV);
}

--- --------------------------------------------------------------------------
-- layout Mesh.TessEval.BezierQuadNoGS

# XXX: due to NVIDIA shader compiler bug (filed as 1687344)
# we can't put patchCoord into interface block.
[
["in", "quads"],
["in", "vec4", "tessOuterLo", "patch"],
["in", "vec4", "tessOuterHi", "patch"],
["in block array", "VertexDataTess", "inpt", "gl_MaxPatchVertices",
["OsdPerPatchVertexBezier", "v"]
],
["out block", "VertexData", "outData",
["vec4", "Peye"],
["vec3", "Neye"]
],
["out", "vec4", "tesPatchCoord"],
["out", "vec2", "tesTessCoord"]
]

--- --------------------------------------------------------------------------
-- glsl Mesh.TessEval.BezierQuadNoGS

void main(void)
{
OsdPerPatchVertexBezier cv[16];
for (int i = 0; i < 16; ++i) {
cv[i] = inpt[i].v;
}
vec2 UV = OsdGetTessParameterization(gl_TessCoord.xy,
tessOuterLo,
tessOuterHi);

vec3 P = vec3(0), dPu = vec3(0), dPv = vec3(0);
vec3 N = vec3(0), dNu = vec3(0), dNv = vec3(0);

ivec3 patchParam = inpt[0].v.patchParam;
OsdEvalPatchBezier(patchParam, UV, cv, P, dPu, dPv, N, dNu, dNv);

tesPatchCoord = OsdInterpolatePatchCoord(UV, patchParam);
tesTessCoord = UV;

// Bilinear basis
vec4 basis = vec4(
(1.0-UV.x) * (1.0-UV.y), UV.x * (1.0-UV.y),
(1.0-UV.x) * UV.y, UV.x * UV.y );

bool isFlipped = IsFlipped(); // consider handedness AND negative-scale
vec3 Neye = isFlipped ? -N : N;

vec3 point = DisplacementTerminal(5, 6, 9, 10, P, Neye, basis, UV);

outData.Peye = vec4(point, 1.0);
outData.Neye = Neye; // normalized

gl_Position = vec4(GetProjectionMatrix() * outData.Peye);
ApplyClipPlanes(outData.Peye);

ProcessPrimvarsOut(basis, 5, 6, 9, 10, UV);
}

--- --------------------------------------------------------------------------
-- layout Mesh.TessControl.BoxSplineTriangle

Expand Down Expand Up @@ -537,6 +598,67 @@ void main(void)
ProcessPrimvarsOut(basis, 4, 5, 8, 0, UV);
}

--- --------------------------------------------------------------------------
-- layout Mesh.TessEval.BezierTriangleNoGS

# XXX: due to NVIDIA shader compiler bug (filed as 1687344)
# we can't put patchCoord into interface block.
[
["in", "triangles"],
["in", "vec4", "tessOuterLo", "patch"],
["in", "vec4", "tessOuterHi", "patch"],
["in block array", "VertexDataTess", "inpt", "gl_MaxPatchVertices",
["OsdPerPatchVertexBezier", "v"]
],
["out block", "VertexData", "outData",
["vec4", "Peye"],
["vec3", "Neye"]
],
["out", "vec4", "tesPatchCoord"],
["out", "vec2", "tesTessCoord"]
]

--- --------------------------------------------------------------------------
-- glsl Mesh.TessEval.BezierTriangleNoGS

void main(void)
{
OsdPerPatchVertexBezier cv[15];
for (int i = 0; i < 15; ++i) {
cv[i] = inpt[i].v;
}
vec2 UV = OsdGetTessParameterizationTriangle(gl_TessCoord.xyz,
tessOuterLo,
tessOuterHi);

vec3 P = vec3(0), dPu = vec3(0), dPv = vec3(0);
vec3 N = vec3(0), dNu = vec3(0), dNv = vec3(0);

ivec3 patchParam = inpt[0].v.patchParam;
OsdEvalPatchBezierTriangle(patchParam, UV, cv, P, dPu, dPv, N, dNu, dNv);

tesPatchCoord = OsdInterpolatePatchCoordTriangle(UV, patchParam);
tesTessCoord = UV;

// Barycentric basis
vec4 basis = vec4(
(1.0f-UV.x-UV.y), UV.x, UV.y, 0.0f);

bool isFlipped = IsFlipped(); // consider handedness AND negative-scale
vec3 Neye = isFlipped ? -N : N;

vec3 point = DisplacementTerminal(4, 5, 8, 0, P, Neye, basis, UV);

outData.Peye = vec4(P, 1);
outData.Neye = Neye; // normalized

gl_Position = vec4(GetProjectionMatrix() * outData.Peye);
ApplyClipPlanes(outData.Peye);

ProcessPrimvarsOut(basis, 4, 5, 8, 0, UV);
}


--- --------------------------------------------------------------------------
-- glsl Mesh.PostTessControl.PatchCommon

Expand Down
Loading