I am trying to automate some settings for new textures based on different criteria. I’ve hooked up a function to the ImportSubsystem->OnAssetPostImport callback which is working so far. I can see the texture and even log different properties on it, however as soon as I try to make any change on the texture’s parameters it causes the editor to crash.
Simplified function that still causes the crash:
void UMyAssetImportingSubsystem::OnAssetPostImport(UFactory* InFactory, UObject* InCreatedObject)
{
UTexture2D* Texture = static_cast<UTexture2D*>(InCreatedObject);
if(Texture)
{
Texture->SRGB = false;
}
}
The crash log is not helpful:
Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x000009cb13fe0000
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor
UnrealEditor
UnrealEditor
UnrealEditor
UnrealEditor
UnrealEditor
kernel32
ntdll
So I’ve dug a bit deeper and and I think the issues comes from the fact that texture imports go through the Interchange Manager and are asynchronously imported.
In the InterchangeManager OnAssetPostImport is triggered while the async task is still going on so edits to the asset being imported are not thread safe.
Not sure what the correct way around this issue is sadly and there’s no thread safe “pre-import” callback for me to inject into in the InterchangeManager 
I’ll keep poking at it but any thoughts would be much appreciated.
Huzzah! I found a workaround that seems to work well.
The class TextureCompiler has a callback “TexturePostCompileEvent” that runs when a texture is finished compiling. This happens after the async import finishes on the main thread so it is safe to make changes to the texture at that point.
That event is fired every time ANY texture is compiled however so rather than just subscribing to it once in my subsystem, I’m still using OnAssetPostImport to detect new asset imports and on texture imports I subscribe to the compile event, the unsubscribe as soon as the compile event finishes.
void UMyAssetImportSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
if(GEditor)
{
if(UImportSubsystem* ImportSubsystem = GEditor->GetEditorSubsystem<UImportSubsystem>())
{
OnAssetPostImportHandle = ImportSubsystem->OnAssetPostImport.AddUObject(this, &UMyAssetImportSubsystem::OnAssetPostImport);
}
}
}
void UMyAssetImportSubsystem::Deinitialize()
{
if(GEditor)
{
if(UImportSubsystem* ImportSubsystem = GEditor->GetEditorSubsystem<UImportSubsystem>())
{
ImportSubsystem->OnAssetPostImport.Remove(OnAssetPostImportHandle);
}
}
Super::Deinitialize();
}
void UMyAssetImportSubsystem::OnAssetPostImport(UFactory* InFactory, UObject* InCreatedObject)
{
if(UTexture2D* Texture = Cast<UTexture2D>(InCreatedObject))
{
if(!OnTexturePostCompileHandle.IsValid())
{
FTextureCompilingManager& TextureCompiler = FTextureCompilingManager::Get();
OnTexturePostCompileHandle = TextureCompiler.OnTexturePostCompileEvent().AddUObject(this, &UMyAssetImportSubsystem::OnTexturePostCompile);
}
}
}
void UMyAssetImportSubsystem::OnTexturePostCompile(const TArrayView<UTexture* const>& Textures)
{
for(UTexture* Texture : Textures)
{
Texture->SRGB = false; //Just an example change
}
FTextureCompilingManager& TextureCompiler = FTextureCompilingManager::Get();
TextureCompiler.OnTexturePostCompileEvent().Remove(OnTexturePostCompileHandle);
OnTexturePostCompileHandle.Reset();
}
2 Likes