381 lines
16 KiB
C++
381 lines
16 KiB
C++
// 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_
|