Files
FC1/CryCommon/IStreamEngine.h
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

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_