Skip to content

Commit e75a1cc

Browse files
committed
Fixed issues relating to string fetching where HAPI string handles are stale by the time they are used.
1 parent 5035f2d commit e75a1cc

File tree

2 files changed

+57
-59
lines changed

2 files changed

+57
-59
lines changed

Plugins/HoudiniEngineUnity/Scripts/Utility/HEU_GeneralUtility.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,13 @@ public static bool GetAttributeArray<T>(HAPI_NodeId geoID, HAPI_PartId partID, s
347347
{
348348
int maxArraySize = HEU_Defines.HAPI_MAX_PAGE_SIZE / (Marshal.SizeOf(typeof(T)) * info.tupleSize);
349349

350+
if (getFunc.GetInvocationList()[0].Method.Name == nameof(GetAttributeStringData))
351+
{
352+
// Disable paging on strings because multiple calls to HAPI to get strings can result in string handles changing
353+
// between each call.
354+
maxArraySize = count;
355+
}
356+
350357
int localCount = count;
351358
int currentIndex = 0;
352359

Plugins/HoudiniEngineUnity/Scripts/Utility/HEU_GenerateGeoCache.cs

Lines changed: 50 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,14 @@ public class HEU_GenerateGeoCache
7171

7272
public Dictionary<int, HEU_UnityMaterialInfo> _unityMaterialInfos;
7373
public HAPI_AttributeInfo _unityMaterialAttrInfo;
74-
public HAPI_StringHandle[] _unityMaterialAttrName;
74+
public int[] _unityMaterialAttrName;
7575

76-
public Dictionary<HAPI_StringHandle, string> _unityMaterialAttrStringsMap =
77-
new Dictionary<HAPI_StringHandle, string>();
76+
public List<string> _unityMaterialAttrStringsMap = new List<string>();
7877

7978
public HAPI_AttributeInfo _substanceMaterialAttrNameInfo;
80-
public HAPI_StringHandle[] _substanceMaterialAttrName;
79+
public int[] _substanceMaterialAttrName;
8180

82-
public Dictionary<HAPI_StringHandle, string> _substanceMaterialAttrStringsMap =
83-
new Dictionary<HAPI_StringHandle, string>();
81+
public List<string> _substanceMaterialAttrStringsMap = new List<string>();
8482

8583
public HAPI_AttributeInfo _substanceMaterialAttrIndexInfo;
8684
public int[] _substanceMaterialAttrIndex;
@@ -246,6 +244,37 @@ public static HEU_GenerateGeoCache GetPopulatedGeoCache(HEU_SessionBase session,
246244
return geoCache;
247245
}
248246

247+
public void GetStringAttributes(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string name,
248+
ref HAPI_AttributeInfo info, ref int[] stringIndices, ref List<string> stringTable)
249+
{
250+
// This a wrapper around HEU_GeneralUtility.GetAttribute(..., session.GetAttributeStringData)
251+
// which returns the strings in stringTable and an array stringIndices which reference these strings.
252+
// This is safer than storing HAPI string handles which can be transient in nature.
253+
254+
var stringHandles = new HAPI_StringHandle[0];
255+
HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, name, ref info, ref stringHandles, session.GetAttributeStringData);
256+
257+
var stringLookup = new Dictionary<HAPI_StringHandle, int>();
258+
259+
stringIndices = new int[info.count * info.tupleSize];
260+
261+
for (int index = 0; index < stringHandles.Length; index++)
262+
{
263+
HAPI_StringHandle strHandle = stringHandles[index];
264+
265+
// if we haven't encountered the string before, store it in the table and handle->string lookup dictionary.
266+
if (!stringLookup.ContainsKey(strHandle))
267+
{
268+
string hapString = HEU_SessionManager.GetString(strHandle, session);
269+
int nextFreeIndex = stringTable.Count;
270+
stringTable.Add(hapString);
271+
stringLookup[strHandle] = nextFreeIndex;
272+
}
273+
274+
stringIndices[index] = stringLookup[strHandle];
275+
}
276+
}
277+
249278
/// <summary>
250279
/// Parse and populate materials in use by part.
251280
/// </summary>
@@ -255,56 +284,21 @@ public void PopulateUnityMaterialData(HEU_SessionBase session)
255284
// We fill up the following dictionary with unique Unity + Substance material information
256285
_unityMaterialInfos = new Dictionary<int, HEU_UnityMaterialInfo>();
257286

258-
_unityMaterialAttrInfo = new HAPI_AttributeInfo();
259-
_unityMaterialAttrName = new HAPI_StringHandle[0];
260-
HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_PluginSettings.UnityMaterialAttribName,
261-
ref _unityMaterialAttrInfo, ref _unityMaterialAttrName, session.GetAttributeStringData);
287+
GetStringAttributes(session,
288+
GeoID, PartID,
289+
HEU_PluginSettings.UnityMaterialAttribName,
290+
ref _unityMaterialAttrInfo, ref _unityMaterialAttrName, ref _unityMaterialAttrStringsMap);
262291

263-
// Store a local copy of the actual string values since the indices get overwritten by the next call to session.GetAttributeStringData.
264-
// Using a dictionary to only query the unique strings, as doing all of them is very slow and unnecessary.
265-
_unityMaterialAttrStringsMap = new Dictionary<HAPI_StringHandle, string>();
266-
foreach (HAPI_StringHandle strHandle in _unityMaterialAttrName)
267-
{
268-
if (!_unityMaterialAttrStringsMap.ContainsKey(strHandle))
269-
{
270-
string materialName = HEU_SessionManager.GetString(strHandle, session);
271-
if (!string.IsNullOrEmpty(materialName))
272-
{
273-
_unityMaterialAttrStringsMap.Add(strHandle, materialName);
274-
}
275-
else
276-
{
277-
// There are some cases (e.g. LOD input), where a material attribute should be empty.
278-
// Warn user of empty string, but add it anyway to our map so we don't keep trying to parse it
279-
// HEU_Logger.LogWarningFormat("Found empty material attribute value for part {0}.", _partName);
280-
}
281-
}
282-
}
292+
GetStringAttributes(session,
293+
GeoID, PartID,
294+
HEU_PluginSettings.UnitySubMaterialAttribName,
295+
ref _substanceMaterialAttrNameInfo, ref _substanceMaterialAttrName, ref _substanceMaterialAttrStringsMap);
283296

284297
_substanceMaterialAttrNameInfo = new HAPI_AttributeInfo();
285298
_substanceMaterialAttrName = new HAPI_StringHandle[0];
286299
HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_PluginSettings.UnitySubMaterialAttribName,
287300
ref _substanceMaterialAttrNameInfo, ref _substanceMaterialAttrName, session.GetAttributeStringData);
288301

289-
_substanceMaterialAttrStringsMap = new Dictionary<HAPI_StringHandle, string>();
290-
foreach (HAPI_StringHandle strHandle in _substanceMaterialAttrName)
291-
{
292-
if (!_substanceMaterialAttrStringsMap.ContainsKey(strHandle))
293-
{
294-
string substanceName = HEU_SessionManager.GetString(strHandle, session);
295-
if (string.IsNullOrEmpty(substanceName))
296-
{
297-
// Warn user of empty string, but add it anyway to our map so we don't keep trying to parse it
298-
HEU_Logger.LogWarningFormat(
299-
"Found invalid substance material attribute value ({0}) for part {1}.",
300-
_partName, substanceName);
301-
}
302-
303-
_substanceMaterialAttrStringsMap.Add(strHandle, substanceName);
304-
//HEU_Logger.LogFormat("Added Substance material: " + substanceName);
305-
}
306-
}
307-
308302
_substanceMaterialAttrIndexInfo = new HAPI_AttributeInfo();
309303
_substanceMaterialAttrIndex = new int[0];
310304
HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_PluginSettings.UnitySubMaterialIndexAttribName,
@@ -323,7 +317,7 @@ public void PopulateUnityMaterialData(HEU_SessionBase session)
323317
}
324318
else
325319
{
326-
for (HAPI_StringHandle i = 0; i < _unityMaterialAttrName.Length; ++i)
320+
for (int i = 0; i < _unityMaterialAttrName.Length; ++i)
327321
{
328322
CreateMaterialInfoEntryFromAttributeIndex(this, i);
329323
}
@@ -337,14 +331,13 @@ public static int GetMaterialKeyFromAttributeIndex(HEU_GenerateGeoCache geoCache
337331
unityMaterialName = null;
338332
substanceName = null;
339333
substanceIndex = -1;
340-
if (attributeIndex < geoCache._unityMaterialAttrName.Length &&
341-
geoCache._unityMaterialAttrStringsMap.TryGetValue(geoCache._unityMaterialAttrName[attributeIndex],
342-
out unityMaterialName))
334+
if (attributeIndex < geoCache._unityMaterialAttrName.Length)
343335
{
336+
unityMaterialName = geoCache._unityMaterialAttrStringsMap[geoCache._unityMaterialAttrName[attributeIndex]];
337+
344338
if (geoCache._substanceMaterialAttrNameInfo.exists && geoCache._substanceMaterialAttrName.Length > 0)
345339
{
346-
geoCache._substanceMaterialAttrStringsMap.TryGetValue(
347-
geoCache._substanceMaterialAttrName[attributeIndex], out substanceName);
340+
substanceName = geoCache._substanceMaterialAttrStringsMap[geoCache._substanceMaterialAttrName[attributeIndex]];
348341
}
349342

350343
if (geoCache._substanceMaterialAttrIndexInfo.exists && string.IsNullOrEmpty(substanceName) &&
@@ -353,8 +346,7 @@ public static int GetMaterialKeyFromAttributeIndex(HEU_GenerateGeoCache geoCache
353346
substanceIndex = geoCache._substanceMaterialAttrIndex[attributeIndex];
354347
}
355348

356-
return HEU_MaterialFactory.GetUnitySubstanceMaterialKey(unityMaterialName, substanceName,
357-
substanceIndex);
349+
return HEU_MaterialFactory.GetUnitySubstanceMaterialKey(unityMaterialName, substanceName, substanceIndex);
358350
}
359351

360352
return HEU_Defines.HEU_INVALID_MATERIAL;
@@ -366,8 +358,7 @@ public static void CreateMaterialInfoEntryFromAttributeIndex(HEU_GenerateGeoCach
366358
string unityMaterialName = null;
367359
string substanceName = null;
368360
int substanceIndex = -1;
369-
int materialKey = GetMaterialKeyFromAttributeIndex(geoCache, materialAttributeIndex, out unityMaterialName,
370-
out substanceName, out substanceIndex);
361+
int materialKey = GetMaterialKeyFromAttributeIndex(geoCache, materialAttributeIndex, out unityMaterialName, out substanceName, out substanceIndex);
371362
if (!geoCache._unityMaterialInfos.ContainsKey(materialKey))
372363
{
373364
geoCache._unityMaterialInfos.Add(materialKey,

0 commit comments

Comments
 (0)