123
This commit is contained in:
380
CryCommon/IStreamEngine.h
Normal file
380
CryCommon/IStreamEngine.h
Normal file
@@ -0,0 +1,380 @@
|
||||
// This is the prototypes of interfaces that will be used for asynchronous
|
||||
// I/O (streaming).
|
||||
// THIS IS NOT FINAL AND IS SUBJECT TO CHANGE WITHOUT NOTICE
|
||||
|
||||
// Some excerpts explaining basic ideas behind streaming design here:
|
||||
|
||||
/*
|
||||
* The idea is that the data loaded is ready for usage and ideally doesn't need further transformation,
|
||||
* therefore the client allocates the buffer (to avoid extra copy). All the data transformations should take place in the Resource Compiler. If you have to allocate a lot of small memory objects, you should revise this strategy in favor of one big allocation (again, that will be read directly from the compiled file).
|
||||
* Anyway, we can negotiate that the streaming engine allocates this memory.
|
||||
* In the end, it could make use of a memory pool, and copying data is not the bottleneck in our engine
|
||||
*
|
||||
* The client should take care of all fast operations. Looking up file size should be fast on the virtual
|
||||
* file system in a pak file, because the directory should be preloaded in memory
|
||||
*/
|
||||
#ifndef _CRY_COMMON_STREAM_ENGINE_HDR_
|
||||
#define _CRY_COMMON_STREAM_ENGINE_HDR_
|
||||
|
||||
#include "smartptr.h"
|
||||
|
||||
struct StreamEngineParams;
|
||||
class IStreamCallback;
|
||||
class ICrySizer;
|
||||
|
||||
enum
|
||||
{
|
||||
ERROR_UNKNOWN_ERROR = 0xF0000000,
|
||||
ERROR_UNEXPECTED_DESTRUCTION = 0xF0000001,
|
||||
ERROR_INVALID_CALL = 0xF0000002,
|
||||
ERROR_CANT_OPEN_FILE = 0xF0000003,
|
||||
ERROR_REFSTREAM_ERROR = 0xF0000004,
|
||||
ERROR_OFFSET_OUT_OF_RANGE = 0xF0000005,
|
||||
ERROR_REGION_OUT_OF_RANGE = 0xF0000006,
|
||||
ERROR_SIZE_OUT_OF_RANGE = 0xF0000007,
|
||||
ERROR_CANT_START_READING = 0xF0000008,
|
||||
ERROR_OUT_OF_MEMORY = 0xF0000009,
|
||||
ERROR_ABORTED_ON_SHUTDOWN = 0xF000000A,
|
||||
ERROR_OUT_OF_MEMORY_QUOTA = 0xF000000B,
|
||||
ERROR_ZIP_CACHE_FAILURE = 0xF000000C,
|
||||
ERROR_USER_ABORT = 0xF000000D
|
||||
};
|
||||
|
||||
|
||||
// these are the flags for the StreamReadParams structure
|
||||
enum StreamReadParamsFlagEnum
|
||||
{
|
||||
// if this flag is set, the callback can be called from within stream engine's worker thread
|
||||
// WARNING: Use with care
|
||||
SRP_FLAGS_ASYNC_CALLBACK = 1,
|
||||
|
||||
// If this flag is set, the file will be read synchronously
|
||||
// NOTE: Not implemented yet
|
||||
SRP_FLAGS_SYNC_READ = 1 << 1,
|
||||
|
||||
// if this flag is set, the stream will be treated as "permanent" and the file handle will
|
||||
// be cached. This is needed for files which are accessed frequently, e.g. Resource files.
|
||||
SRP_FLAGS_MAKE_PERMANENT = 1<<2,
|
||||
|
||||
// if this flag is set and the stream was made permanent before (either explicitly because of
|
||||
// the SRP_FLAGS_MAKE_PERMANENT flag, or implicitly because of the policy of the StreamEngine),
|
||||
// the stream will be removed as soon as the last proxy will be released.
|
||||
SRP_FLAGS_MAKE_TRANSIENT = 1 << 3,
|
||||
|
||||
// with this flag, the progress routine will be called asynchronously
|
||||
SRP_FLAGS_ASYNC_PROGRESS = 1 << 4,
|
||||
|
||||
// this means that the path passed to StartRead is real path, and shouldn't undergo
|
||||
// adjustments through mod searching mechanics
|
||||
SRP_FLAGS_PATH_REAL = 1 << 5,
|
||||
|
||||
// if this is set, it is adviced that Update(0) be called during startread, which
|
||||
// can effectively call the callback before StartRead returns
|
||||
// SRP_IMMEDIATE_UPDATE and SRP_QUICK_RETURN are mutually exclusive
|
||||
SRP_IMMEDIATE_UPDATE = 1 << 6,
|
||||
|
||||
// if this is set, it is adviced that Update(0) not be called during StartRead,
|
||||
// which yields quicker response.
|
||||
// SRP_IMMEDIATE_UPDATE and SRP_QUICK_RETURN are mutually exclusive
|
||||
SRP_QUICK_STARTREAD = 1 << 7
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// this is used as parameter to the asynchronous read function
|
||||
// all the unnecessary parameters go here, because there are many of them
|
||||
struct StreamReadParams
|
||||
{
|
||||
StreamReadParams (
|
||||
//const char* _szFile,
|
||||
//IStreamCallback* _pCallback,
|
||||
DWORD_PTR _dwUserData = 0,
|
||||
int _nPriority = 0,
|
||||
unsigned _nLoadTime = 0,
|
||||
unsigned _nMaxLoadTime = 0,
|
||||
unsigned _nOffset = 0,
|
||||
unsigned _nSize = 0,
|
||||
void* _pBuffer = NULL,
|
||||
unsigned _nFlags = 0
|
||||
):
|
||||
//szFile (_szFile),
|
||||
//pCallback(_pCallback),
|
||||
dwUserData (_dwUserData),
|
||||
nPriority(_nPriority),
|
||||
nLoadTime(_nLoadTime),
|
||||
nMaxLoadTime(_nMaxLoadTime),
|
||||
pBuffer (_pBuffer),
|
||||
nOffset (_nOffset),
|
||||
nSize (_nSize),
|
||||
nFlags (_nFlags)
|
||||
{
|
||||
}
|
||||
|
||||
// file name
|
||||
//const char* szFile;
|
||||
// the callback
|
||||
//IStreamCallback* pCallback;
|
||||
// the user data that'll be used to call the callback
|
||||
DWORD_PTR dwUserData;
|
||||
|
||||
// the priority of this read; INT_MIN is the idle, INT_MAX is the highest, 0 is the average
|
||||
int nPriority;
|
||||
|
||||
// the desirable loading time, in milliseconds, from the time of call
|
||||
// 0 means as fast as possible (desirably in this frame)
|
||||
unsigned nLoadTime;
|
||||
|
||||
// the maximum load time, in milliseconds. 0 means forever. If the read lasts longer, it can be discarded.
|
||||
// WARNING: avoid too small max times, like 1-10 ms, because many loads will be discarded in this case.
|
||||
unsigned nMaxLoadTime;
|
||||
|
||||
// the buffer into which to read the file or the file piece
|
||||
// if this is NULL, the streaming engine will supply the buffer
|
||||
// DO NOT USE THIS BUFFER during read operation! DO NOT READ from it, it can lead to memory corruption!
|
||||
void* pBuffer;
|
||||
|
||||
// offset in the file to read; if this is not 0, then the file read
|
||||
// occurs beginning with the specified offset in bytes.
|
||||
// the callback interface receives the size of already read data as nSize
|
||||
// and generally behaves as if the piece of file would be a file of its own.
|
||||
unsigned nOffset;
|
||||
|
||||
// number of bytes to read; if this is 0, then the whole file is read
|
||||
// if nSize == 0 && nOffset != 0, then the file from the offset to the end is read
|
||||
// If nSize != 0, then the file piece from nOffset is read, at most nSize bytes
|
||||
// (if less, an error is reported). So, from nOffset byte to nOffset + nSize - 1 byte in the file
|
||||
unsigned nSize;
|
||||
|
||||
// the combination of one or several flags from StreamReadParamsFlagEnum
|
||||
unsigned nFlags;
|
||||
};
|
||||
|
||||
class IReadStream;
|
||||
//typedef IReadStream_AutoPtr auto ptr wrapper
|
||||
TYPEDEF_AUTOPTR(IReadStream);
|
||||
typedef IReadStream_AutoPtr IReadStreamPtr;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// The highest level. THere is only one StreamingEngine in the application
|
||||
// and it controls all I/O streams.
|
||||
struct IStreamEngine
|
||||
{
|
||||
public:
|
||||
// general purpose flags
|
||||
enum
|
||||
{
|
||||
// if this is set in the call to Update, the time quota for callbacks is ignored,
|
||||
// and all the callbacks for which the data is available are called
|
||||
FLAGS_DISABLE_CALLBACK_TIME_QUOTA = 1
|
||||
};
|
||||
|
||||
// Starts asynchronous read from the specified file (the file may be on a
|
||||
// virtual file system, in pak or zip file or wherever).
|
||||
// Reads the file contents into the given buffer, up to the given size.
|
||||
// Upon success, calls success callback. If the file is truncated or for other
|
||||
// reason can not be read, calls error callback. THe callback can be NULL (in this case, the client should poll
|
||||
// the returned IReadStream object; the returned object must be locked for that)
|
||||
// NOTE: the error/success/ progress callbacks can also be called from INSIDE this function.
|
||||
// pParams - PLACEHOLDER for the future additional parameters (like priority), or really
|
||||
// a pointer to a structure that will hold the parameters if there are too many of them
|
||||
//
|
||||
// IReadStream is reference-counted and will be automatically deleted if you don't refer to it;
|
||||
// If you don't store it immediately in an auto-pointer, it may be deleted as soon as on the next line of code,
|
||||
// because the read operation may complete immediately inside StartRead() and the object is self-disposed
|
||||
// as soon as the callback is called
|
||||
//
|
||||
// in some implementations disposal of the old pointers happen synchronously
|
||||
// (in the main thread) outside StartRead() (it happens in the entity update),
|
||||
// so you're guaranteed that it won't trash inside the calling function. However, this may change in the future
|
||||
// and you'll be required to assign it to IReadStream immediately (StartRead will return IReadStream_AutoPtr then)
|
||||
virtual IReadStreamPtr StartRead (const char* szSource, const char* szFile, IStreamCallback* pCallback = NULL, StreamReadParams* pParams = NULL) = 0;
|
||||
|
||||
// returns the size of the file; returns 0 if there's no such file.
|
||||
// nCryPakFlags is the flag set as in ICryPak
|
||||
virtual unsigned GetFileSize (const char* szFile, unsigned nCryPakFlags = 0) = 0;
|
||||
|
||||
// waits at most the specified number of milliseconds, or until at least one pending operation is completed
|
||||
// nFlags: may have the following flag set:
|
||||
// FLAGS_DISABLE_CALLBACK_TIME_QUOTA
|
||||
virtual void Update (unsigned nFlags = 0) = 0;
|
||||
|
||||
// wait at most the specified time for the IO jobs to be completed.
|
||||
// Returns the number of jobs that actually were completed (finalized) during the call.
|
||||
// It may be different from the number of executed jobs.
|
||||
virtual unsigned Wait(unsigned nMilliseconds, unsigned nFlags = 0) = 0;
|
||||
|
||||
//! Puts the memory statistics into the given sizer object
|
||||
//! According to the specifications in interface ICrySizer
|
||||
virtual void GetMemoryStatistics(ICrySizer *pSizer) = 0;
|
||||
|
||||
//! Enables or disables callback time quota per frame
|
||||
virtual void SuspendCallbackTimeQuota(){}
|
||||
virtual void ResumeCallbackTimeQuota(){}
|
||||
|
||||
//! lossy stream compression useful for network comunication (affects load/save as well)
|
||||
//! /return 0=no compression
|
||||
virtual DWORD GetStreamCompressionMask() const=0;
|
||||
|
||||
virtual ~IStreamEngine() {}
|
||||
};
|
||||
|
||||
|
||||
class AutoSuspendTimeQuota
|
||||
{
|
||||
public:
|
||||
AutoSuspendTimeQuota(IStreamEngine* pStreamEngine)
|
||||
{
|
||||
m_pStreamEngine = pStreamEngine;
|
||||
pStreamEngine->SuspendCallbackTimeQuota();
|
||||
}
|
||||
|
||||
~AutoSuspendTimeQuota()
|
||||
{
|
||||
m_pStreamEngine->ResumeCallbackTimeQuota();
|
||||
}
|
||||
protected:
|
||||
IStreamEngine* m_pStreamEngine;
|
||||
};
|
||||
|
||||
|
||||
class AutoResumeTimeQuota
|
||||
{
|
||||
public:
|
||||
AutoResumeTimeQuota(IStreamEngine* pStreamEngine)
|
||||
{
|
||||
m_pStreamEngine = pStreamEngine;
|
||||
pStreamEngine->ResumeCallbackTimeQuota();
|
||||
}
|
||||
|
||||
~AutoResumeTimeQuota()
|
||||
{
|
||||
m_pStreamEngine->SuspendCallbackTimeQuota();
|
||||
}
|
||||
protected:
|
||||
IStreamEngine* m_pStreamEngine;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// This is the file "handle" that can be used to query the status
|
||||
// of the asynchronous operation on the file. The same object may be returned
|
||||
// for the same file to multiple clients.
|
||||
//
|
||||
// It will actually represent the asynchronous object in memory, and will be
|
||||
// thread-safe reference-counted (both AddRef() and Release() will be virtual
|
||||
// and thread-safe, just like the others)
|
||||
// USE:
|
||||
// IReadStream_AutoPtr pReadStream = pStreamEngine->StartRead ("bla.xxx", this);
|
||||
// OR:
|
||||
// pStreamEngine->StartRead ("MusicSystem","bla.xxx", this);
|
||||
class IReadStream: public _reference_target_MT
|
||||
{
|
||||
public:
|
||||
// returns true if the file read was not successful.
|
||||
virtual bool IsError() = 0;
|
||||
// returns true if the file read was completed successfully
|
||||
// check IsError to check if the whole requested file (piece) was read
|
||||
virtual bool IsFinished() = 0;
|
||||
// returns the number of bytes read so far (the whole buffer size if IsFinished())
|
||||
// if bWait == true, then waits until the pending I/O operation completes
|
||||
// returns the total number of bytes read (if it completes successfully, returns the size of block being read)
|
||||
virtual unsigned int GetBytesRead(bool bWait=false) = 0;
|
||||
// returns the buffer into which the data has been or will be read
|
||||
// at least GetBytesRead() bytes in this buffer are guaranteed to be already read
|
||||
// DO NOT USE THIS BUFFER during read operation! DO NOT READ from it, it can lead to memory corruption!
|
||||
virtual const void* GetBuffer () = 0;
|
||||
|
||||
// tries to stop reading the stream; this is advisory and may have no effect
|
||||
// but the callback will not be called after this. If you just destructing object,
|
||||
// dereference this object and it will automatically abort and release all associated resources.
|
||||
virtual void Abort() {}
|
||||
|
||||
// tries to raise the priority of the read; this is advisory and may have no effect
|
||||
virtual void RaisePriority (int nPriority) {}
|
||||
|
||||
// Returns the transparent DWORD that was passed in the StreamReadParams::dwUserData field
|
||||
// of the structure passed in the call to IStreamEngine::StartRead
|
||||
virtual DWORD_PTR GetUserData() = 0;
|
||||
|
||||
// unconditionally waits until the callback is called
|
||||
// i.e. if the stream hasn't yet finish, it's guaranteed that the user-supplied callback
|
||||
// is called before return from this function (unless no callback was specified)
|
||||
virtual void Wait() = 0;
|
||||
protected:
|
||||
// the clients are not allowed to destroy this object directly; only via Release()
|
||||
virtual ~IReadStream() {}
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// the callback that will be called by the streaming engine
|
||||
// must be implemented by all clients that want to use StreamingEngine services
|
||||
// NOTE:
|
||||
// the pStream interface is guaranteed to be locked (have reference count > 0)
|
||||
// while inside the function, but can vanish any time outside the function.
|
||||
// If you need it, keep it from the beginning (after call to StartRead())
|
||||
// some or all callbacks MAY be called from inside IStreamEngine::StartRead()
|
||||
class IStreamCallback
|
||||
{
|
||||
public:
|
||||
// For some applications, even partially loaded data can be useful.
|
||||
// To have this callback possibly called, specify the corresponding flag in IStreamEngine::StartRead()
|
||||
// This callback signals about finish of reading of nSize bytes to the specified buffer pData
|
||||
// so the client can read that much data inside this callback
|
||||
// NOTE:
|
||||
// The default implementation is {} so the clients need not implement it if they don't care
|
||||
// This callback is not guaranteed to be called.
|
||||
// If this callback is called, it doesn't mean the data load will finish successfully;
|
||||
// it still may not finish (then the OnError is called)
|
||||
// nSize is always LESS than the requested data size; when it's equal, StreamOnFinish is called instead
|
||||
virtual void StreamOnProgress (IReadStream* pStream) {}
|
||||
|
||||
// signals that reading the requested data has completed (with or without error).
|
||||
// this callback is always called, whether an error occurs or not
|
||||
// pStream will signal either IsFinished() or IsError() and will hold the (perhaps partially) read data until this interface is released
|
||||
// GetBytesRead() will return the size of the file (the completely read buffer) in case of successful operation end
|
||||
// or the size of partially read data in case of error (0 if nothing was read)
|
||||
// Pending status is true during this callback, because the callback itself is the part of IO operation
|
||||
// nError == 0 : Success
|
||||
// nError != 0 : Error code
|
||||
virtual void StreamOnComplete (IReadStream* pStream, unsigned nError) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
enum StreamingStrategy
|
||||
{
|
||||
// First in-first out, the strategy which ignores priorities
|
||||
SS_FIFO,
|
||||
// Random order, ignoring priorities, but the sequence is chosen to read as fast as possible
|
||||
SS_FAST,
|
||||
// Inside the same priority class, first in - first out.
|
||||
// The highest priorities get serviced first
|
||||
SS_PRIORITIZED,
|
||||
// attempt to make read smooth, taking priorities into account
|
||||
SS_MULTISTREAM
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// this is the approximate parameter block for the streaming engine
|
||||
// set up
|
||||
struct StreamEngineParams
|
||||
{
|
||||
// the strategy to use when queuing clients
|
||||
StreamingStrategy nStrategy;
|
||||
// the stream capacity to use, at most N bytes per second
|
||||
// if read requests come faster than that, delay loading
|
||||
unsigned nMaxBytesPerSecond;
|
||||
// the maximum number of ticks (TICK is yet to be defined -
|
||||
// CPU clock, or mcs, or ms, or whatever) to spend inside
|
||||
// the callbacks per SECOND. If callbacks spend more than that,
|
||||
// delay callback execution until the next frame
|
||||
// The actual limit is per-frame, the streaming engine uses estimated
|
||||
// FPS to calculate the per-frame max callback time.
|
||||
unsigned nMaxCallbackTicksPerSecond;
|
||||
// the maximum allowable simultaneously open streams
|
||||
unsigned nMaxStreams;
|
||||
};
|
||||
|
||||
|
||||
#endif //_CRY_COMMON_STREAM_ENGINE_HDR_
|
||||
Reference in New Issue
Block a user