VirtualBox

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

Last change on this file since 69496 was 69496, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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