/* -------------------------------------------------------------- AssetGuid -------------------------------------------------------------- */ typedef mxUUID AssetGuid; bool AssetGuid_CreateNew( AssetGuid & newGuid ); bool AssetGuid_ToString( const AssetGuid& assetId, char *buffer, UINT bufferSize ); bool AssetGuid_IsNull( const AssetGuid& assetId ); bool AssetGuid_IsValid( const AssetGuid& assetId ); bool AssetGuids_AreEqual( const AssetGuid& assetIdA, const AssetGuid& assetIdB ); AssetGuid AssetGuid_GetNull(); /* ----------------------------------------------------------------------------- AFilePackage resource/asset data loader interface (resource/content provider) ----------------------------------------------------------------------------- */ struct AFilePackage : public TSinglyLinkedList< AFilePackage > { // low-level real-only file stream //NOTE: this structure must be bitwise-copyable struct Stream : DbgNamedObject< FS_MAX_PATH_CHARS > { // these fields are for internal use only! // Don't touch them! UINT64 fileHandle; UINT uncompressedSize; UINT sortKey; // for sorting by file offsets UINT id; // internal stream id public: Stream(); }; //[must be threadsafe] virtual bool OpenFile( const AssetGuid& fileId, Stream *stream ) = 0; //[must be threadsafe] virtual void CloseFile( Stream * stream ) = 0; // Reads the file into the preallocated buffer //[must be threadsafe] virtual SizeT ReadFile( Stream * stream, UINT startOffset, void *buffer, UINT bytesToRead ) = 0; protected: virtual ~AFilePackage() {} }; /* -------------------------------------------------------------- FileSystem virtual file system (based on package files), it simply provides access to individual files by their ids. -------------------------------------------------------------- */ class FileSystem : SingleInstance< FileSystem > { AFilePackage::Head m_packages; // linked list of mounted packages public: FileSystem(); ~FileSystem(); bool MountPackage( AFilePackage* package ); void UnmountPackage( AFilePackage* package ); //[must be threadsafe] AFilePackage* OpenFile( const AssetGuid& fileId, AFilePackage::Stream *stream ); }; // Load request priority. enum ELoadPriority { Load_Priority_Low = 0, Load_Priority_Normal = 64, Load_Priority_High = 128, Load_Priority_Critical = 255, Load_Priority_MAX, }; enum { Load_Priority_Collision_Hull = Load_Priority_Critical, Load_Priority_Animation = Load_Priority_Critical, Load_Priority_Sound = Load_Priority_High, Load_Priority_Music = Load_Priority_Normal, Load_Priority_Texture = Load_Priority_Low, }; enum ELoadStatus { LoadStatus_HadErrors = 0, LoadStatus_Loaded, LoadStatus_Unloaded, LoadStatus_Pending, LoadStatus_Cancelled, }; // load request id, could be 64-bit to prevent wraparound typedef UINT LoadRequestId; static const LoadRequestId INVALID_LOAD_REQUEST_ID = ~0UL; // parameters for loading data asynchronously in a background thread struct SLoadContext { void * buffer; UINT size; void * userData; LoadRequestId requestId; //AssetGuid assetId; }; // parameters for instantiating a 'live' resource object ('asset instance') in the main thread struct SFinalizeContext { void * buffer; UINT size; void * userData; LoadRequestId requestId; //AssetGuid assetId; }; // this is called in the background loading thread // when the data has been read into the buffer - must be thread-safe! // (e.g. file parsing, deserialization stuff, pointer fixups) typedef void F_AsyncLoad( const SLoadContext& context ); // this is called in the main thread after F_AsyncLoad() // (e.g. creating live resource objects from parsed file data) // NOTE: added another parameter to avoid typecasting errors with F_AsyncLoad() typedef void F_Finalize( const SFinalizeContext& context ); // resource load request (main thread => resource loading thread) // represents a request for loading raw asset data bytes // from which a 'live' asset object will be constructed struct SLoadRequest { AssetGuid assetId; // filename void * buffer; // output buffer UINT bytesToRead; // if 0, then the entire file will be read ELoadPriority priority; //callbacks void * userData; F_AsyncLoad * asyncLoad; //<= must never be null F_Finalize * finalize; //<= must never be null public: SLoadRequest(); }; bool SLoadRequest_CheckValid( const SLoadRequest& o ); /* -------------------------------------------------------------- AsyncReader creates a separate 'streaming' thread for background resource loading and decompression -------------------------------------------------------------- */ class AsyncReader : SingleInstance< AsyncReader > { public: // queued asynchronous data load request //NOTE: this structure must be bitwise-copyable struct SPendingRequest { AFilePackage * package; AFilePackage::Stream stream; ELoadPriority priority; void * buffer; // output buffer (non-null if user supplied) UINT bytesToRead; //callbacks void * userData; F_AsyncLoad * asyncLoad; // called in the streaming thread F_Finalize * finalize; // called in the main thread LoadRequestId uid; // unique id }; typedef TArray< SPendingRequest, UINT > PendingRequests; //NOTE: this structure must be bitwise-copyable struct SCompletedRequest { void * buffer; UINT bytesRead; //callbacks void * userData; F_Finalize * finalize; // called in the main thread LoadRequestId uid; // unique id public: //this is called in the main thread inline void Finalize() { SFinalizeContext args; { args.buffer = buffer; args.size = bytesRead; args.userData = userData; args.requestId = uid; //args.assetId = assetId; } (*finalize)( args ); } }; typedef TArray< SCompletedRequest, UINT > CompletedRequests; private: PendingRequests m_pendingRequests; // queued load requests SpinWait m_pendingRequestsCS;// for serializing access to the load queue CompletedRequests m_completedRequests;// executed load requests SpinWait m_completedRequestsCS; Thread m_IOThread; // worker thread for asynchronous I/O EventFlag m_wakeupEvent; // tells the thread that there's work to do EventFlag m_workDoneEvent;// signals that the thread has processed all items volatile BOOL m_isRunning; // this flag is used to stop the I/O thread LoadRequestId m_lastRequestId; // for generating unique ids public: AsyncReader(); ~AsyncReader(); void Initialize(); //[main thread only] void Shutdown(); //[main thread only] bool AddLoadRequest( const SLoadRequest& request, LoadRequestId *id = nil ); bool Cancel( LoadRequestId requestId ); //[main thread only] bool WaitFor( LoadRequestId requestId, UINT timeOutMilliseconds = INFINITE ); void CancelAll(); void WaitForAll( UINT milliseconds = INFINITE ); void FinalizeCompletedRequests( UINT maxNumRequests = 0 ); private: //NOTE: the returned pointers cannot be cached SPendingRequest* FindPendingRequest_NoLock( LoadRequestId requestId ); SCompletedRequest* FindCompletedRequest_NoLock( LoadRequestId requestId ); void SortQueuedRequests_NoLock(); static UINT32 PASCAL StaticResourceLoaderThread( void* userData ); UINT32 ResourceLoaderThread(); };