VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedOpenGL/dlm/dlm.c@ 56566

Last change on this file since 56566 was 56566, checked in by vboxsync, 10 years ago

Host 3D: Expando SPU, DLM module.

  • DLM module reworked. Now it uses hardware way in order to execute Display List (software approach dropped);
  • Chromium/utils slightly extended with more helper functions needed for Expando/DLM;
  • More testing needed especially for glCallLists() and glListBase();
  • Expando/DLM code now enabed for Mac hosts.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.6 KB
Line 
1/* $Id: dlm.c 56566 2015-06-20 08:10:59Z vboxsync $ */
2
3#include <float.h>
4#include "cr_dlm.h"
5#include "cr_mem.h"
6#include "dlm.h"
7
8/**
9 * \mainpage Dlm
10 *
11 * \section DlmIntroduction Introduction
12 *
13 * Chromium consists of all the top-level files in the cr
14 * directory. The dlm module basically takes care of API dispatch,
15 * and OpenGL state management.
16 *
17 */
18
19/**
20 * Module globals: the current DLM state, bound either to each thread, or
21 * to a global.
22 */
23#ifdef CHROMIUM_THREADSAFE
24CRtsd CRDLMTSDKey;
25#else
26CRDLMContextState *CRDLMCurrentState = NULL;
27#endif
28
29#define MIN(a,b) ((a)<(b)?(a):(b))
30
31
32/*************************************************************************/
33
34#ifdef CHROMIUM_THREADSAFE
35/**
36 * This is the thread-specific destructor function for the
37 * data used in the DLM. It's very simple: if a thread exits
38 * that has DLM-specific data, the data represents the listState
39 * for the thread. All data and buffers associated with the list
40 * can be deleted, and the structure itself can be freed.
41 *
42 * Most Chromium threads don't have such things; but then,
43 * if a thread dies elsewhere in Chromium, huge buffers
44 * of information won't still be floating around in
45 * unrecoverable allocated areas, either.
46 */
47static void threadDestructor(void *tsd)
48{
49 CRDLMContextState *listState = (CRDLMContextState *)tsd;
50
51 if (listState)
52 {
53 //if (listState->currentListInfo)
54 // crdlm_free_list(listState->currentListInfo);
55
56 crFree(listState);
57 }
58}
59#endif
60
61/**
62 * This function creates and initializes a new display list
63 * manager. It returns a pointer to the manager, or NULL in
64 * the case of insufficient memory. The dispatch table pointer
65 * is passed in to allow the utilities to muck with the table
66 * to gain functional control when GL calls are made.
67 */
68CRDLM DLM_APIENTRY *crDLMNewDLM(unsigned int userConfigSize, const CRDLMConfig *userConfig)
69{
70 CRDLM *dlm;
71
72 /* This is the default configuration. We'll overwrite it later
73 * with user-supplied configuration information.
74 */
75 CRDLMConfig config = {
76 CRDLM_DEFAULT_BUFFERSIZE,
77 };
78
79 dlm = crAlloc(sizeof(*dlm));
80 if (!dlm) {
81 return NULL;
82 }
83
84 /* Start off by initializing all entries that require further
85 * memory allocation, so we can free up all the memory if there's
86 * a problem.
87 */
88 if (!(dlm->displayLists = crAllocHashtable())) {
89 crFree(dlm);
90 return NULL;
91 }
92
93 /* The creator counts as the first user. */
94 dlm->userCount = 1;
95
96#ifdef CHROMIUM_THREADSAFE
97 /* This mutex ensures that only one thread is changing the displayLists
98 * hash at a time. Note that we may also need a mutex to guarantee that
99 * the hash is not changed by one thread while another thread is
100 * traversing it; this issue has not yet been resolved.
101 */
102 crInitMutex(&(dlm->dlMutex));
103
104 /* Although the thread-specific data (TSD) functions will initialize
105 * the thread key themselves when needed, those functions do not allow
106 * us to specify a thread destructor. Since a thread could potentially
107 * exit with considerable memory allocated (e.g. if a thread exits
108 * after it has issued NewList but before EndList, and while there
109 * are considerable content buffers allocated), I do the initialization
110 * myself, in order to be able to reclaim those resources if a thread
111 * exits.
112 */
113 crInitTSDF(&(dlm->tsdKey), threadDestructor);
114 crInitTSD(&CRDLMTSDKey);
115#endif
116
117 /* Copy over any appropriate configuration values */
118 if (userConfig != NULL) {
119 /* Copy over as much configuration information as is provided.
120 * Note that if the CRDLMConfig structure strictly grows, this
121 * allows forward compatability - routines compiled with
122 * older versions of the structure will only initialize that
123 * section of the structure that they know about.
124 */
125 crMemcpy((void *)&config, (void *) userConfig,
126 MIN(userConfigSize, sizeof(config)));
127 }
128 dlm->bufferSize = config.bufferSize;
129
130 /* Return the pointer to the newly-allocated display list manager */
131 return dlm;
132}
133
134void DLM_APIENTRY crDLMUseDLM(CRDLM *dlm)
135{
136 DLM_LOCK(dlm);
137 dlm->userCount++;
138 DLM_UNLOCK(dlm);
139}
140
141/**
142 * This routine is called when a context or thread is done with a DLM.
143 * It maintains an internal count of users, and will only actually destroy
144 * itself when no one is still using the DLM.
145 */
146void DLM_APIENTRY crDLMFreeDLM(CRDLM *dlm, SPUDispatchTable *dispatchTable)
147{
148 /* We're about to change the displayLists hash; lock it first */
149 DLM_LOCK(dlm)
150
151 /* Decrement the user count. If the user count has gone to
152 * 0, then free the rest of the DLM. Otherwise, other
153 * contexts or threads are still using this DLM; keep
154 * it around.
155 */
156 dlm->userCount--;
157 if (dlm->userCount == 0) {
158
159 crFreeHashtableEx(dlm->displayLists, crdlmFreeDisplayListResourcesCb, dispatchTable);
160 dlm->displayLists = NULL;
161
162 /* Must unlock before freeing the mutex */
163 DLM_UNLOCK(dlm)
164
165#ifdef CHROMIUM_THREADSAFE
166 /* We release the mutex here; we really should delete the
167 * thread data key, but there's no utility in Chromium to
168 * do this.
169 *
170 * Note that, should one thread release the entire DLM
171 * while other threads still believe they are using it,
172 * any other threads that have current display lists (i.e.
173 * have issued glNewList more recently than glEndList)
174 * will be unable to reclaim their (likely very large)
175 * content buffers, as there will be no way to reclaim
176 * the thread-specific data.
177 *
178 * On the other hand, if one thread really does release
179 * the DLM while other threads still believe they are
180 * using it, unreclaimed memory is the least of the
181 * application's problems...
182 */
183 crFreeMutex(&(dlm->dlMutex));
184
185 /* We free the TSD key here as well. Note that this will
186 * strand any threads that still have thread-specific data
187 * tied to this key; but as stated above, if any threads
188 * still do have thread-specific data attached to this DLM,
189 * they're in big trouble anyway.
190 */
191 crFreeTSD(&(dlm->tsdKey));
192 crFreeTSD(&CRDLMTSDKey);
193#endif
194
195 /* Free the master record, and we're all done. */
196 crFree(dlm);
197 }
198 else {
199 /* We're keeping the DLM around for other users. Unlock it,
200 * but retain its memory and display lists.
201 */
202 DLM_UNLOCK(dlm)
203 }
204}
205
206/**
207 * The actual run-time state of a DLM is bound to a context
208 * (because each context can be used by at most one thread at
209 * a time, and a thread can only use one context at a time,
210 * while multiple contexts can use the same DLM).
211 * This creates the structure required to hold the state, and
212 * returns it to the caller, who should store it with any other
213 * context-specific information.
214 */
215
216CRDLMContextState DLM_APIENTRY *crDLMNewContext(CRDLM *dlm)
217{
218 CRDLMContextState *state;
219
220 /* Get a record for our own internal state structure */
221 state = (CRDLMContextState *)crAlloc(sizeof(CRDLMContextState));
222 if (!state) {
223 return NULL;
224 }
225
226 state->dlm = dlm;
227 state->currentListIdentifier = 0;
228 state->currentListInfo = NULL;
229 state->currentListMode = GL_FALSE;
230 state->listBase = 0;
231 state->replayState = CRDLM_IMMEDIATE;
232
233 /* Increment the use count of the DLM provided. This guarantees that
234 * the DLM won't be released until all the contexts have released it.
235 */
236 crDLMUseDLM(dlm);
237
238 return state;
239}
240
241
242/**
243 * This routine should be called when a MakeCurrent changes the current
244 * context. It sets the thread data (or global data, in an unthreaded
245 * environment) appropriately; this in turn changes the behavior of
246 * the installed DLM API functions.
247 */
248void DLM_APIENTRY crDLMSetCurrentState(CRDLMContextState *state)
249{
250 CRDLMContextState *currentState = CURRENT_STATE();
251 if (currentState != state) {
252 SET_CURRENT_STATE(state);
253 }
254}
255
256CRDLMContextState DLM_APIENTRY *crDLMGetCurrentState(void)
257{
258 return CURRENT_STATE();
259}
260
261/**
262 * This routine, of course, is used to release a DLM context when it
263 * is no longer going to be used.
264 */
265
266void DLM_APIENTRY crDLMFreeContext(CRDLMContextState *state, SPUDispatchTable *dispatchTable)
267{
268 CRDLMContextState *listState = CURRENT_STATE();
269
270 /* If we're currently using this context, release it first */
271 if (listState == state)
272 crDLMSetCurrentState(NULL);
273
274 /* Try to free the DLM. This will either decrement the use count,
275 * or will actually free the DLM, if we were the last user.
276 */
277 crDLMFreeDLM(state->dlm, dispatchTable);
278 state->dlm = NULL;
279
280 /* If any buffers still remain (e.g. because there was an open
281 * display list), remove those as well.
282 */
283 if (state->currentListInfo)
284 {
285 crdlmFreeDisplayListResourcesCb((void *)state->currentListInfo, (void *)dispatchTable);
286 state->currentListInfo = NULL;
287 }
288 state->currentListIdentifier = 0;
289
290 /* Free the state record itself */
291 crFree(state);
292}
293
294
295/**
296 * This function can be used if the caller wishes to free up the
297 * potentially considerable resources used to store the display list
298 * content, without losing the rest of the display list management.
299 * For one example, consider an SPU that conditionally sends its
300 * input stream to multiple servers. It could broadcast all display
301 * lists to all servers, or it could only send display lists to servers
302 * that need them. After all servers have the display list, the SPU
303 * may wish to release the resources used to manage the content.
304 */
305CRDLMError DLM_APIENTRY crDLMDeleteListContent(CRDLM *dlm, unsigned long listIdentifier)
306{
307 DLMListInfo *listInfo;
308 DLMInstanceList *instance;
309
310 listInfo = (DLMListInfo *) crHashtableSearch(dlm->displayLists, listIdentifier);
311 if (listInfo && (instance = listInfo->first)) {
312 while (instance) {
313 DLMInstanceList *nextInstance;
314 nextInstance = instance->next;
315 crFree(instance);
316 instance = nextInstance;
317 }
318 listInfo->first = listInfo->last = NULL;
319 }
320 return GL_NO_ERROR;
321}
322
323/* Return whether the current thread is involved in playback.
324 * This is useful for some routines to selectively choose their
325 * unpack state, for example (as replayed DLM functions must be
326 * unpacked with crStateNativePixelPacking instead of the
327 * normal unpack state, for example.
328 */
329CRDLMReplayState DLM_APIENTRY crDLMGetReplayState(void)
330{
331 CRDLMContextState *listState = CURRENT_STATE();
332 if (listState) {
333 return listState->replayState;
334 }
335 else {
336 return CRDLM_IMMEDIATE;
337 }
338}
339
340/**
341 *
342 * Playback/execute a list.
343 * dlm - the display list manager context
344 * listIdentifier - the display list ID (as specified by app) to playback
345 * dispatchTable - the GL dispatch table to jump through as we execute commands
346 */
347void DLM_APIENTRY crDLMReplayDLMList(CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
348{
349 DLMListInfo *listInfo;
350
351 listInfo = (DLMListInfo *)crHashtableSearch(dlm->displayLists, listIdentifier);
352 if (listInfo) {
353 DLMInstanceList *instance = listInfo->first;
354 while (instance) {
355 /* mutex, to make sure another thread doesn't change the list? */
356 /* For now, leave it alone. */
357 (*instance->execute)(instance, dispatchTable);
358 instance = instance->next;
359 }
360 }
361}
362
363/* Playback/execute a list in the current DLM */
364void DLM_APIENTRY crDLMReplayList(unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
365{
366 CRDLMContextState *listState = CURRENT_STATE();
367 if (listState) {
368 CRDLMReplayState oldReplayState = listState->replayState;
369 listState->replayState = CRDLM_REPLAY_ALL_FUNCTIONS;
370 crDLMReplayDLMList(listState->dlm, listIdentifier, dispatchTable);
371 listState->replayState = oldReplayState;
372 }
373}
374
375/*
376 * Playback/execute the state changing portions of a list.
377 * dlm - the display list manager context
378 * listIdentifier - the display list ID (as specified by app) to playback
379 * dispatchTable - the GL dispatch table to jump through as we execute commands
380 */
381void DLM_APIENTRY crDLMReplayDLMListState(CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
382{
383 DLMListInfo *listInfo;
384
385 listInfo = (DLMListInfo *)crHashtableSearch(dlm->displayLists, listIdentifier);
386 if (listInfo) {
387 DLMInstanceList *instance = listInfo->stateFirst;
388 while (instance) {
389 /* mutex, to make sure another thread doesn't change the list? */
390 /* For now, leave it alone. */
391 (*instance->execute)(instance, dispatchTable);
392 instance = instance->stateNext;
393 }
394 }
395}
396
397void DLM_APIENTRY crDLMReplayListState(unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
398{
399 CRDLMContextState *listState = CURRENT_STATE();
400 if (listState) {
401 CRDLMReplayState oldReplayState = listState->replayState;
402 listState->replayState = CRDLM_REPLAY_STATE_FUNCTIONS;
403 crDLMReplayDLMListState(listState->dlm, listIdentifier, dispatchTable);
404 listState->replayState = oldReplayState;
405 }
406}
407
408/* This is a switch statement that lists every "type" value valid for a
409 * glCallLists() function call, with code for decoding the subsequent
410 * values correctly. It uses the current value of the EXPAND() macro,
411 * which must expand into an appropriate action to be taken.
412 * Its codification here allows for multiple uses.
413 */
414#define CALL_LISTS_SWITCH(type, defaultAction) \
415 switch (type) {\
416 EXPAND(GL_BYTE, GLbyte *, *p, p++)\
417 EXPAND(GL_UNSIGNED_BYTE, GLubyte *, *p, p++)\
418 EXPAND(GL_SHORT, GLshort *, *p, p++)\
419 EXPAND(GL_UNSIGNED_SHORT, GLushort *, *p, p++)\
420 EXPAND(GL_INT, GLint *, *p, p++)\
421 EXPAND(GL_FLOAT, GLfloat *, *p, p++)\
422 EXPAND(GL_2_BYTES, unsigned char *, 256*p[0] + p[1], p += 2)\
423 EXPAND(GL_3_BYTES, unsigned char *, 65536*p[0] + 256*p[1] + p[2], p += 3)\
424 EXPAND(GL_4_BYTES, unsigned char *, 16777216*p[0] + 65536*p[1] + 256*p[2] + p[3], p += 4)\
425 default:\
426 defaultAction;\
427 }
428
429void DLM_APIENTRY crDLMReplayDLMLists(CRDLM *dlm, GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
430{
431 unsigned long listId;
432 CRDLMContextState *listState = CURRENT_STATE();
433
434#define EXPAND(TYPENAME, TYPE, REFERENCE, INCREMENT) \
435 case TYPENAME: {\
436 TYPE p = (TYPE)lists;\
437 while (n--) {\
438 listId = listState->listBase + (unsigned long) (REFERENCE);\
439 crDLMReplayDLMList(dlm, listId, dispatchTable);\
440 INCREMENT;\
441 }\
442 break;\
443 }
444
445 CALL_LISTS_SWITCH(type, break)
446#undef EXPAND
447
448}
449
450void DLM_APIENTRY crDLMReplayLists(GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
451{
452 CRDLMContextState *listState = CURRENT_STATE();
453 if (listState) {
454 crDLMReplayDLMLists(listState->dlm, n, type, lists, dispatchTable);
455 }
456}
457
458void DLM_APIENTRY crDLMReplayDLMListsState(CRDLM *dlm, GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
459{
460 unsigned long listId;
461 CRDLMContextState *listState = CURRENT_STATE();
462
463#define EXPAND(TYPENAME, TYPE, REFERENCE, INCREMENT) \
464 case TYPENAME: {\
465 TYPE p = (TYPE)lists;\
466 while (n--) {\
467 listId = listState->listBase + (unsigned long) (REFERENCE);\
468 crDLMReplayDLMListState(dlm, listId, dispatchTable);\
469 INCREMENT;\
470 }\
471 break;\
472 }
473
474 CALL_LISTS_SWITCH(type, break)
475#undef EXPAND
476
477}
478
479void DLM_APIENTRY crDLMReplayListsState(GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable)
480{
481 CRDLMContextState *listState = CURRENT_STATE();
482 if (listState) {
483 crDLMReplayDLMListsState(listState->dlm, n, type, lists, dispatchTable);
484 }
485}
486
487/* When we compiled the display list, we packed all pixel data
488 * tightly. When we execute the display list, we have to make
489 * sure that the client state reflects that the pixel data is
490 * tightly packed, or it will be interpreted incorrectly.
491 */
492void DLM_APIENTRY crDLMSetupClientState(SPUDispatchTable *dispatchTable)
493{
494 dispatchTable->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
495 dispatchTable->PixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
496 dispatchTable->PixelStorei(GL_UNPACK_SKIP_ROWS, 0);
497 dispatchTable->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
498}
499
500void DLM_APIENTRY crDLMRestoreClientState(CRClientState *clientState, SPUDispatchTable *dispatchTable)
501{
502 if (clientState) {
503 dispatchTable->PixelStorei(GL_UNPACK_ROW_LENGTH, clientState->unpack.rowLength);
504 dispatchTable->PixelStorei(GL_UNPACK_SKIP_PIXELS, clientState->unpack.skipPixels);
505 dispatchTable->PixelStorei(GL_UNPACK_SKIP_ROWS, clientState->unpack.skipRows);
506 dispatchTable->PixelStorei(GL_UNPACK_ALIGNMENT, clientState->unpack.alignment);
507 }
508}
509
510void DLM_APIENTRY crDLMSendDLMList(CRDLM *dlm, unsigned long listIdentifier,
511 SPUDispatchTable *dispatchTable)
512{
513 dispatchTable->NewList(listIdentifier, GL_COMPILE);
514 crDLMReplayDLMList(dlm, listIdentifier, dispatchTable);
515 dispatchTable->EndList();
516}
517
518void DLM_APIENTRY crDLMSendList(unsigned long listIdentifier, SPUDispatchTable *dispatchTable)
519{
520 CRDLMContextState *listState = CURRENT_STATE();
521 if (listState) {
522 crDLMSendDLMList(listState->dlm, listIdentifier, dispatchTable);
523 }
524}
525
526struct sendListsCallbackParms {
527 CRDLM *dlm;
528 SPUDispatchTable *dispatchTable;
529};
530
531static void sendListsCallback(unsigned long key, void *data, void *dataPtr2)
532{
533 struct sendListsCallbackParms *parms = (struct sendListsCallbackParms *)dataPtr2;
534
535 crDLMSendDLMList(parms->dlm, key, parms->dispatchTable);
536}
537
538void DLM_APIENTRY crDLMSendAllDLMLists(CRDLM *dlm, SPUDispatchTable *dispatchTable)
539{
540 struct sendListsCallbackParms parms;
541
542 /* This is how we pass our parameter information to the callback routine -
543 * through a pointer to this local structure.
544 */
545 parms.dlm = dlm;
546 parms.dispatchTable = dispatchTable;
547
548 crHashtableWalk(dlm->displayLists, sendListsCallback, (void *)&parms);
549}
550
551void DLM_APIENTRY crDLMSendAllLists(SPUDispatchTable *dispatchTable)
552{
553 CRDLMContextState *listState = CURRENT_STATE();
554 if (listState) {
555 crDLMSendAllDLMLists(listState->dlm, dispatchTable);
556 }
557}
558
559/** Another clever callback arrangement to get the desired data. */
560struct getRefsCallbackParms {
561 int remainingOffset;
562 int remainingCount;
563 unsigned int *buffer;
564 int totalCount;
565};
566
567static void getRefsCallback(unsigned long key, void *data, void *dataPtr2)
568{
569 struct getRefsCallbackParms *cbParms =
570 (struct getRefsCallbackParms *)dataPtr2;
571
572 /* Count the total number of references */
573 cbParms->totalCount++;
574
575 /* If we haven't yet reached the desired offset, decrement it */
576 if (cbParms->remainingOffset > 0) {
577 cbParms->remainingOffset--;
578 }
579 else if (cbParms->remainingCount > 0) {
580 /* Store data until we've stored all we can.
581 */
582 *(cbParms->buffer++) = key;
583 cbParms->remainingCount--;
584 }
585}
586
587int DLM_APIENTRY crDLMGetReferences(CRDLM *dlm, unsigned long listIdentifier,
588 int firstIndex, int sizeofBuffer, unsigned int *buffer)
589{
590 DLMListInfo *listInfo;
591
592 listInfo = (DLMListInfo *) crHashtableSearch(dlm->displayLists, listIdentifier);
593 if (listInfo) {
594 struct getRefsCallbackParms cbParms;
595
596 cbParms.remainingOffset = firstIndex;
597 cbParms.remainingCount = sizeofBuffer;
598 cbParms.buffer = buffer;
599 cbParms.totalCount = 0;
600
601 crHashtableWalk(listInfo->references, getRefsCallback, (void *)&cbParms);
602
603 return cbParms.totalCount;
604 }
605 else {
606 /* No list exists; it therefore has no references */
607 return 0;
608 }
609}
610
611/*
612 * Return id of list currently being compiled. Returns 0 of there's no
613 * current DLM state, or if no list is being compiled.
614 */
615GLuint DLM_APIENTRY crDLMGetCurrentList(void)
616{
617 CRDLMContextState *listState = CURRENT_STATE();
618 return listState ? listState->currentListIdentifier : 0;
619}
620
621/*
622 * Return mode of list currently being compiled. Should be
623 * GL_FALSE if no list is being compiled, or GL_COMPILE if a
624 * list is being compiled but not executed, or GL_COMPILE_AND_EXECUTE
625 * if a list is being compiled and executed.
626 */
627GLenum DLM_APIENTRY crDLMGetCurrentMode(void)
628{
629 CRDLMContextState *listState = CURRENT_STATE();
630 return listState ? listState->currentListMode : 0;
631}
632
633
634static CRDLMErrorCallback ErrorCallback = NULL;
635
636void DLM_APIENTRY crDLMErrorFunction(CRDLMErrorCallback callback)
637{
638 ErrorCallback = callback;
639}
640
641void crdlm_error(int line, const char *file, GLenum error, const char *info)
642{
643 if (ErrorCallback)
644 (*ErrorCallback)(line, file, error, info);
645}
646
647static void crDLMSaveListsCb(unsigned long key, void *pData1, void *pData2 /* unused */ )
648{
649 DLMListInfo *pListInfo = (DLMListInfo*)pData1;
650
651 if (pListInfo)
652 {
653 crDebug("Saving Display Lists: found ID=%u, numInstances=%d, references=%p.",
654 key, pListInfo->numInstances, pListInfo->references);
655
656 DLMInstanceList *pInstance = pListInfo->first;
657 while (pInstance) {
658 crDebug("\t%p", pInstance->execute);
659 pInstance = pInstance->next;
660 }
661 }
662 else
663 crError("Saving Display Lists: found record with no data. Skipping.");
664}
665
666int32_t DLM_APIENTRY crDLMSaveState(void)
667{
668 CRDLMContextState *pListState = CURRENT_STATE();
669
670 if (pListState)
671 crHashtableWalk(pListState->dlm->displayLists, crDLMSaveListsCb, (void *)NULL);
672 else
673 crDebug("Saving Display Lists: no data to save.");
674
675 return 0;
676}
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