/*
--------------------------------------------------------------
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();
};