AssetHandlers are responsible for loading and destroying assets
when the asset manager requests it.
To create a handler for a specific asset type, derive from this class
and register an instance of the handler with the asset manager.
Asset handling functions may be called from multiple threads, so the
handlers need to be thread-safe.
It is ok for the handler to block the calling thread during the actual
asset load.
NOTE! Because it doesn't go without saying:
It is NOT OK for an AssetHandler to queue work for another thread and block
on that work being finished, in the case that that thread is the same one doing
the blocking. That will result in a single thread deadlock.
If you need to queue work, the logic needs to be similar to this:
AssetHandler::LoadResult MyAssetHandler::LoadAssetData(const Asset<AssetData>& asset, AZStd::shared_ptr<AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB)
{
.
.
.
if (AZStd::this_thread::get_id() == m_loadingThreadId)
{
load asset immediately } else { queue job to load asset in thread identified by m_loadingThreadId auto* queuedJob = QueueLoadingOnOtherThread(...);
block waiting for queued job to complete queuedJob->BlockUntilComplete(); }
}
Called when an asset requested to load is actually missing from the catalog when we are trying to resolve it from an ID to a file name and other streaming info. Here, optionally, you can return a non-empty asset ID for it to try to use that as fallback data instead. Providing it with a non-empty assetId will cause it to attach the handler to the file data for that asset instead, but still retain the original assetId for the loaded asset. This allows you to perform simple 'placeholder' substitution for assets that are missing, errored, or still being compiled. If you need your system to do something more complicated than simple substitution, the place for that is in the component entity class that requested the load in the first place. This API is just for basic substitution cases.