[hdSt] Provide visual feedback for broken material shaders.
authorrajabala <rajabala@users.noreply.github.com>
Wed, 3 Jan 2024 19:50:48 +0000 (11:50 -0800)
committerpixar-oss <pixar-oss@users.noreply.github.com>
Wed, 3 Jan 2024 19:50:48 +0000 (11:50 -0800)
- Add an "invalid material network" shader to provide visual feedback for prims that fail shader compilation/linking. This shader is used when errors are encountered during shader code generation when the env setting HDST_ENABLE_BROKEN_SHADER_VISUAL_FEEDBACK is enabled. It is currently disabled by default.

Fixes #2613

(Internal change: 2310129)

pxr/imaging/hdSt/CMakeLists.txt
pxr/imaging/hdSt/drawBatch.cpp
pxr/imaging/hdSt/package.cpp
pxr/imaging/hdSt/package.h
pxr/imaging/hdSt/shaders/fallbackMaterialNetwork.glslfx
pxr/imaging/hdSt/shaders/invalidMaterialNetwork.glslfx [new file with mode: 0644]
pxr/usdImaging/usdImagingGL/CMakeLists.txt
pxr/usdImaging/usdImagingGL/testenv/testUsdImagingGLInvalidMaterial/baseline/testUsdImagingGLInvalidMaterial.png [new file with mode: 0644]
pxr/usdImaging/usdImagingGL/testenv/testUsdImagingGLInvalidMaterial/buggyShader.glslfx [new file with mode: 0644]
pxr/usdImaging/usdImagingGL/testenv/testUsdImagingGLInvalidMaterial/invalidMaterial.usda [new file with mode: 0644]

index 81a5088471dabf90717b6007de803201111f1414..7ea6a90532149ded60db9d1554c554112999bdd0 100644 (file)
@@ -192,6 +192,7 @@ pxr_library(hdSt
         shaders/frustumCull.glslfx
         shaders/imageShader.glslfx
         shaders/instancing.glslfx
+        shaders/invalidMaterialNetwork.glslfx
         shaders/mesh.glslfx
         shaders/meshFaceCull.glslfx
         shaders/meshNormal.glslfx
index 9b58b79585428252ca1b1d51f77de319b31103ee..a125d0c53a09e56d02577cdeb8f5529f17c2e633 100644 (file)
@@ -41,8 +41,8 @@
 
 #include "pxr/imaging/hio/glslfx.h"
 
+#include "pxr/base/tf/envSetting.h"
 #include "pxr/base/tf/getenv.h"
-
 #include "pxr/base/tf/hash.h"
 
 #include <mutex>
 PXR_NAMESPACE_OPEN_SCOPE
 
 
+TF_DEFINE_ENV_SETTING(HDST_ENABLE_BROKEN_SHADER_VISUAL_FEEDBACK, false,
+    "Provide visual feedback for prims when the composed shader fails to "
+    "compile or link by using the invalid material shader.");
+
 namespace
 {
+
+bool
+_ProvideVisualFeedbackForBrokenShaders()
+{
+    static const bool enabled =
+        TfGetEnvSetting(HDST_ENABLE_BROKEN_SHADER_VISUAL_FEEDBACK);
+    return enabled;
+}
+
 const std::string&
 _GetPrimPathSubstringForDebugLogging()
 {
@@ -277,6 +290,24 @@ _GetFallbackMaterialNetworkShader()
     return fallbackShader;
 }
 
+static
+HdSt_MaterialNetworkShaderSharedPtr
+_GetInvalidMaterialNetworkShader()
+{
+    static std::once_flag once;
+    static HdSt_MaterialNetworkShaderSharedPtr invalidShader;
+   
+    std::call_once(once, [](){
+        HioGlslfxSharedPtr glslfx =
+            std::make_shared<HioGlslfx>(
+                HdStPackageInvalidMaterialNetworkShader());
+
+        invalidShader.reset(new HdStGLSLFXShader(glslfx));
+    });
+
+    return invalidShader;
+}
+
 HdSt_DrawBatch::_DrawingProgram &
 HdSt_DrawBatch::_GetDrawingProgram(HdStRenderPassStateSharedPtr const &state,
                                  HdStResourceRegistrySharedPtr const &resourceRegistry)
@@ -333,24 +364,30 @@ HdSt_DrawBatch::_GetDrawingProgram(HdStRenderPassStateSharedPtr const &state,
             TF_CODING_ERROR("Failed to compile shader for prim %s.",
                             firstDrawItem->GetRprimID().GetText());
 
-
             // If we failed to compile the material network, replace it
-            // with the fallback material network shader and try again.
+            // either with the invalid material network shader OR the
+            // fallback material network shader and try again.
             // XXX: Note that we only say "material network shader" here
             // because it is currently the only one for which we allow
             // customization.  We expect all the other shaders to compile
             // or else the shipping code is broken and needs to be fixed.
             // When we open up more shaders for customization, we will
             // need to check them as well.
-            
-            _program.SetMaterialNetworkShader(
-                _GetFallbackMaterialNetworkShader());
+
+            const HdSt_MaterialNetworkShaderSharedPtr shader =
+                _ProvideVisualFeedbackForBrokenShaders()
+                ? _GetInvalidMaterialNetworkShader()
+                : _GetFallbackMaterialNetworkShader();
+                
+            _program.SetMaterialNetworkShader(shader);
 
             bool res = _program.CompileShader(firstDrawItem, 
                                               resourceRegistry,
                                               logCacheLookup);
-            // We expect the fallback shader to always compile.
-            TF_VERIFY(res, "Failed to compile with fallback material network");
+
+            // We expect the invalid/fallback shader to always compile.
+            TF_VERIFY(res, "Failed to compile with the invalid/fallback "
+                           "material network shader.");
         }
 
         _shaderHash = shaderHash;
index 8ec75c47fb592f9ab7383371059532b9c89f308f..f60609bc6ea7e5ecfc1e52501ece5643bf3f78b9 100644 (file)
@@ -103,6 +103,13 @@ HdStPackageFallbackMaterialNetworkShader()
     return s;
 }
 
+TfToken
+HdStPackageInvalidMaterialNetworkShader()
+{
+    static TfToken s = _GetShaderPath("invalidMaterialNetwork.glslfx");
+    return s;
+}
+
 TfToken
 HdStPackageFallbackVolumeShader()
 {
index f0edb5c26a562b49a09ec90575fbfcf946f7d6a5..a107b40546b89cbcf2de32cf642e1ff838f3c3e7 100644 (file)
@@ -52,6 +52,9 @@ TfToken HdStPackageFallbackLightingShader();
 HDST_API
 TfToken HdStPackageFallbackMaterialNetworkShader();
 
+HDST_API
+TfToken HdStPackageInvalidMaterialNetworkShader();
+
 HDST_API
 TfToken HdStPackageFallbackVolumeShader();
 
index 2dea4e7dc8c135dbfc3f044dfcf2e1831c737f87..fe45962c239c421b2be021d2aea766c59a4e876f 100644 (file)
 //
 
 --- This is what an import might look like.
---- #import $TOOLS/hdSt/shaders/fallbackSurface.glslfx
+--- #import $TOOLS/hdSt/shaders/fallbackMaterialNetwork.glslfx
 
 ---
---- The fallback shader is used as a replacement shader if the
---- original material shader failed to compile. It needs to
---- define both the surfaceShader() and displacementShader() terminals.
+--- The fallback shader is used as a replacement shader if no material binding
+--- was provided. It needs to define both the surfaceShader() and
+--- displacementShader() terminals.
 ---
 -- configuration
 {
diff --git a/pxr/imaging/hdSt/shaders/invalidMaterialNetwork.glslfx b/pxr/imaging/hdSt/shaders/invalidMaterialNetwork.glslfx
new file mode 100644 (file)
index 0000000..fdcdced
--- /dev/null
@@ -0,0 +1,70 @@
+-- glslfx version 0.1
+
+//
+// Copyright 2023 Pixar
+//
+// Licensed under the Apache License, Version 2.0 (the "Apache License")
+// with the following modification; you may not use this file except in
+// compliance with the Apache License and the following modification to it:
+// Section 6. Trademarks. is deleted and replaced with:
+//
+// 6. Trademarks. This License does not grant permission to use the trade
+//    names, trademarks, service marks, or product names of the Licensor
+//    and its affiliates, except as required to comply with Section 4(c) of
+//    the License and to reproduce the content of the NOTICE file.
+//
+// You may obtain a copy of the Apache License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the Apache License with the above modification is
+// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the Apache License for the specific
+// language governing permissions and limitations under the Apache License.
+//
+
+--- This is what an import might look like.
+--- #import $TOOLS/hdSt/shaders/invalidMaterialNetwork.glslfx
+
+---
+--- The invalid shader is used as a replacement shader if the
+--- original material shader failed to compile. It needs to
+--- define both the surfaceShader() and displacementShader() terminals.
+---
+-- configuration
+{
+    "techniques": {
+        "default": {
+            "displacementShader": {
+                "source": [ "Invalid.Displacement" ]
+            },
+            "surfaceShader": {
+                "source": [ "Invalid.Surface" ]
+            }
+        }
+    }
+}
+
+--- --------------------------------------------------------------------------
+-- glsl Invalid.Surface
+
+vec4 surfaceShader(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord)
+{
+    vec2 t = gl_FragCoord.xy;
+    float v = mod(round(t.x + t.y), 16.0);
+
+    const vec4 invalidColor = vec4(0.7, 0.3, 0.3, 1.0);
+    return mix(color, invalidColor, v);
+
+    // Alt look:
+    // Override the color to a bright red. Don't light it.
+    // return vec4(0.9, 0.0, 0.0, 1.0);
+}
+--- --------------------------------------------------------------------------
+-- glsl Invalid.Displacement
+
+vec4 displacementShader(int index, vec4 Peye, vec3 Neye, vec4 patchCoord)
+{
+    return Peye;
+}
index fb8fd850a0cd8848ebe5d987966ca7125021db3f..2841ef8722975181fb743803f26ef698199e6017 100644 (file)
@@ -611,11 +611,15 @@ pxr_install_test_dir(
     DEST testUsdImagingGLGeomSubsets
 )
 
+pxr_install_test_dir(
+    SRC testenv/testUsdImagingGLInvalidMaterial
+    DEST testUsdImagingGLInvalidMaterial
+)
+
 #
 # Register tests that don't depend on build configuration or external libraries
 # (such as MaterialX, OpenVDB, PTEX).
 #
-
 pxr_register_test(testUsdImagingGLBasicDrawing
     COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testUsdImagingGLBasicDrawing -offscreen -stage basicDrawing/basicDrawing.usda -write testUsdImagingGLBasicDrawing.png"
     IMAGE_DIFF_COMPARE
@@ -4566,6 +4570,17 @@ pxr_register_test(testUsdImagingGLAovVisualization_Storm_depth
     TESTENV testUsdImagingGLAovVisualization
 )
 
+pxr_register_test(testUsdImagingGLInvalidMaterial
+    COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testUsdImagingGLBasicDrawing -offscreen -lighting -shading smooth  -frameAll -stage invalidMaterial.usda -write testUsdImagingGLInvalidMaterial.png"
+    IMAGE_DIFF_COMPARE
+    testUsdImagingGLInvalidMaterial.png
+    FAIL 1
+    FAIL_PERCENT 1
+    PERCEPTUAL
+    EXPECTED_RETURN_CODE 0
+    TESTENV testUsdImagingGLInvalidMaterial
+)
+
 #
 # Conditionally install and register MaterialX tests
 #
diff --git a/pxr/usdImaging/usdImagingGL/testenv/testUsdImagingGLInvalidMaterial/baseline/testUsdImagingGLInvalidMaterial.png b/pxr/usdImaging/usdImagingGL/testenv/testUsdImagingGLInvalidMaterial/baseline/testUsdImagingGLInvalidMaterial.png
new file mode 100644 (file)
index 0000000..e3c1825
Binary files /dev/null and b/pxr/usdImaging/usdImagingGL/testenv/testUsdImagingGLInvalidMaterial/baseline/testUsdImagingGLInvalidMaterial.png differ
diff --git a/pxr/usdImaging/usdImagingGL/testenv/testUsdImagingGLInvalidMaterial/buggyShader.glslfx b/pxr/usdImaging/usdImagingGL/testenv/testUsdImagingGLInvalidMaterial/buggyShader.glslfx
new file mode 100644 (file)
index 0000000..d6e52e8
--- /dev/null
@@ -0,0 +1,22 @@
+-- glslfx version 0.1
+
+-- configuration
+{
+    "techniques": {
+        "default": {
+            "surfaceShader": {
+                "source": [ "Surface.Buggy" ]
+            }
+        }
+    }
+}
+
+--- --------------------------------------------------------------------------
+-- glsl Surface.Buggy
+
+vec4 surfaceShader(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord)
+{
+    int foo = vec2(0,0); // <-- this won't compile, thankfully.
+    // Use foo below in case compiler optimization eliminates the above line.
+    return vec4(foo, 0, 0.8, 0.8);
+}
diff --git a/pxr/usdImaging/usdImagingGL/testenv/testUsdImagingGLInvalidMaterial/invalidMaterial.usda b/pxr/usdImaging/usdImagingGL/testenv/testUsdImagingGLInvalidMaterial/invalidMaterial.usda
new file mode 100644 (file)
index 0000000..6b5e539
--- /dev/null
@@ -0,0 +1,61 @@
+#usda 1.0
+(
+    upAxis = "Y"
+)
+
+def DistantLight "Sun"
+{
+    # Use default intensity and direction (light is emitted along -Z).
+    # The latter is fine since the stage is Y up.
+}
+
+def Xform "Cubes"
+{
+    def Cube "RedCube" (
+        prepend apiSchemas = ["MaterialBindingAPI"]
+    )
+    {
+        custom rel material:binding = </Materials/Red>
+        custom double3 xformOp:translate = (-2,0,0)
+        uniform token[] xformOpOrder = ["xformOp:translate"]
+    }
+
+    def Cube "BuggyCube" (
+        prepend apiSchemas = ["MaterialBindingAPI"]
+    )
+    {
+        custom rel material:binding = </Materials/Buggy>
+        custom double3 xformOp:translate = (2,0,0)
+        uniform token[] xformOpOrder = ["xformOp:translate"]
+    }
+}
+
+def Scope "Materials"
+{
+    def Material "Red" (
+    )
+    {
+        token outputs:surface.connect = </Materials/Red/PbrPreview.outputs:surface>
+
+        def Shader "PbrPreview"
+        {
+            uniform token info:id = "UsdPreviewSurface"
+            color3f inputs:diffuseColor = (0.8, 0, 0)
+            token outputs:surface
+        }
+    }
+
+    def Material "Buggy"
+    {
+        token outputs:surface.connect = </Materials/Buggy/Surface.outputs:surface>
+
+        def Shader "Surface"
+        {
+            uniform asset info:glslfx:sourceAsset = @buggyShader.glslfx@
+            uniform token info:implementationSource = "sourceAsset"
+            uniform float3 inputs:diffuseColor = (1, 0, 1)
+            token outputs:surface
+        }
+    }
+}
+