Skip to content

Commit

Permalink
[HdSt,HgiMetal] Metal tessellation for basisCurves and meshes
Browse files Browse the repository at this point in the history
Implemented Storm support for the remaining refined drawing
modes for basisCurves and mesh geometry for HgiMetal.

Two significant differences with Metal tessellation are:
  1) need to pre-compute a buffer of tessellation factors.
  2) different attribute data access from PTVS vs VS/TCS.

For 1) we introduce a PostTessellationControl (PTCS) shader
which is a Metal PostTessellationVertex shader (PTVS) executed
with rasterization disabled to compute and store values into
a buffer of tessellation factors.

For 2) we're able to handle most data access differences within
Storm codeGen's accessor methods or within helper methods in
the shader source.

- Added HgiBindResourceTypeTessFactors to designate a buffer
binding for the tessFactors buffer.

- Updated HdSt_PipelineDrawBatch to allocate a tessFactors buffer
and execute PTCS shaders.

- Updated HgiMetal to bind the tessFactors buffer and execute
PTCS shaders.

- Updated Storm codeGen to support accessing varying data buffers
indexed using values from the primitive index buffer.

- Updated Storm basisCurves shader source and shaderKey to support
both VS/TCS/TES and PTCS/PTVS pipelines with most shader source
code shared in common between the two different kinds of pipelines.

- Updated Storm mesh shader source and shaderKey to support
PTCS/PTVS tessellation using OpenSubdiv surface evaluation.

Contribution: Thor Hjalmarsson, David G Yu

Fixes #2027

(Internal change: 2269604)
  • Loading branch information
davidgyu authored and pixar-oss committed Mar 31, 2023
1 parent cdcc750 commit f818023
Show file tree
Hide file tree
Showing 23 changed files with 1,545 additions and 266 deletions.
19 changes: 8 additions & 11 deletions pxr/imaging/hdSt/basisCurves.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,16 +289,9 @@ HdStBasisCurves::_UpdateDrawItemGeometricShader(
std::static_pointer_cast<HdStResourceRegistry>(
renderIndex.GetResourceRegistry());

// For the time being, don't use complex curves on Metal. Support for this
// is planned for the future.
const bool hasMetalTessellation =
resourceRegistry->GetHgi()->GetCapabilities()->
IsSet(HgiDeviceCapabilitiesBitsMetalTessellation);

TfToken curveType = _topology->GetCurveType();
TfToken curveBasis = _topology->GetCurveBasis();
bool supportsRefinement = _SupportsRefinement(_refineLevel) &&
!hasMetalTessellation;
bool supportsRefinement = _SupportsRefinement(_refineLevel);
if (!supportsRefinement) {
// XXX: Rendering non-linear (i.e., cubic) curves as linear segments
// when unrefined can be confusing. Should we continue to do this?
Expand Down Expand Up @@ -329,8 +322,7 @@ HdStBasisCurves::_UpdateDrawItemGeometricShader(
case HdBasisCurvesGeomStylePatch:
{
if (_SupportsRefinement(_refineLevel) &&
_SupportsUserWidths(drawItem) &&
!hasMetalTessellation) {
_SupportsUserWidths(drawItem)) {
if (_SupportsUserNormals(drawItem)){
drawStyle = HdSt_BasisCurvesShaderKey::RIBBON;
normalStyle = HdSt_BasisCurvesShaderKey::ORIENTED;
Expand Down Expand Up @@ -381,6 +373,10 @@ HdStBasisCurves::_UpdateDrawItemGeometricShader(
}
}

bool const hasMetalTessellation =
resourceRegistry->GetHgi()->GetCapabilities()->
IsSet(HgiDeviceCapabilitiesBitsMetalTessellation);

HdSt_BasisCurvesShaderKey shaderKey(curveType,
curveBasis,
drawStyle,
Expand All @@ -389,7 +385,8 @@ HdStBasisCurves::_UpdateDrawItemGeometricShader(
_basisNormalInterpolation,
shadingTerminal,
hasAuthoredTopologicalVisiblity,
_pointsShadingEnabled);
_pointsShadingEnabled,
hasMetalTessellation);

TF_DEBUG(HD_RPRIM_UPDATED).
Msg("HdStBasisCurves(%s) - Shader Key PrimType: %s\n ",
Expand Down
347 changes: 261 additions & 86 deletions pxr/imaging/hdSt/basisCurvesShaderKey.cpp

Large diffs are not rendered by default.

17 changes: 14 additions & 3 deletions pxr/imaging/hdSt/basisCurvesShaderKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,24 +74,35 @@ struct HdSt_BasisCurvesShaderKey : public HdSt_ShaderKey
bool basisNormalInterpolation,
TfToken shadingTerminal,
bool hasAuthoredTopologicalVisibility,
bool pointsShadingEnabled);
bool pointsShadingEnabled,
bool hasMetalTessellation);

~HdSt_BasisCurvesShaderKey();

TfToken const &GetGlslfxFilename() const override { return glslfx; }
TfToken const *GetVS() const override { return VS; }
TfToken const *GetTCS() const override { return TCS; }
TfToken const *GetTES() const override { return TES; }
TfToken const *GetPTCS() const override { return PTCS; }
TfToken const *GetPTVS() const override { return PTVS; }
TfToken const *GetFS() const override { return FS; }

HdSt_GeometricShader::PrimitiveType GetPrimitiveType() const override {
return primType;
}

bool UseMetalTessellation() const override {
return useMetalTessellation;
}

HdSt_GeometricShader::PrimitiveType primType;
bool useMetalTessellation;
TfToken glslfx;
TfToken VS[7];
TfToken TCS[4];
TfToken TES[9];
TfToken TCS[7];
TfToken TES[12];
TfToken PTCS[9];
TfToken PTVS[14];
TfToken FS[8];
};

Expand Down
140 changes: 119 additions & 21 deletions pxr/imaging/hdSt/codeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1916,6 +1916,16 @@ HdSt_CodeGen::Compile(HdStResourceRegistry*const registry)
}
}

if (_geometricShader->IsPrimTypeMesh() &&
_geometricShader->IsPrimTypePatches()) {
if (_hasPTCS) {
_genPTCS << _GetOSDPatchBasisShaderSource();
}
if (_hasPTVS) {
_genPTVS << _GetOSDPatchBasisShaderSource();
}
}

// Needed for patch-based face-varying primvar refinement
if (_geometricShader->GetFvarPatchType() ==
HdSt_GeometricShader::FvarPatchType::PATCH_BSPLINE ||
Expand Down Expand Up @@ -1983,15 +1993,17 @@ HdSt_CodeGen::Compile(HdStResourceRegistry*const registry)

_procPTVSOut << "template <typename T>\n"
"T InterpolatePrimvar("
"T inPv0, T inPv1, T inPv2, T inPv3, vec4 basis) {\n"
"T inPv0, T inPv1, T inPv2, T inPv3, vec4 basis, "
"vec2 uv = vec2()) {\n"
" return"
" inPv0 * basis[0] +"
" inPv1 * basis[1] +"
" inPv2 * basis[2] +"
" inPv3 * basis[3];\n"
"}\n"
"void ProcessPrimvarsOut("
"vec4 basis, int i0, int i1, int i2, int i3) {\n";
"vec4 basis, int i0, int i1, int i2, int i3, "
"vec2 uv = vec2()) {\n";

// geometry shader plumbing
switch(_geometricShader->GetPrimitiveType())
Expand Down Expand Up @@ -2122,12 +2134,12 @@ HdSt_CodeGen::Compile(HdStResourceRegistry*const registry)
if (tessEvalShader.find("OsdEvalPatch") != std::string::npos) {
_osdTES << _GetOSDCommonShaderSource();
}
if (postTessControlShader.find("OsdComputePerPatch") != std::string::npos
if (postTessControlShader.find("OsdComputeTessLevels") != std::string::npos
|| postTessControlShader.find("OsdInterpolatePatchCoord")
!= std::string::npos) {
_osdPTCS << _GetOSDCommonShaderSource();
}
if (postTessVertexShader.find("OsdEvalPatch") != std::string::npos
if (postTessVertexShader.find("OsdEvaluatePatchBasis") != std::string::npos
|| postTessVertexShader.find("OsdInterpolatePatchCoord")
!= std::string::npos) {
_osdPTVS << _GetOSDCommonShaderSource();
Expand Down Expand Up @@ -2645,8 +2657,31 @@ HdSt_CodeGen::_CompileWithGeneratedHgiResources(
HgiShaderFunctionDesc ptcsDesc;
ptcsDesc.shaderStage = HgiShaderStagePostTessellationControl;

if (_metaData.tessFactorsBinding.binding.IsValid()) {
HdStBinding binding = _metaData.tessFactorsBinding.binding;
_EmitDeclaration(&_resPTCS,
_metaData.tessFactorsBinding.name,
_metaData.tessFactorsBinding.dataType,
binding,
true);
}

ptcsDesc.tessellationDescriptor.numVertsPerPatchIn =
std::to_string(_geometricShader->GetPrimitiveIndexSize());
ptcsDesc.tessellationDescriptor.numVertsPerPatchOut =
std::to_string(_geometricShader->GetNumPatchEvalVerts());

ptcsDesc.tessellationDescriptor.patchType =
(_geometricShader->IsPrimTypeTriangles() ||
_geometricShader->GetPrimitiveType() ==
HdSt_GeometricShader::PrimitiveType::PRIM_MESH_BOXSPLINETRIANGLE) ?
HgiShaderFunctionTessellationDesc::PatchType::Triangles :
HgiShaderFunctionTessellationDesc::PatchType::Quads;
if (_geometricShader->GetHgiPrimitiveType() ==
HgiPrimitiveTypePointList) {
ptcsDesc.tessellationDescriptor.patchType =
HgiShaderFunctionTessellationDesc::PatchType::Isolines;
}

resourceGen._GenerateHgiResources(&ptcsDesc,
HdShaderTokens->postTessControlShader, _resAttrib, _metaData);
Expand All @@ -2655,6 +2690,12 @@ HdSt_CodeGen::_CompileWithGeneratedHgiResources(
resourceGen._GenerateHgiResources(&ptcsDesc,
HdShaderTokens->postTessControlShader, _resPTCS, _metaData);

// material in PTCS
resourceGen._GenerateHgiResources(&ptcsDesc,
HdShaderTokens->postTessControlShader, _resMaterial, _metaData);
resourceGen._GenerateHgiTextureResources(&ptcsDesc,
HdShaderTokens->postTessControlShader, _resTextures, _metaData);

std::string const declarations =
_genDefines.str() + _genDecl.str() + _osdPTCS.str();
std::string const source = _genAccessors.str() + _genPTCS.str();
Expand All @@ -2666,14 +2707,38 @@ HdSt_CodeGen::_CompileWithGeneratedHgiResources(
// builtins

HgiShaderFunctionAddStageInput(
&ptcsDesc, "thread_position_in_grid", "unsigned",
"thread_position_in_grid");
&ptcsDesc, "hd_BaseInstance", "uint",
HgiShaderKeywordTokens->hdBaseInstance);
HgiShaderFunctionAddStageInput(
&ptcsDesc, "patch_id", "uint",
HgiShaderKeywordTokens->hdPatchID);

std::string tessCoordType =
(_geometricShader->IsPrimTypeTriangles() ||
_geometricShader->GetPrimitiveType() ==
HdSt_GeometricShader::PrimitiveType::PRIM_MESH_BOXSPLINETRIANGLE)
? "vec3" : "vec2";

HgiShaderFunctionAddStageInput(
&ptcsDesc, "thread_position_in_threadgroup", "unsigned",
"thread_position_in_threadgroup");
&ptcsDesc, "gl_TessCoord", tessCoordType,
HgiShaderKeywordTokens->hdPositionInPatch);

HgiShaderFunctionAddStageInput(
&ptcsDesc, "threadgroup_position_in_grid", "unsigned",
"threadgroup_position_in_grid");
&ptcsDesc, "hd_InstanceID", "uint",
HgiShaderKeywordTokens->hdInstanceID);

HgiShaderFunctionAddStageOutput(
&ptcsDesc, "gl_Position", "vec4",
"position");

char const* pointRole =
(_geometricShader->GetPrimitiveType() ==
HdSt_GeometricShader::PrimitiveType::PRIM_POINTS)
? "point_size" : "";

HgiShaderFunctionAddStageOutput(
&ptcsDesc, "gl_PointSize", "float",
pointRole);

if (!glslProgram->CompileShader(ptcsDesc)) {
return nullptr;
Expand All @@ -2688,12 +2753,21 @@ HdSt_CodeGen::_CompileWithGeneratedHgiResources(

ptvsDesc.tessellationDescriptor.numVertsPerPatchIn =
std::to_string(_geometricShader->GetPrimitiveIndexSize());
ptvsDesc.tessellationDescriptor.numVertsPerPatchOut =
std::to_string(_geometricShader->GetNumPatchEvalVerts());

//Set the patchtype to later decide tessfactor types
ptvsDesc.tessellationDescriptor.patchType =
_geometricShader->IsPrimTypeTriangles() ?
HgiShaderFunctionTessellationDesc::PatchType::Triangles :
HgiShaderFunctionTessellationDesc::PatchType::Quads;
(_geometricShader->IsPrimTypeTriangles() ||
_geometricShader->GetPrimitiveType() ==
HdSt_GeometricShader::PrimitiveType::PRIM_MESH_BOXSPLINETRIANGLE)
? HgiShaderFunctionTessellationDesc::PatchType::Triangles
: HgiShaderFunctionTessellationDesc::PatchType::Quads;
if (_geometricShader->GetHgiPrimitiveType() ==
HgiPrimitiveTypePointList) {
ptvsDesc.tessellationDescriptor.patchType =
HgiShaderFunctionTessellationDesc::PatchType::Isolines;
}

resourceGen._GenerateHgiResources(&ptvsDesc,
HdShaderTokens->postTessVertexShader, _resAttrib, _metaData);
Expand Down Expand Up @@ -2726,8 +2800,10 @@ HdSt_CodeGen::_CompileWithGeneratedHgiResources(
HgiShaderKeywordTokens->hdPatchID);

std::string tessCoordType =
_geometricShader->IsPrimTypeTriangles() ?
"vec3" : "vec2";
(_geometricShader->IsPrimTypeTriangles() ||
_geometricShader->GetPrimitiveType() ==
HdSt_GeometricShader::PrimitiveType::PRIM_MESH_BOXSPLINETRIANGLE)
? "vec3" : "vec2";

HgiShaderFunctionAddStageInput(
&ptvsDesc, "gl_TessCoord", tessCoordType,
Expand All @@ -2738,8 +2814,8 @@ HdSt_CodeGen::_CompileWithGeneratedHgiResources(
HgiShaderKeywordTokens->hdInstanceID);

HgiShaderFunctionAddStageOutput(
&ptvsDesc, "gl_Position", "vec4",
"position");
&ptvsDesc, "gl_Position", "vec4",
"position");

char const* pointRole =
(_geometricShader->GetPrimitiveType() ==
Expand Down Expand Up @@ -5160,7 +5236,7 @@ HdSt_CodeGen::_GenerateVertexAndFaceVaryingPrimvar()
<< "HdGet_" << name << "(i0), "
<< "HdGet_" << name << "(i1), "
<< "HdGet_" << name << "(i2), "
<< "HdGet_" << name << "(i3), basis);\n";
<< "HdGet_" << name << "(i3), basis, uv);\n";
}

/*
Expand Down Expand Up @@ -5190,6 +5266,28 @@ HdSt_CodeGen::_GenerateVertexAndFaceVaryingPrimvar()
} inPrimvars;
*/

HdSt_ResourceBinder::MetaData::BindingDeclaration const &
indexBufferBinding = _metaData.indexBufferBinding;
if (!indexBufferBinding.name.IsEmpty()) {
_EmitDeclaration(&_resPTCS,
indexBufferBinding.name,
indexBufferBinding.dataType,
indexBufferBinding.binding);
_EmitDeclaration(&_resPTVS,
indexBufferBinding.name,
indexBufferBinding.dataType,
indexBufferBinding.binding);

_EmitBufferAccessor(accessorsPTCS,
indexBufferBinding.name,
indexBufferBinding.dataType,
"patch_id * VERTEX_CONTROL_POINTS_PER_PATCH + localIndex");
_EmitBufferAccessor(accessorsPTVS,
indexBufferBinding.name,
indexBufferBinding.dataType,
"patch_id * VERTEX_CONTROL_POINTS_PER_PATCH + localIndex");
}

TF_FOR_ALL (it, _metaData.varyingData) {
HdStBinding binding = it->first;
TfToken const &name = it->second.name;
Expand All @@ -5214,11 +5312,11 @@ HdSt_CodeGen::_GenerateVertexAndFaceVaryingPrimvar()

// Access PTCS varying primvar from varying data buffer.
_EmitBufferAccessor(accessorsPTCS, name, dataType,
"GetDrawingCoord().varyingCoord + patch_id + localIndex");
"GetDrawingCoord().varyingCoord + HdGet_indices(localIndex)");

// Access PTVS varying primvar from varying data buffer.
_EmitBufferAccessor(accessorsPTVS, name, dataType,
"GetDrawingCoord().varyingCoord + patch_id + localIndex");
"GetDrawingCoord().varyingCoord + HdGet_indices(localIndex)");

// interstage plumbing
_procVS << " outPrimvars." << name
Expand All @@ -5240,7 +5338,7 @@ HdSt_CodeGen::_GenerateVertexAndFaceVaryingPrimvar()
<< "HdGet_" << name << "(i0), "
<< "HdGet_" << name << "(i1), "
<< "HdGet_" << name << "(i2), "
<< "HdGet_" << name << "(i3), basis);\n";
<< "HdGet_" << name << "(i3), basis, uv);\n";
}

/*
Expand Down
Loading

0 comments on commit f818023

Please sign in to comment.