skip to main content

Patch Content Early – NOT At Runtime

PatchContentEarly

I’m definitely in favour of allowing flexibility to game developers. I’d like to see a day where artists and designers don’t need to worry about end-platform performance. Sometimes, though, we can nudge things in the right direction.

Rules and guidelines are often laid out in development guides given to the development team… things like:-

  • Don’t use more than 8 textures on a material;
  • Don’t use textures bigger than 4096 x 4096;
  • Don’t use more than 80 bones on your characters;
  • Don’t use spaces in the “name” parameters on objects.

That last one is very common – and most old-school artists would follow this one regardless of whether it’s a restriction in the modern day or not. Personally, I prefer to see most of the restrictions taken out of guideline manuals by either:-

  • Fixing the root of the problem – eg. remove the restriction on spaces within names;
  • Not allowing the problem to happen – eg. don’t allow the content creator to add a space to names (note: existing content would still potentially need patching or fixing up);
  • Patching the problem – eg. convert the space to a “safe” character such as a dash (note: this can cause new issues – you could cause a duplicate and all manner of problems this way);
  • Warning the content creator about the problem – when the name is input, in a map check, at runtime…

Obviously, if you’re going to patch the problem or warn about it, ideally you should be doing that at the earliest point. Doing it at runtime, even when the patch looks simple, can cause performance problems.

So, leading into the finding for this report, we found two places in the Unreal Engine codebase where we were seeing very large numbers of allocations through string operations where bone names were being patched up if they contained spaces. Note that our project is very animation and character heavy – if yours isn’t, don’t expect to see the same ‘problems’ that we did.

bool FBoneReference::Initialize(const FBoneContainer& RequiredBones)
{
  BoneName = *BoneName.ToString().Trim().TrimTrailing();
  BoneIndex = RequiredBones.GetPoseBoneIndexForBoneName(BoneName);

And the change:-

bool FBoneReference::Initialize(const FBoneContainer& RequiredBones)
{
  check(!FString(BoneName.Contains(" ")));
  BoneIndex = RequiredBones.GetPoseBoneIndexForBoneName(BoneName);

The second optimization is with GetConvertedBoneName() in SkeletalMeshComponentPhysics.cpp. Again, Epic’s optimizations have helped somewhat. Currently, before optimizing, we were seeing around 60,000 short-term allocations (3.6mb) per minute.

// convert a bone name from APEX stype to FBX style
static FName GetConvertedBoneName(NxClothingAsset* ApexClothingAsset, int32 BoneIndex)
{
  return *FString(ApexClothingAsset->getBoneName(BoneIndex)).Replace(TEXT(" "), TEXT("-"));
}

Very simple to fix, we just change this to:-

// convert a bone name from APEX style to FBX style
static FName GetConvertedBoneName(NxClothingAsset* ApexClothingAsset, int32 BoneIndex)
{
  check(!FString(ApexClothingAsset->getBoneName(BoneIndex)).Contains(" "));
  return ApexClothingAsset->getBoneName(BoneIndex);
}

We also fixed up the code comment at the same time – the last time that I checked, APEX doesn’t support tampons (www.dictionary.com/browse/stype) – so converting from an “APEX stype” to “FBX style” doesn’t make sense at all. There’s another tampon reference in SkeletalMesh.cpp that could perhaps be plugged at the same time…

Hopefully, if these changes are picked up by Epic, they can add relevant editor-side or serialization code to fix/patch/warn about these earlier on. For our own project that’s not critical as the artists are pretty diligent…

Anyway, that’s it for this round! Please feel free to comment if you have a chance to try out this or any of our other optimizations – or even if you don’t.

Credit(s): Robert Troughton (Coconut Lizard)
Status: Currently unimplemented in 4.12

Facebook Messenger Twitter Pinterest Whatsapp Email
Go to Top