VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Video/disp/xpdm/VBoxDispVrdpBmp.cpp@ 48944

Last change on this file since 48944 was 48944, checked in by vboxsync, 11 years ago

Additions/nt: Whitespace and svn:keyword cleanups by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.1 KB
Line 
1/* $Id: VBoxDispVrdpBmp.cpp 48944 2013-10-07 21:32:37Z vboxsync $ */
2
3/** @file
4 * VBox XPDM Display driver
5 */
6
7/*
8 * Copyright (C) 2011-2013 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "VBoxDisp.h"
20#include <iprt/crc.h>
21#include <VBox/RemoteDesktop/VRDEOrders.h>
22
23/*
24 * Cache has a fixed number of preallocated entries. Entries are linked in the MRU lists.
25 *
26 * A new bitmap hash is added to the "temporary" list, and the caller is told that the
27 * bitmap was not cached. If the hash is used again, then it is moved to the "cached" list.
28 * This protects against "cache, memblt, cache, memblt, ..." sequences.
29 *
30 * "Temporary" list contains free and temporary entries. Temporary entries are at the head,
31 * free entries are at the tail. New temporary entries are inserted in the head.
32 *
33 * "Cached" list contains cached entries. When a entry is used, it is moved to the head.
34 *
35 * The purpose of the cache is to answer whether the bitmap was already encountered
36 * before.
37 *
38 * No serialization because the code is executed under vboxHwBuffer* semaphore.
39 */
40
41static uint64_t surfHash (const SURFOBJ *pso, uint32_t cbLine)
42{
43 uint64_t u64CRC = RTCrc64Start ();
44
45 uint32_t h = pso->sizlBitmap.cy;
46 uint8_t *pu8 = (uint8_t *)pso->pvScan0;
47
48 while (h > 0)
49 {
50 u64CRC = RTCrc64Process (u64CRC, pu8, cbLine);
51 pu8 += pso->lDelta;
52 h--;
53 }
54
55 u64CRC = RTCrc64Finish (u64CRC);
56
57 return u64CRC;
58} /* Hash function end. */
59
60
61static BOOL bcComputeHash (const SURFOBJ *pso, VRDPBCHASH *phash)
62{
63 uint32_t cbLine;
64
65 int bytesPerPixel = format2BytesPerPixel (pso);
66
67 if (bytesPerPixel == 0)
68 {
69 return FALSE;
70 }
71
72 phash->cx = (uint16_t)pso->sizlBitmap.cx;
73 phash->cy = (uint16_t)pso->sizlBitmap.cy;
74 phash->bytesPerPixel = bytesPerPixel;
75
76 cbLine = pso->sizlBitmap.cx * bytesPerPixel;
77 phash->hash64 = surfHash (pso, cbLine);
78
79 memset (phash->padding, 0, sizeof (phash->padding));
80
81 return TRUE;
82}
83
84static void bcRemoveFromCached(VRDPBC *pCache, VRDPBCENTRY *pEntry)
85{
86 if (pEntry->prev)
87 {
88 pEntry->prev->next = pEntry->next;
89 }
90 else
91 {
92 pCache->headCached = pEntry->next;
93 }
94
95 if (pEntry->next)
96 {
97 pEntry->next->prev = pEntry->prev;
98 }
99 else
100 {
101 pCache->tailCached = pEntry->prev;
102 }
103}
104
105static void bcRemoveFromTmp(VRDPBC *pCache, VRDPBCENTRY *pEntry)
106{
107 if (pEntry->prev)
108 {
109 pEntry->prev->next = pEntry->next;
110 }
111 else
112 {
113 pCache->headTmp = pEntry->next;
114 }
115
116 if (pEntry->next)
117 {
118 pEntry->next->prev = pEntry->prev;
119 }
120 else
121 {
122 pCache->tailTmp = pEntry->prev;
123 }
124}
125
126static void bcInsertHeadCached(VRDPBC *pCache, VRDPBCENTRY *pEntry)
127{
128 pEntry->prev = NULL;
129 pEntry->next = pCache->headCached;
130
131 if (pCache->headCached)
132 {
133 pCache->headCached->prev = pEntry;
134 }
135 else
136 {
137 pCache->tailCached = pEntry;
138 }
139
140 pCache->headCached = pEntry;
141}
142
143static void bcInsertHeadTmp(VRDPBC *pCache, VRDPBCENTRY *pEntry)
144{
145 pEntry->prev = NULL;
146 pEntry->next = pCache->headTmp;
147
148 if (pCache->headTmp)
149 {
150 pCache->headTmp->prev = pEntry;
151 }
152 else
153 {
154 pCache->tailTmp = pEntry;
155 }
156
157 pCache->headTmp = pEntry;
158}
159
160/* Moves an entry to the head of MRU list. */
161static void bcMoveToHeadCached(VRDPBC *pCache, VRDPBCENTRY *pEntry)
162{
163 if (pEntry->prev)
164 {
165 /* The entry is not yet in the head. Exclude from list. */
166 bcRemoveFromCached(pCache, pEntry);
167
168 /* Insert the entry at the head of MRU list. */
169 bcInsertHeadCached(pCache, pEntry);
170 }
171}
172
173static void bcMoveToHeadTmp(VRDPBC *pCache, VRDPBCENTRY *pEntry)
174{
175 if (pEntry->prev)
176 {
177 /* The entry is not yet in the head. Exclude from list. */
178 bcRemoveFromTmp(pCache, pEntry);
179
180 /* Insert the entry at the head of MRU list. */
181 bcInsertHeadTmp(pCache, pEntry);
182 }
183}
184
185static void bcMoveTmpToCached(VRDPBC *pCache, VRDPBCENTRY *pEntry)
186{
187 /* Remove from Tmp list. */
188 bcRemoveFromTmp(pCache, pEntry);
189
190 /* Insert the entry at the head of Cached list. */
191 bcInsertHeadCached(pCache, pEntry);
192}
193
194static void bcMoveCachedToTmp(VRDPBC *pCache, VRDPBCENTRY *pEntry)
195{
196 /* Remove from cached list. */
197 bcRemoveFromCached(pCache, pEntry);
198
199 /* Insert the entry at the head of Tmp list. */
200 bcInsertHeadTmp(pCache, pEntry);
201}
202
203
204/* Returns pointer to the entry if the hash already presents in the cache.
205 * Moves the found entry to the head of cached MRU list.
206 */
207static VRDPBCENTRY *bcFindHash (VRDPBC *pCache, const VRDPBCHASH *phash)
208{
209 /* Search the "Cached" MRU list. */
210 VRDPBCENTRY *pEntry = pCache->headCached;
211
212 while (pEntry)
213 {
214 if (memcmp (&pEntry->hash, phash, sizeof (VRDPBCHASH)) == 0)
215 {
216 /* Found the entry. Move it to the head of Cached MRU list. */
217 bcMoveToHeadCached(pCache, pEntry);
218
219 return pEntry;
220 }
221
222 pEntry = pEntry->next;
223 }
224
225 /* Search the "Temporary" MRU list. */
226 pEntry = pCache->headTmp;
227
228 while ( pEntry
229 && pEntry->u32Status != VRDP_BC_ENTRY_STATUS_EMPTY)
230 {
231 if (memcmp (&pEntry->hash, phash, sizeof (VRDPBCHASH)) == 0)
232 {
233 /* Found the entry. It will be removed from the list by the caller. */
234 return pEntry;
235 }
236
237 pEntry = pEntry->next;
238 }
239
240 return NULL;
241}
242
243/* Returns TRUE is a entry was also deleted to make room for new entry. */
244static int bcInsertHash (VRDPBC *pCache, const VRDPBCHASH *phash, VRDPBCHASH *phashDeleted, BOOL bForce)
245{
246 LOG(("bcInsertHash %p, tmp tail %p, cached tail %p.", pCache, pCache->tailTmp, pCache->tailCached));
247
248 /* Get the free entry to be used. Try Tmp list, then the tail of the Cached list. */
249 VRDPBCENTRY *pEntry = pCache->tailTmp;
250
251 if (pEntry != NULL)
252 {
253 /* Insert to the head of Tmp list. */
254 bcMoveToHeadTmp(pCache, pEntry);
255 LOG(("bcInsertHash %p, use tmp tail %p.", pCache, pEntry));
256 }
257 else
258 {
259 pEntry = pCache->tailCached;
260 LOG(("bcInsertHash %p, reuse cached tail %p.", pCache, pEntry, pEntry? pEntry->u32Status: 0));
261
262 if (pEntry != NULL)
263 {
264 bcMoveCachedToTmp(pCache, pEntry);
265 }
266 }
267
268 if (!pEntry)
269 {
270 LOG(("bcInsertHash %p, failed to find an entry!!!", pCache));
271 return VRDPBMP_RC_NOT_CACHED;
272 }
273
274 BOOL bHashDeleted;
275 if (pEntry->u32Status == VRDP_BC_ENTRY_STATUS_CACHED)
276 {
277 /* The cache is full. Remove the tail hash. */
278 memcpy (phashDeleted, &pEntry->hash, sizeof (VRDPBCHASH));
279 bHashDeleted = TRUE;
280 }
281 else
282 {
283 bHashDeleted = FALSE;
284 }
285
286 /* The just inserted entry is at the head of Tmp list, so the temporary
287 * entries will be deleted when there is no room in the cache.
288 */
289 memcpy (&pEntry->hash, phash, sizeof (VRDPBCHASH));
290
291 int rc;
292 if (bForce)
293 {
294 LOG(("Force cache"));
295 bcMoveTmpToCached(pCache, pEntry);
296 pEntry->u32Status = VRDP_BC_ENTRY_STATUS_CACHED;
297 rc = VRDPBMP_RC_CACHED;
298 }
299 else
300 {
301 pEntry->u32Status = VRDP_BC_ENTRY_STATUS_TEMPORARY;
302 rc = VRDPBMP_RC_NOT_CACHED;
303 }
304
305 if (bHashDeleted)
306 {
307 rc |= VRDPBMP_RC_F_DELETED;
308 }
309
310 return rc;
311}
312
313/* Find out whether the surface already in the cache.
314 * Insert in the cache if not.
315 * Protection against "cache, memblt, cache, memblt, ..." sequence:
316 * first time just append the bitmap hash and mark it as "temporary";
317 * if the hash is used again, mark as cached and tell the caller to cache the bitmap;
318 * remove "temporary" entries before any other.
319 *
320 */
321int vrdpbmpCacheSurface(VRDPBC *pCache, const SURFOBJ *pso, VRDPBCHASH *phash, VRDPBCHASH *phashDeleted, BOOL bForce)
322{
323 VRDPBCHASH hash;
324
325 BOOL bResult = bcComputeHash (pso, &hash);
326 LOG(("vrdpbmpCacheSurface: compute hash %d.", bResult));
327
328 if (!bResult)
329 {
330 WARN(("MEMBLT: vrdpbmpCacheSurface: could not compute hash."));
331 return VRDPBMP_RC_NOT_CACHED;
332 }
333
334 *phash = hash;
335
336 VRDPBCENTRY *pEntry = bcFindHash (pCache, &hash);
337 LOG(("vrdpbmpCacheSurface: find hash %d.", pEntry? pEntry->u32Status: 0));
338
339 if (pEntry)
340 {
341 if (pEntry->u32Status == VRDP_BC_ENTRY_STATUS_CACHED)
342 {
343 return VRDPBMP_RC_ALREADY_CACHED;
344 }
345
346 /* The status must be VRDP_BC_ENTRY_STATUS_TEMPORARY here.
347 * Update it to *_CACHED.
348 */
349 if (pEntry->u32Status != VRDP_BC_ENTRY_STATUS_TEMPORARY)
350 {
351 LOG(("MEMBLT: vrdpbmpCacheSurface: unexpected status %d.", pEntry->u32Status));
352 return VRDPBMP_RC_NOT_CACHED;
353 }
354
355 bcMoveTmpToCached(pCache, pEntry);
356
357 pEntry->u32Status = VRDP_BC_ENTRY_STATUS_CACHED;
358 return VRDPBMP_RC_CACHED;
359 }
360
361 int rc = bcInsertHash (pCache, &hash, phashDeleted, bForce);
362 LOG(("vrdpbmpCacheSurface: insert hash %x.", rc));
363
364 return rc;
365}
366
367/* Setup the initial state of the cache. */
368void vrdpbmpReset(VRDPBC *pCache)
369{
370 int i;
371
372 Assert(sizeof (VRDPBCHASH) == sizeof (VRDEBITMAPHASH));
373
374 LOG(("vrdpbmpReset: %p.", pCache));
375
376 /* Reinitialize the cache structure. */
377 memset(pCache, 0, sizeof (VRDPBC));
378
379 pCache->headTmp = &pCache->aEntries[0];
380 pCache->tailTmp = &pCache->aEntries[RT_ELEMENTS(pCache->aEntries) - 1];
381
382 for (i = 0; i < RT_ELEMENTS(pCache->aEntries); i++)
383 {
384 VRDPBCENTRY *pEntry = &pCache->aEntries[i];
385
386 if (pEntry != pCache->tailTmp)
387 {
388 pEntry->next = &pCache->aEntries[i + 1];
389 }
390
391 if (pEntry != pCache->headTmp)
392 {
393 pEntry->prev = &pCache->aEntries[i - 1];
394 }
395 }
396
397 pCache->headCached = NULL;
398 pCache->tailCached = NULL;
399}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette