forked from blender/blender
Campbell Barton
5035fbdd23
Added license headers based on the original LGPL files from: gitlab.kuleuven.be/rob-itasc
228 lines
9.9 KiB
C++
228 lines
9.9 KiB
C++
/* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
* Copyright 2009 Benoit Bolsee. */
|
|
|
|
/** \file
|
|
* \ingroup intern_itasc
|
|
*/
|
|
|
|
#ifndef CACHE_HPP_
|
|
#define CACHE_HPP_
|
|
|
|
#include <map>
|
|
|
|
namespace iTaSC {
|
|
|
|
#define CACHE_LOOKUP_TABLE_SIZE 128
|
|
#define CACHE_DEFAULT_BUFFER_SIZE 32768
|
|
#define CACHE_CHANNEL_EXTEND_SIZE 10
|
|
#define CACHE_MAX_ITEM_SIZE 0x3FFF0
|
|
|
|
/* macro to get the alignment gap after an item header */
|
|
#define CACHE_ITEM_GAPB(item) (unsigned int)(((size_t)item+sizeof(CacheItem))&(sizeof(void*)-1))
|
|
/* macro to get item data position, item=CacheItem pointer */
|
|
#define CACHE_ITEM_DATA_POINTER(item) (void*)((unsigned char*)item+sizeof(CacheItem)+CACHE_ITEM_GAPB(item))
|
|
/* macro to get item size in 32bit words from item address and length, item=CacheItem pointer */
|
|
#define CACHE_ITEM_SIZEW(item,length) (unsigned int)((sizeof(CacheItem)+CACHE_ITEM_GAPB(item)+(((length)+3)&~0x3))>>2)
|
|
/* macto to move from one item to the next, item=CacheItem pointer, updated by the macro */
|
|
#define CACHE_NEXT_ITEM(item) ((item)+(item)->m_sizeW)
|
|
#define CACHE_BLOCK_ITEM_ADDR(chan,buf,block) (&(buf)->m_firstItem+(((unsigned int)(block)<<chan->m_positionToBlockShiftW)+(buf)->lookup[block].m_offsetW))
|
|
#define CACHE_ITEM_ADDR(buf,pos) (&(buf)->m_firstItem+(pos))
|
|
#define CACHE_ITEM_POSITIONW(buf,item) (unsigned int)(item-&buf->m_firstItem)
|
|
|
|
typedef unsigned int CacheTS;
|
|
|
|
struct Timestamp
|
|
{
|
|
double realTimestamp;
|
|
double realTimestep;
|
|
CacheTS cacheTimestamp;
|
|
unsigned int numstep:8;
|
|
unsigned int substep:1;
|
|
unsigned int reiterate:1;
|
|
unsigned int cache:1;
|
|
unsigned int update:1;
|
|
unsigned int interpolate:1;
|
|
unsigned int dummy:19;
|
|
|
|
Timestamp() { memset(this, 0, sizeof(Timestamp)); }
|
|
};
|
|
|
|
/* utility function to return second timestamp to millisecond */
|
|
inline void setCacheTimestamp(Timestamp& timestamp)
|
|
{
|
|
if (timestamp.realTimestamp < 0.0 || timestamp.realTimestamp > 4294967.295)
|
|
timestamp.cacheTimestamp = 0;
|
|
else
|
|
timestamp.cacheTimestamp = (CacheTS)(timestamp.realTimestamp*1000.0+0.5);
|
|
}
|
|
|
|
|
|
/*
|
|
class Cache:
|
|
Top level class, only one instance of this class should exists.
|
|
A device (=constraint, object) uses this class to create a cache entry for its data.
|
|
A cache entry is divided into cache channels, each providing a separate buffer for cache items.
|
|
The cache channels must be declared by the devices before they can be used.
|
|
The device must specify the largest cache item (limited to 256Kb) so that the cache
|
|
buffer can be organized optimally.
|
|
Cache channels are identified by small number (starting from 0) allocated by the cache system.
|
|
Cache items are inserted into cache channels ordered by timestamp. Writing is always done
|
|
at the end of the cache buffer: writing an item automatically clears all items with
|
|
higher timestamp.
|
|
A cache item is an array of bytes provided by the device; the format of the cache item is left
|
|
to the device.
|
|
The device can retrieve a cache item associated with a certain timestamp. The cache system
|
|
returns a pointer that points directly in the cache buffer to avoid unnecessary copy.
|
|
The pointer is guaranteed to be pointer aligned so that direct mapping to C structure is possible
|
|
(=32 bit aligned on 32 systems and 64 bit aligned on 64 bits system).
|
|
|
|
Timestamp = rounded time in millisecond.
|
|
*/
|
|
|
|
struct CacheEntry;
|
|
struct CacheBuffer;
|
|
struct CacheItem;
|
|
struct CacheChannel;
|
|
|
|
class Cache
|
|
{
|
|
private:
|
|
/* map between device and cache entry.
|
|
Dynamically updated when more devices create cache channels */
|
|
typedef std::map<const void *, struct CacheEntry*> CacheMap;
|
|
CacheMap m_cache;
|
|
const CacheItem *getCurrentCacheItemInternal(const void *device, int channel, CacheTS timestamp);
|
|
|
|
public:
|
|
Cache();
|
|
~Cache();
|
|
/* add a cache channel, maxItemSize must be < 256k.
|
|
name : channel name, truncated at 31 characters
|
|
msxItemSize : maximum size of item in bytes, items of larger size will be rejected
|
|
return value >= 0: channel id, -1: error */
|
|
int addChannel(const void *device, const char *name, unsigned int maxItemSize);
|
|
|
|
/* delete a cache channel (and all associated buffers and items) */
|
|
int deleteChannel(const void *device, int channel);
|
|
/* delete all channels of a device and remove the device from the map */
|
|
int deleteDevice(const void *device);
|
|
/* removes all cache items, leaving the special item at timestamp=0.
|
|
if device=NULL, apply to all devices. */
|
|
void clearCacheFrom(const void *device, CacheTS timestamp);
|
|
|
|
/* add a new cache item
|
|
channel: the cache channel (as returned by AddChannel
|
|
data, length: the cache item and length in bytes
|
|
If data is NULL, the memory is allocated in the cache but not writen
|
|
return: error: NULL, success: pointer to item in cache */
|
|
void *addCacheItem(const void *device, int channel, CacheTS timestamp, void *data, unsigned int length);
|
|
|
|
/* specialized function to add a vector of double in the cache
|
|
It will first check if a vector exist already in the cache for the same timestamp
|
|
and compared the cached vector with the new values.
|
|
If all values are within threshold, the vector is updated but the cache is not deleted
|
|
for the future timestamps. */
|
|
double *addCacheVectorIfDifferent(const void *device, int channel, CacheTS timestamp, double *data, unsigned int length, double threshold);
|
|
|
|
/* returns the cache item with timestamp that is just before the given timestamp.
|
|
returns the data pointer or NULL if there is no cache item before timestamp.
|
|
On return, timestamp is updated with the actual timestamp of the item being returned.
|
|
Note that the length of the item is not returned, it is up to the device to organize
|
|
the data so that length can be retrieved from the data if needed.
|
|
Device can NULL, it will then just look the first channel available, useful to
|
|
test the status of the cache. */
|
|
const void *getPreviousCacheItem(const void *device, int channel, CacheTS *timestamp);
|
|
|
|
/* returns the cache item with the timestamp that is exactly equal to the given timestamp
|
|
If there is no cache item for this timestamp, returns NULL.*/
|
|
const void *getCurrentCacheItem(const void *device, int channel, CacheTS timestamp);
|
|
|
|
};
|
|
|
|
/* the following structures are not internal use only, they should not be used directly */
|
|
|
|
struct CacheEntry
|
|
{
|
|
CacheChannel *m_channelArray; // array of channels, automatically resized if more channels are created
|
|
unsigned int m_count; // number of channel in channelArray
|
|
CacheEntry() : m_channelArray(NULL), m_count(0) {}
|
|
~CacheEntry();
|
|
};
|
|
|
|
struct CacheChannel
|
|
{
|
|
CacheItem* initItem; // item corresponding to timestamp=0
|
|
struct CacheBuffer *m_firstBuffer; // first buffer of list
|
|
struct CacheBuffer *m_lastBuffer; // last buffer of list to which an item was written
|
|
char m_name[32]; // channel name
|
|
unsigned char m_busy; // =0 if channel is free, !=0 when channel is in use
|
|
unsigned char m_positionToBlockShiftW; // number of bits to shift a position in word to get the block number
|
|
unsigned short m_positionToOffsetMaskW; // bit mask to apply on a position in word to get offset in a block
|
|
unsigned int m_maxItemSizeB; // maximum item size in bytes
|
|
unsigned int m_bufferSizeW; // size of item buffer in word to allocate when a new buffer must be created
|
|
unsigned int m_blockSizeW; // block size in words of the lookup table
|
|
unsigned int m_lastTimestamp; // timestamp of the last item that was written
|
|
unsigned int m_lastItemPositionW; // position in words in lastBuffer of the last item written
|
|
void clear();
|
|
CacheBuffer* allocBuffer();
|
|
CacheItem* findItemOrLater(unsigned int timestamp, CacheBuffer **rBuffer);
|
|
CacheItem* findItemEarlier(unsigned int timestamp, CacheBuffer **rBuffer);
|
|
// Internal function: finds an item in a buffer that is < timeOffset
|
|
// timeOffset must be a valid offset for the buffer and the buffer must not be empty
|
|
// on return highBlock contains the block with items above or equal to timeOffset
|
|
CacheItem *_findBlock(CacheBuffer *buffer, unsigned short timeOffset, unsigned int *highBlock);
|
|
};
|
|
|
|
struct CacheBlock {
|
|
unsigned short m_timeOffset; // timestamp relative to m_firstTimestamp
|
|
unsigned short m_offsetW; // position in words of item relative to start of block
|
|
};
|
|
|
|
/* CacheItem is the header of each item in the buffer, must be 32bit
|
|
Items are always 32 bits aligned and size is the number of 32 bit words until the
|
|
next item header, including an eventual pre and post padding gap for pointer alignment */
|
|
struct CacheItem
|
|
{
|
|
unsigned short m_timeOffset; // timestamp relative to m_firstTimestamp
|
|
unsigned short m_sizeW; // size of item in 32 bit words
|
|
// item data follows header immediately or after a gap if position is not pointer aligned
|
|
};
|
|
|
|
// Buffer header
|
|
// Defined in a macro to avoid sizeof() potential problem.
|
|
// next for linked list. = NULL for last buffer
|
|
// m_firstTimestamp timestamp of first item in this buffer
|
|
// m_lastTimestamp timestamp of last item in this buffer
|
|
// m_lastTimestamp must be < m_firstTimestamp+65536
|
|
// m_lastItemPositionW position in word of last item written
|
|
// m_firstFreePositionW position in word where a new item can be written, 0 if buffer is empty
|
|
// lookup lookup table for fast access to item by timestamp
|
|
// The buffer is divided in blocks of 2**n bytes with n chosen so that
|
|
// there are no more than CACHE_LOOKUP_TABLE_SIZE blocks and that each
|
|
// block will contain at least one item.
|
|
// Each element of the lookup table gives the timestamp and offset
|
|
// of the last cache item occupying (=starting in) the corresponding block.
|
|
#define CACHE_HEADER \
|
|
struct CacheBuffer *m_next; \
|
|
unsigned int m_firstTimestamp; \
|
|
unsigned int m_lastTimestamp; \
|
|
\
|
|
unsigned int m_lastItemPositionW; \
|
|
unsigned int m_firstFreePositionW;\
|
|
struct CacheBlock lookup[CACHE_LOOKUP_TABLE_SIZE]
|
|
|
|
struct CacheBufferHeader {
|
|
CACHE_HEADER;
|
|
};
|
|
#define CACHE_BUFFER_HEADER_SIZE (sizeof(struct CacheBufferHeader))
|
|
struct CacheBuffer
|
|
{
|
|
CACHE_HEADER;
|
|
struct CacheItem m_firstItem; // the address of this field marks the start of the buffer
|
|
};
|
|
|
|
|
|
}
|
|
|
|
#endif /* CACHE_HPP_ */
|