Author Topic: (Random bug) NullReferenceException when using SubstanceGraph.Duplicate  (Read 1098 times)

we get this error very often but not always...
this can happen in editor or at runtime. when in unity editor we have to quit and relaunch unity.

Code: [Select]
NullReferenceException: Object reference not set to an instance of an object.
  at Substance.Game.SubstanceGraph.OnEnable () [0x00000] in <00000000000000000000000000000000>:0
  at UnityEngine.ScriptableObject.CreateInstance[T] () [0x00000] in <00000000000000000000000000000000>:0
  at Substance.Game.SubstanceGraph.CreateGraphInstance (Substance.Game.Substance parent, System.UInt32 descIndex, System.UInt32 instanceIndex, System.String packageURL, System.String graphLabel, System.String prototypeName, System.String description, System.String category, System.String keywords, System.String author, System.String authorURL, System.String userTag) [0x00000] in <00000000000000000000000000000000>:0
  at Substance.Game.SubstanceGraph.DuplicateInstance (Substance.Game.SubstanceGraph graph, System.String pNewGraphName) [0x00000] in <00000000000000000000000000000000>:0
  at Substance.Game.SubstanceGraph.Duplicate (Substance.Game.SubstanceGraph graph1, System.String pNewGraphName) [0x00000] in <00000000000000000000000000000000>:0

sometimes this error comes instead:
Code: [Select]
NullReferenceException: Object reference not set to an instance of an object
Substance.Game.SubstanceGraph.GetUniqueGraphName (Substance.Game.Substance substance, System.String prototypeName) (at <686b229c4bb54a199af4ea1b7d9b3fc4>:0)
Substance.Game.SubstanceGraph.DuplicateInstance (Substance.Game.SubstanceGraph graph, System.String pNewGraphName) (at <686b229c4bb54a199af4ea1b7d9b3fc4>:0)
Substance.Game.SubstanceGraph.Duplicate (Substance.Game.SubstanceGraph graph1, System.String pNewGraphName) (at <686b229c4bb54a199af4ea1b7d9b3fc4>:0)
Substance.Game.SubstanceGraph.Duplicate (Substance.Game.SubstanceGraph graph1) (at <686b229c4bb54a199af4ea1b7d9b3fc4>:0)
Substance.Game.SubstanceGraph.Duplicate () (at <686b229c4bb54a199af4ea1b7d9b3fc4>:0)

we don't do anything special.
loading the substance graph from an assetbundle (at runtime) or as a referenced asset (in editor)
trying to duplicate it to change the randomseed and use the new material.

this is done in a loop with async rendering in parallel, could it be a problem (both rendering and duplicate - maybe some concurrent access in threads?)
Code: [Select]
for (int i = 0; i < nb; i++)

// clone substance graph
sgClone = substanceRef.Duplicate();

sgClone.SetInputFloat("$randomseed", Random.Range(0, 1 << 8));
// render substance graph
_RandomizeSubstancesData.Add(new SubstanceData(new Material(sgClone.material), refid));

and finally .. Unity crashes (App Version: Unity 2019.3.6f1_5c3fb0a11183):

Code: [Select]

  at <unknown> <0xffffffff>
  at (wrapper managed-to-native) object.wrapper_native_00007FFD348450C0 (intptr) [0x00009] in <437ba245d8404784b9fbab9b439ac908>:0
  at Substance.Platform.NativeFunctions.cppGetPreset (intptr) [0x0003d] in D:\Git\projectname\Unity\Assets\Allegorithmic\Plugins\Substance.Platform\NativeFunctions.cs:377
  at Substance.Game.SubstanceGraph.get_preset () [0x00006] in <686b229c4bb54a199af4ea1b7d9b3fc4>:0
  at Substance.Game.SubstanceGraph.DuplicateInstance (Substance.Game.SubstanceGraph,string) [0x00089] in <686b229c4bb54a199af4ea1b7d9b3fc4>:0
  at Substance.Game.SubstanceGraph.Duplicate (Substance.Game.SubstanceGraph,string) [0x00002] in <686b229c4bb54a199af4ea1b7d9b3fc4>:0
  at Substance.Game.SubstanceGraph.Duplicate (Substance.Game.SubstanceGraph) [0x00007] in <686b229c4bb54a199af4ea1b7d9b3fc4>:0
  at Substance.Game.SubstanceGraph.Duplicate () [0x00002] in <686b229c4bb54a199af4ea1b7d9b3fc4>:0

Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.


========== OUTPUTTING STACK TRACE ==================

0x00007FFD3486074D (Substance.Engine) cppShutdownSubstance
0x00007FFD3484EEAB (Substance.Engine) cppShutdownSubstance
0x0000019F6BFCEC08 (Mono JIT Code) (wrapper managed-to-native) object:wrapper_native_00007FFD348450C0 (intptr)
0x0000019F6BFCEA83 (Mono JIT Code) [D:\Git\projectname\Unity\Assets\Allegorithmic\Plugins\Substance.Platform\NativeFunctions.cs:377] Substance.Platform.NativeFunctions:cppGetPreset (intptr)
0x0000019F6BFCE61B (Mono JIT Code) Substance.Game.SubstanceGraph:get_preset ()
0x0000019F6BFCBF7B (Mono JIT Code) Substance.Game.SubstanceGraph:DuplicateInstance (Substance.Game.SubstanceGraph,string)
0x0000019F6BFC9303 (Mono JIT Code) Substance.Game.SubstanceGraph:Duplicate (Substance.Game.SubstanceGraph,string)
0x0000019F6BFC90A3 (Mono JIT Code) Substance.Game.SubstanceGraph:Duplicate (Substance.Game.SubstanceGraph)
0x0000019F6BFC8F83 (Mono JIT Code) Substance.Game.SubstanceGraph:Duplicate ()
<GenerateRandomMats>d__16:MoveNext ()
0x0000019F666DCB31 (Mono JIT Code) UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr)
0x0000019F666DCD20 (Mono JIT Code) (wrapper runtime-invoke) <Module>:runtime_invoke_void_object_intptr (object,intptr,intptr,intptr)
0x00007FFCC30DCBB0 (mono-2.0-bdwgc) [c:\build\output\unity-technologies\mono\mono\mini\mini-runtime.c:2809] mono_jit_runtime_invoke
0x00007FFCC3062122 (mono-2.0-bdwgc) [c:\build\output\unity-technologies\mono\mono\metadata\object.c:2921] do_runtime_invoke
0x00007FFCC306B11F (mono-2.0-bdwgc) [c:\build\output\unity-technologies\mono\mono\metadata\object.c:2968] mono_runtime_invoke
0x00007FF793B05D1E (Unity) scripting_method_invoke
0x00007FF793AFFA6D (Unity) ScriptingInvocation::Invoke
0x00007FF793AC1FDF (Unity) Coroutine::Run
0x00007FF793AD4BCC (Unity) MonoBehaviour::TryCreateAndRunCoroutine
0x00007FF793AC03AA (Unity) Coroutine::HandleIEnumerableCurrentReturnValue
0x00007FF793AC1D1F (Unity) Coroutine::ProcessCoroutineCurrent
0x00007FF793AC2113 (Unity) Coroutine::Run
0x00007FF793ABF54C (Unity) Coroutine::ContinueCoroutine
0x00007FF79317EA18 (Unity) DelayedCallManager::Update
0x00007FF7935AD15F (Unity) `InitPlayerLoopCallbacks'::`2'::UpdateScriptRunDelayedDynamicFrameRateRegistrator::Forward
0x00007FF7935963D8 (Unity) ExecutePlayerLoop
0x00007FF7935964AD (Unity) ExecutePlayerLoop
0x00007FF79359B6F4 (Unity) PlayerLoop
0x00007FF7919A799B (Unity) PlayerLoopController::UpdateScene
0x00007FF7919A58D8 (Unity) Application::TickTimer
0x00007FF7922ECF80 (Unity) MainMessageLoop
0x00007FF7922F6D1A (Unity) WinMain
0x00007FF7952895E2 (Unity) __scrt_common_main_seh
0x00007FFD48877BD4 (KERNEL32) BaseThreadInitThunk
0x00007FFD48B4CED1 (ntdll) RtlUserThreadStart

========== END OF STACKTRACE ===========


Is there a reason why you're calling Duplicate in the first place?

I wrote a thread about this just yesterday and it dawned upon me that I didn't actually want or need to use Duplicate.


The short of it is that instead of calling Duplicate do the following - via script:
- access your Substance Graph
- set your parameters (ie. $randomseed)
- generate the textures
- get those generated textures and copy them using Graphics.CopyTexture (see
- set the copied textures to your material

Doing it this way keeps the SubstanceGraph minimal and I can then treat it as a factory for procedurally creating textures based on whatever parameters I want to set when I need them.

It seems to me the only reason why I want to have multiple instances inside the SubstanceGraph (either from Duplicate or from manually creating them) is if I have some notion of a well-defined set of textures that I always want to generate.

Note that CopyTexture is pretty tedious, as in, you'll want to use the constructor that specifies not just the width/length, but also the format and mipmap count and so on. 

Not sure why there isn't a CopyTexture method that just takes another texture and returns you back a copy with everything set for you.

Hopefully that helps you out...

Last Edit: June 08, 2020, 04:09:00 pm

Thanks mate!

yes we are going to do just that, generate textures and copy them.
just didn't managed to find correct script to assign the 3 textures to correct material indexes yet.

and be sure to have all substanceGraph cleaned up after that.
Do you use .Substance.Free() ?:

Code: [Select]
var list = Substance.Game.Substance.ListLoadedAssetPaths();
foreach (var sub in list)

Nope, haven't seen Free().  What does it do?  Unfortunately the API docs are abysmal.

Also ran into another issue yesterday - having do with the rendering of the textures (sync or async).

Unfortunately there doesn't seem to be a way to detect when Substance is done rendering a texture when using RenderAsync().  Which kind of makes it useless from a runtime perspective.

The RenderSync() doesn't seem to be a sync call at all - it doesn't block... so it's pretty useless.

So now I'm restructuring things so that the texture generation is done inside a coroutine so that I can ping the GetGeneratedTextures() and see if there are new textures in there before trying to set them onto a material. 

when using RenderAsync, i think you need to use
Code: [Select]
Substance.IsJobCompleted(uint)..but when i tried it never get out of the loop...

and as you said api doc is ... mostly void

RenderSync does bloc though.

By chance, do you have your code snippet where you copy the material and generated textures ?
can't find anything clean .

i got 3 textures as output, but do not know how to assign them to the correct textureid in the new material.
i would loike to make it generic enough so that i don't have to seaarch if the texture name contains "diffuse" or "metallic" to assign into the corresponding mat.

something like that (but this doesn't work ^^)
Code: [Select]
public void CopyTextures()
List<int> texturesIds = new List<int>();

//var orignalTextures = Graph.GetGeneratedTextures();
_textures = new List<Texture2D>();
for (int i = 0; i < texturesIds.Count; ++i)
Texture2D t = Graph.material.GetTexture(i) as Texture2D;
if (t != null)
Texture2D newTex = new Texture2D(t.width, t.height, t.format, t.mipmapCount > 0);
Graphics.CopyTexture(t, newTex);
_mat.SetTexture(texturesIds[i], newTex);

//Texture2D t = orignalTextures[i];

damn you were right the RenderSync is not sync at all -_-...
gonna go your way with ResetGeneratedTextures(); and testing GetGeneratedTextures().Count

 :-X :-X :-X

Here's a sample of my code to find the textures that you want based on the name and make a copy of them:

Code: [Select]
        public void SetFarViewTextures(List<Texture2D> textures) {
            var diffuseTex = textures.FirstOrDefault(texture =>"diffuse"));
            var normalTex = textures.FirstOrDefault(texture =>"any"));
            FarDiffuseTex = new Texture2D(diffuseTex.width, diffuseTex.height, diffuseTex.format, diffuseTex.mipmapCount, true);
            FarNormalTex = new Texture2D(normalTex.width, normalTex.height, normalTex.format, normalTex.mipmapCount, true);

            Graphics.CopyTexture(diffuseTex, FarDiffuseTex);
            Graphics.CopyTexture(normalTex, FarNormalTex);