Snapshot_Jobs.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../idlib/precompiled.h"
  22. #include "Snapshot_Jobs.h"
  23. uint32 SnapObjChecksum( const uint8 * data, int length ) {
  24. extern unsigned long CRC32_BlockChecksum( const void *data, int length );
  25. return CRC32_BlockChecksum( data, length );
  26. }
  27. /*
  28. ========================
  29. ObjectsSame
  30. ========================
  31. */
  32. ID_INLINE bool ObjectsSame( objJobState_t & newState, objJobState_t & oldState ) {
  33. assert( newState.valid && oldState.valid );
  34. assert( newState.objectNum == oldState.objectNum );
  35. if ( newState.size != oldState.size ) {
  36. //assert( newState.data != oldState.data) );
  37. return false; // Can't match if sizes different
  38. }
  39. /*
  40. if ( newState.data == oldState.data ) {
  41. return true; // Definite match
  42. }
  43. */
  44. if ( memcmp( newState.data, oldState.data, newState.size ) == 0 ) {
  45. return true; // Byte match, same
  46. }
  47. return false; // Not the same
  48. }
  49. /*
  50. ========================
  51. SnapshotObjectJob
  52. This job processes objects by delta comparing them, and then zrle encoding them to the dest stream
  53. The dest stream is then eventually read by the lzw job, and then lzw compressed into the final delta packet
  54. ready to be sent to peers.
  55. ========================
  56. */
  57. void SnapshotObjectJob( objParms_t * parms ) {
  58. int visIndex = parms->visIndex;
  59. objJobState_t & newState = parms->newState;
  60. objJobState_t & oldState = parms->oldState;
  61. objHeader_t * header = parms->destHeader;
  62. uint8 * dataStart = parms->dest;
  63. assert( newState.valid || oldState.valid );
  64. // Setup header
  65. header->flags = 0;
  66. header->size = newState.valid ? newState.size : 0;
  67. header->csize = 0;
  68. header->objID = -1; // Default to ack
  69. header->data = dataStart;
  70. assert( header->size <= MAX_UNSIGNED_TYPE( objectSize_t ) );
  71. // Setup checksum and tag
  72. #ifdef SNAPSHOT_CHECKSUMS
  73. header->checksum = 0;
  74. #endif
  75. idZeroRunLengthCompressor rleCompressor;
  76. bool visChange = false; // visibility changes will be signified with a 0xffff state size
  77. bool visSendState = false; // the state is sent when an entity is no longer stale
  78. // Compute visibility changes
  79. // (we need to do this before writing out object id, because we may not need to write out the id if we early out)
  80. // (when we don't write out the id, we assume this is an "ack" when we deserialize the objects)
  81. if ( newState.valid && oldState.valid ) {
  82. // Check visibility
  83. assert( newState.objectNum == oldState.objectNum );
  84. if ( visIndex > 0 ) {
  85. bool oldVisible = ( oldState.visMask & ( 1 << visIndex ) ) != 0;
  86. bool newVisible = ( newState.visMask & ( 1 << visIndex ) ) != 0;
  87. // Force visible if we need to either create or destroy this object
  88. newVisible |= ( newState.size == 0 ) != ( oldState.size == 0 );
  89. if ( !oldVisible && !newVisible ) {
  90. // object is stale and ack'ed for this client, write nothing (see 'same object' below)
  91. header->flags |= OBJ_SAME;
  92. return;
  93. } else if ( oldVisible && !newVisible ) {
  94. //SNAP_VERBOSE_PRINT( "object %d to client %d goes stale\n", newState->objectNum, visIndex );
  95. visChange = true;
  96. visSendState = false;
  97. } else if ( !oldVisible && newVisible ) {
  98. //SNAP_VERBOSE_PRINT( "object %d to client %d no longer stale\n", newState->objectNum, visIndex );
  99. visChange = true;
  100. visSendState = true;
  101. }
  102. }
  103. // Same object, write a delta (never early out during vis changes)
  104. if ( !visChange && ObjectsSame( newState, oldState ) ) {
  105. // same state, write nothing
  106. header->flags |= OBJ_SAME;
  107. return;
  108. }
  109. }
  110. // Get the id of the object we are writing out
  111. int32 objectNum = ( newState.valid ) ? newState.objectNum : oldState.objectNum;
  112. // Write out object id
  113. header->objID = objectNum;
  114. if ( !newState.valid ) {
  115. // Deleted, write 0 size
  116. assert( oldState.valid );
  117. header->flags |= OBJ_DELETED;
  118. } else if ( !oldState.valid ) {
  119. // New object, write out full state
  120. assert( newState.valid );
  121. // delta against an empty snap
  122. rleCompressor.Start( dataStart, NULL, OBJ_DEST_SIZE_ALIGN16( newState.size ) );
  123. rleCompressor.WriteBytes( newState.data, newState.size );
  124. header->csize = rleCompressor.End();
  125. header->flags |= OBJ_NEW;
  126. if ( header->csize == -1 ) {
  127. // Not enough space, don't compress, have lzw job do zrle compression instead
  128. memcpy( dataStart, newState.data, newState.size );
  129. }
  130. } else {
  131. // Compare to same obj id in different snapshot
  132. assert( newState.objectNum == oldState.objectNum );
  133. header->flags |= OBJ_DIFFERENT;
  134. if ( visChange ) {
  135. header->flags |= visSendState ? OBJ_VIS_NOT_STALE : OBJ_VIS_STALE;
  136. }
  137. if ( !visChange || visSendState ) {
  138. int compareSize = Min( newState.size, oldState.size );
  139. rleCompressor.Start( dataStart, NULL, OBJ_DEST_SIZE_ALIGN16( newState.size ) );
  140. for ( int b = 0; b < compareSize; b++ ) {
  141. byte delta = newState.data[b] - oldState.data[b];
  142. rleCompressor.WriteByte( ( 0xFF + 1 + delta ) & 0xFF );
  143. }
  144. // Get leftover
  145. int leftOver = newState.size - compareSize;
  146. if ( leftOver > 0 ) {
  147. rleCompressor.WriteBytes( newState.data + compareSize, leftOver );
  148. }
  149. header->csize = rleCompressor.End();
  150. if ( header->csize == -1 ) {
  151. // Not enough space, don't compress, have lzw job do zrle compression instead
  152. for ( int b = 0; b < compareSize; b++ ) {
  153. *dataStart++ = ( ( 0xFF + 1 + ( newState.data[b] - oldState.data[b] ) ) & 0xFF );
  154. }
  155. // Get leftover
  156. int leftOver = newState.size - compareSize;
  157. if ( leftOver > 0 ) {
  158. memcpy( dataStart, newState.data + compareSize, leftOver );
  159. }
  160. }
  161. }
  162. }
  163. assert( header->csize <= OBJ_DEST_SIZE_ALIGN16( header->size ) );
  164. #ifdef SNAPSHOT_CHECKSUMS
  165. if ( newState.valid ) {
  166. assert( newState.size );
  167. header->checksum = SnapObjChecksum( newState.data, newState.size );
  168. }
  169. #endif
  170. }
  171. /*
  172. ========================
  173. FinishLZWStream
  174. ========================
  175. */
  176. static void FinishLZWStream( lzwParm_t * parm, idLZWCompressor * lzwCompressor ) {
  177. if ( lzwCompressor->IsOverflowed() ) {
  178. lzwCompressor->Restore();
  179. }
  180. lzwDelta_t & pendingDelta = parm->ioData->lzwDeltas[parm->ioData->numlzwDeltas];
  181. if ( lzwCompressor->End() == -1 ) {
  182. // If we couldn't end the stream, notify the main thread
  183. pendingDelta.offset = -1;
  184. pendingDelta.size = -1;
  185. pendingDelta.snapSequence = -1;
  186. parm->ioData->numlzwDeltas++;
  187. return;
  188. }
  189. int size = lzwCompressor->Length();
  190. pendingDelta.offset = parm->ioData->lzwBytes; // Remember offset into buffer
  191. pendingDelta.size = size; // Remember size
  192. pendingDelta.snapSequence = parm->ioData->snapSequence; // Remember which snap sequence this delta belongs to
  193. parm->ioData->lzwBytes += size;
  194. parm->ioData->numlzwDeltas++;
  195. }
  196. /*
  197. ========================
  198. NewLZWStream
  199. ========================
  200. */
  201. static void NewLZWStream( lzwParm_t * parm, idLZWCompressor * lzwCompressor ) {
  202. // Reset compressor
  203. int maxSize = parm->ioData->maxlzwMem - parm->ioData->lzwBytes;
  204. lzwCompressor->Start( &parm->ioData->lzwMem[parm->ioData->lzwBytes], maxSize );
  205. parm->ioData->lastObjId = 0;
  206. parm->ioData->snapSequence++;
  207. lzwCompressor->WriteAgnostic( parm->ioData->snapSequence );
  208. lzwCompressor->WriteAgnostic( parm->baseSequence );
  209. lzwCompressor->WriteAgnostic( parm->curTime );
  210. }
  211. /*
  212. ========================
  213. ContinueLZWStream
  214. ========================
  215. */
  216. static void ContinueLZWStream( lzwParm_t * parm, idLZWCompressor * lzwCompressor ) {
  217. // Continue compressor where we left off
  218. int maxSize = parm->ioData->maxlzwMem - parm->ioData->lzwBytes;
  219. lzwCompressor->Start( &parm->ioData->lzwMem[parm->ioData->lzwBytes], maxSize, true );
  220. }
  221. /*
  222. ========================
  223. LZWJobInternal
  224. This job takes a stream of objects, which should already be zrle compressed, and then lzw compresses them
  225. and builds a final delta packet ready to be sent to peers.
  226. ========================
  227. */
  228. void LZWJobInternal( lzwParm_t * parm, unsigned int dmaTag ) {
  229. assert( parm->numObjects > 0 );
  230. #ifndef ALLOW_MULTIPLE_DELTAS
  231. if ( parm->ioData->numlzwDeltas > 0 ) {
  232. // Currently, we don't use fragmented deltas.
  233. // We only send the first one and rely on a full snap being sent to get the whole snap across
  234. assert( parm->ioData->numlzwDeltas == 1 );
  235. assert( !parm->ioData->fullSnap );
  236. return;
  237. }
  238. #endif
  239. assert( parm->ioData->lzwBytes < parm->ioData->maxlzwMem );
  240. dmaTag = dmaTag;
  241. ALIGN16( idLZWCompressor lzwCompressor( parm->ioData->lzwData ) );
  242. if ( parm->fragmented ) {
  243. // This packet was partially written out, we need to continue writing, using previous lzw dictionary values
  244. ContinueLZWStream( parm, &lzwCompressor );
  245. } else {
  246. // We can start a new lzw dictionary
  247. NewLZWStream( parm, &lzwCompressor );
  248. }
  249. int numChangedObjProcessed = 0;
  250. for ( int i = 0; i < parm->numObjects; i++ ) {
  251. // This will eventually be gracefully caught in SnapshotProcessor.cpp.
  252. // It's nice to know right when it happens though, so you can inspect the situation.
  253. assert( !lzwCompressor.IsOverflowed() || numChangedObjProcessed > 1 );
  254. // First, see if we need to finish the current lzw stream
  255. if ( lzwCompressor.IsOverflowed() || lzwCompressor.Length() >= parm->ioData->optimalLength ) {
  256. FinishLZWStream( parm, &lzwCompressor );
  257. // indicate how much needs to be DMA'ed back out
  258. parm->ioData->lzwDmaOut = parm->ioData->lzwBytes;
  259. #ifdef ALLOW_MULTIPLE_DELTAS
  260. NewLZWStream( parm, &lzwCompressor );
  261. #else
  262. // Currently, we don't use fragmented deltas.
  263. // We only send the first one and rely on a full snap being sent to get the whole snap across
  264. assert( !parm->ioData->fullSnap );
  265. assert( parm->ioData->numlzwDeltas == 1 );
  266. return;
  267. #endif
  268. }
  269. if ( numChangedObjProcessed > 0 ) {
  270. // We should be at a good spot in the stream if we've written at least one obj without overflowing, so save it
  271. lzwCompressor.Save();
  272. }
  273. // Get header
  274. objHeader_t * header = &parm->headers[i];
  275. if ( header->objID == -1 ) {
  276. assert( header->flags & OBJ_SAME );
  277. continue; // Don't send object (which means ack)
  278. }
  279. numChangedObjProcessed++;
  280. // Write obj id as delta into stream
  281. lzwCompressor.WriteAgnostic<uint16>( (uint16)( header->objID - parm->ioData->lastObjId ) );
  282. parm->ioData->lastObjId = (uint16)header->objID;
  283. // Check special stale/notstale flags
  284. if ( header->flags & ( OBJ_VIS_STALE | OBJ_VIS_NOT_STALE ) ) {
  285. // Write stale/notstale flag
  286. objectSize_t value = ( header->flags & OBJ_VIS_STALE ) ? SIZE_STALE : SIZE_NOT_STALE;
  287. lzwCompressor.WriteAgnostic<objectSize_t>( value );
  288. }
  289. if ( header->flags & OBJ_VIS_STALE ) {
  290. continue; // Don't write out data for stale objects
  291. }
  292. if ( header->flags & OBJ_DELETED ) {
  293. // Object was deleted
  294. lzwCompressor.WriteAgnostic<objectSize_t>( 0 );
  295. continue;
  296. }
  297. // Write size
  298. lzwCompressor.WriteAgnostic<objectSize_t>( (objectSize_t)header->size );
  299. // Get compressed data area
  300. uint8 * compressedData = header->data;
  301. if ( header->csize == -1 ) {
  302. // Wasn't zrle compressed, zrle now while lzw'ing
  303. idZeroRunLengthCompressor rleCompressor;
  304. rleCompressor.Start( NULL, &lzwCompressor, 0xFFFF );
  305. rleCompressor.WriteBytes( compressedData, header->size );
  306. rleCompressor.End();
  307. } else {
  308. // Write out zero-rle compressed data
  309. lzwCompressor.Write( compressedData, header->csize );
  310. }
  311. #ifdef SNAPSHOT_CHECKSUMS
  312. // Write checksum
  313. lzwCompressor.WriteAgnostic( header->checksum );
  314. #endif
  315. // This will eventually be gracefully caught in SnapshotProcessor.cpp.
  316. // It's nice to know right when it happens though, so you can inspect the situation.
  317. assert( !lzwCompressor.IsOverflowed() || numChangedObjProcessed > 1 );
  318. }
  319. if ( !parm->saveDictionary ) {
  320. // Write out terminator
  321. uint16 objectDelta = 0xFFFF - parm->ioData->lastObjId;
  322. lzwCompressor.WriteAgnostic( objectDelta );
  323. // Last stream
  324. FinishLZWStream( parm, &lzwCompressor );
  325. // indicate how much needs to be DMA'ed back out
  326. parm->ioData->lzwDmaOut = parm->ioData->lzwBytes;
  327. parm->ioData->fullSnap = true; // We sent a full snap
  328. } else {
  329. // the compressor did some work, wrote data to lzwMem, but since we didn't call FinishLZWStream to end the compression,
  330. // we need to figure how much needs to be DMA'ed back out
  331. assert( parm->ioData->lzwBytes == 0 ); // I don't think we ever hit this with lzwBytes != 0, but adding it just in case
  332. parm->ioData->lzwDmaOut = parm->ioData->lzwBytes + lzwCompressor.Length();
  333. }
  334. assert( parm->ioData->lzwBytes < parm->ioData->maxlzwMem );
  335. }
  336. /*
  337. ========================
  338. LZWJob
  339. ========================
  340. */
  341. void LZWJob( lzwParm_t * parm ) {
  342. LZWJobInternal( parm, 0 );
  343. }