doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
VertexCache.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
23 
24 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.
25 
26 ===========================================================================
27 */
28 
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31 
32 #include "tr_local.h"
33 
34 
35 static const int FRAME_MEMORY_BYTES = 0x200000;
36 static const int EXPAND_HEADERS = 1024;
37 
38 idCVar idVertexCache::r_showVertexCache( "r_showVertexCache", "0", CVAR_INTEGER|CVAR_RENDERER, "" );
39 idCVar idVertexCache::r_vertexBufferMegs( "r_vertexBufferMegs", "32", CVAR_INTEGER|CVAR_RENDERER, "" );
40 
42 
43 /*
44 ==============
45 R_ListVertexCache_f
46 ==============
47 */
48 static void R_ListVertexCache_f( const idCmdArgs &args ) {
49  vertexCache.List();
50 }
51 
52 /*
53 ==============
54 idVertexCache::ActuallyFree
55 ==============
56 */
58  if (!block) {
59  common->Error( "idVertexCache Free: NULL pointer" );
60  }
61 
62  if ( block->user ) {
63  // let the owner know we have purged it
64  *block->user = NULL;
65  block->user = NULL;
66  }
67 
68  // temp blocks are in a shared space that won't be freed
69  if ( block->tag != TAG_TEMP ) {
70  staticAllocTotal -= block->size;
72 
73  if ( block->vbo ) {
74 #if 0 // this isn't really necessary, it will be reused soon enough
75  // filling with zero length data is the equivalent of freeing
78 #endif
79  } else if ( block->virtMem ) {
80  Mem_Free( block->virtMem );
81  block->virtMem = NULL;
82  }
83  }
84  block->tag = TAG_FREE; // mark as free
85 
86  // unlink stick it back on the free list
87  block->next->prev = block->prev;
88  block->prev->next = block->next;
89 
90 #if 1
91  // stick it on the front of the free list so it will be reused immediately
92  block->next = freeStaticHeaders.next;
93  block->prev = &freeStaticHeaders;
94 #else
95  // stick it on the back of the free list so it won't be reused soon (just for debugging)
96  block->next = &freeStaticHeaders;
97  block->prev = freeStaticHeaders.prev;
98 #endif
99 
100  block->next->prev = block;
101  block->prev->next = block;
102 }
103 
104 /*
105 ==============
106 idVertexCache::Position
107 
108 this will be a real pointer with virtual memory,
109 but it will be an int offset cast to a pointer with
110 ARB_vertex_buffer_object
111 
112 The ARB_vertex_buffer_object will be bound
113 ==============
114 */
116  if ( !buffer || buffer->tag == TAG_FREE ) {
117  common->FatalError( "idVertexCache::Position: bad vertCache_t" );
118  }
119 
120  // the ARB vertex object just uses an offset
121  if ( buffer->vbo ) {
122  if ( r_showVertexCache.GetInteger() == 2 ) {
123  if ( buffer->tag == TAG_TEMP ) {
124  common->Printf( "GL_ARRAY_BUFFER_ARB = %i + %i (%i bytes)\n", buffer->vbo, buffer->offset, buffer->size );
125  } else {
126  common->Printf( "GL_ARRAY_BUFFER_ARB = %i (%i bytes)\n", buffer->vbo, buffer->size );
127  }
128  }
129  if ( buffer->indexBuffer ) {
131  } else {
133  }
134  return (void *)buffer->offset;
135  }
136 
137  // virtual memory is a real pointer
138  return (void *)((byte *)buffer->virtMem + buffer->offset);
139 }
140 
143 }
144 
145 
146 //================================================================================
147 
148 /*
149 ===========
150 idVertexCache::Init
151 ===========
152 */
154  cmdSystem->AddCommand( "listVertexCache", R_ListVertexCache_f, CMD_FL_RENDERER, "lists vertex cache" );
155 
156  if ( r_vertexBufferMegs.GetInteger() < 8 ) {
158  }
159 
160  virtualMemory = false;
161 
162  // use ARB_vertex_buffer_object unless explicitly disabled
164  common->Printf( "using ARB_vertex_buffer_object memory\n" );
165  } else {
166  virtualMemory = true;
167  r_useIndexBuffers.SetBool( false );
168  common->Printf( "WARNING: vertex array range in virtual memory (SLOW)\n" );
169  }
170 
171  // initialize the cache memory blocks
177 
178  // set up the dynamic frame memory
179  frameBytes = FRAME_MEMORY_BYTES;
180  staticAllocTotal = 0;
181 
182  byte *junk = (byte *)Mem_Alloc( frameBytes );
183  for ( int i = 0 ; i < NUM_VERTEX_FRAMES ; i++ ) {
184  allocatingTempBuffer = true; // force the alloc to use GL_STREAM_DRAW_ARB
185  Alloc( junk, frameBytes, &tempBuffers[i] );
186  allocatingTempBuffer = false;
188  // unlink these from the static list, so they won't ever get purged
191  }
192  Mem_Free( junk );
193 
194  EndFrame();
195 }
196 
197 /*
198 ===========
199 idVertexCache::PurgeAll
200 
201 Used when toggling vertex programs on or off, because
202 the cached data isn't valid
203 ===========
204 */
206  while( staticHeaders.next != &staticHeaders ) {
208  }
209 }
210 
211 /*
212 ===========
213 idVertexCache::Shutdown
214 ===========
215 */
217 // PurgeAll(); // !@#: also purge the temp buffers
218 
219  headerAllocator.Shutdown();
220 }
221 
222 /*
223 ===========
224 idVertexCache::Alloc
225 ===========
226 */
227 void idVertexCache::Alloc( void *data, int size, vertCache_t **buffer, bool indexBuffer ) {
228  vertCache_t *block;
229 
230  if ( size <= 0 ) {
231  common->Error( "idVertexCache::Alloc: size = %i\n", size );
232  }
233 
234  // if we can't find anything, it will be NULL
235  *buffer = NULL;
236 
237  // if we don't have any remaining unused headers, allocate some more
239 
240  for ( int i = 0; i < EXPAND_HEADERS; i++ ) {
241  block = headerAllocator.Alloc();
242  block->next = freeStaticHeaders.next;
243  block->prev = &freeStaticHeaders;
244  block->next->prev = block;
245  block->prev->next = block;
246 
247  if( !virtualMemory ) {
248  qglGenBuffersARB( 1, & block->vbo );
249  }
250  }
251  }
252 
253  // move it from the freeStaticHeaders list to the staticHeaders list
254  block = freeStaticHeaders.next;
255  block->next->prev = block->prev;
256  block->prev->next = block->next;
257  block->next = staticHeaders.next;
258  block->prev = &staticHeaders;
259  block->next->prev = block;
260  block->prev->next = block;
261 
262  block->size = size;
263  block->offset = 0;
264  block->tag = TAG_USED;
265 
266  // save data for debugging
267  staticAllocThisFrame += block->size;
270  staticAllocTotal += block->size;
271 
272  // this will be set to zero when it is purged
273  block->user = buffer;
274  *buffer = block;
275 
276  // allocation doesn't imply used-for-drawing, because at level
277  // load time lots of things may be created, but they aren't
278  // referenced by the GPU yet, and can be purged if needed.
280 
281  block->indexBuffer = indexBuffer;
282 
283  // copy the data
284  if ( block->vbo ) {
285  if ( indexBuffer ) {
288  } else {
290  if ( allocatingTempBuffer ) {
292  } else {
294  }
295  }
296  } else {
297  block->virtMem = Mem_Alloc( size );
298  SIMDProcessor->Memcpy( block->virtMem, data, size );
299  }
300 }
301 
302 /*
303 ===========
304 idVertexCache::Touch
305 ===========
306 */
308  if ( !block ) {
309  common->Error( "idVertexCache Touch: NULL pointer" );
310  }
311 
312  if ( block->tag == TAG_FREE ) {
313  common->FatalError( "idVertexCache Touch: freed pointer" );
314  }
315  if ( block->tag == TAG_TEMP ) {
316  common->FatalError( "idVertexCache Touch: temporary pointer" );
317  }
318 
319  block->frameUsed = currentFrame;
320 
321  // move to the head of the LRU list
322  block->next->prev = block->prev;
323  block->prev->next = block->next;
324 
325  block->next = staticHeaders.next;
326  block->prev = &staticHeaders;
327  staticHeaders.next->prev = block;
328  staticHeaders.next = block;
329 }
330 
331 /*
332 ===========
333 idVertexCache::Free
334 ===========
335 */
337  if (!block) {
338  return;
339  }
340 
341  if ( block->tag == TAG_FREE ) {
342  common->FatalError( "idVertexCache Free: freed pointer" );
343  }
344  if ( block->tag == TAG_TEMP ) {
345  common->FatalError( "idVertexCache Free: temporary pointer" );
346  }
347 
348  // this block still can't be purged until the frame count has expired,
349  // but it won't need to clear a user pointer when it is
350  block->user = NULL;
351 
352  block->next->prev = block->prev;
353  block->prev->next = block->next;
354 
355  block->next = deferredFreeList.next;
356  block->prev = &deferredFreeList;
357  deferredFreeList.next->prev = block;
358  deferredFreeList.next = block;
359 }
360 
361 /*
362 ===========
363 idVertexCache::AllocFrameTemp
364 
365 A frame temp allocation must never be allowed to fail due to overflow.
366 We can't simply sync with the GPU and overwrite what we have, because
367 there may still be future references to dynamically created surfaces.
368 ===========
369 */
371  vertCache_t *block;
372 
373  if ( size <= 0 ) {
374  common->Error( "idVertexCache::AllocFrameTemp: size = %i\n", size );
375  }
376 
377  if ( dynamicAllocThisFrame + size > frameBytes ) {
378  // if we don't have enough room in the temp block, allocate a static block,
379  // but immediately free it so it will get freed at the next frame
380  tempOverflow = true;
381  Alloc( data, size, &block );
382  Free( block);
383  return block;
384  }
385 
386  // this data is just going on the shared dynamic list
387 
388  // if we don't have any remaining unused headers, allocate some more
390 
391  for ( int i = 0; i < EXPAND_HEADERS; i++ ) {
392  block = headerAllocator.Alloc();
393  block->next = freeDynamicHeaders.next;
394  block->prev = &freeDynamicHeaders;
395  block->next->prev = block;
396  block->prev->next = block;
397  }
398  }
399 
400  // move it from the freeDynamicHeaders list to the dynamicHeaders list
401  block = freeDynamicHeaders.next;
402  block->next->prev = block->prev;
403  block->prev->next = block->next;
404  block->next = dynamicHeaders.next;
405  block->prev = &dynamicHeaders;
406  block->next->prev = block;
407  block->prev->next = block;
408 
409  block->size = size;
410  block->tag = TAG_TEMP;
411  block->indexBuffer = false;
412  block->offset = dynamicAllocThisFrame;
413  dynamicAllocThisFrame += block->size;
415  block->user = NULL;
416  block->frameUsed = 0;
417 
418  // copy the data
419  block->virtMem = tempBuffers[listNum]->virtMem;
420  block->vbo = tempBuffers[listNum]->vbo;
421 
422  if ( block->vbo ) {
425  } else {
426  SIMDProcessor->Memcpy( (byte *)block->virtMem + block->offset, data, size );
427  }
428 
429  return block;
430 }
431 
432 /*
433 ===========
434 idVertexCache::EndFrame
435 ===========
436 */
438  // display debug information
439  if ( r_showVertexCache.GetBool() ) {
440  int staticUseCount = 0;
441  int staticUseSize = 0;
442 
443  for ( vertCache_t *block = staticHeaders.next ; block != &staticHeaders ; block = block->next ) {
444  if ( block->frameUsed == currentFrame ) {
445  staticUseCount++;
446  staticUseSize += block->size;
447  }
448  }
449 
450  const char *frameOverflow = tempOverflow ? "(OVERFLOW)" : "";
451 
452  common->Printf( "vertex dynamic:%i=%ik%s, static alloc:%i=%ik used:%i=%ik total:%i=%ik\n",
453  dynamicCountThisFrame, dynamicAllocThisFrame/1024, frameOverflow,
455  staticUseCount, staticUseSize/1024,
457  }
458 
459 #if 0
460  // if our total static count is above our working memory limit, start purging things
461  while ( staticAllocTotal > r_vertexBufferMegs.GetInteger() * 1024 * 1024 ) {
462  // free the least recently used
463 
464  }
465 #endif
466 
467  if( !virtualMemory ) {
468  // unbind vertex buffers so normal virtual memory will be used in case
469  // r_useVertexBuffers / r_useIndexBuffers
472  }
473 
474 
481  tempOverflow = false;
482 
483  // free all the deferred free headers
484  while( deferredFreeList.next != &deferredFreeList ) {
486  }
487 
488  // free all the frame temp headers
490  if ( block != &dynamicHeaders ) {
491  block->prev = &freeDynamicHeaders;
494  freeDynamicHeaders.next = block;
495 
497  }
498 }
499 
500 /*
501 =============
502 idVertexCache::List
503 =============
504 */
505 void idVertexCache::List( void ) {
506  int numActive = 0;
507  int numDeferred = 0;
508  int frameStatic = 0;
509  int totalStatic = 0;
510  int deferredSpace = 0;
511 
512  vertCache_t *block;
513  for ( block = staticHeaders.next ; block != &staticHeaders ; block = block->next) {
514  numActive++;
515 
516  totalStatic += block->size;
517  if ( block->frameUsed == currentFrame ) {
518  frameStatic += block->size;
519  }
520  }
521 
522  int numFreeStaticHeaders = 0;
523  for ( block = freeStaticHeaders.next ; block != &freeStaticHeaders ; block = block->next ) {
524  numFreeStaticHeaders++;
525  }
526 
527  int numFreeDynamicHeaders = 0;
528  for ( block = freeDynamicHeaders.next ; block != &freeDynamicHeaders ; block = block->next ) {
529  numFreeDynamicHeaders++;
530  }
531 
532  common->Printf( "%i megs working set\n", r_vertexBufferMegs.GetInteger() );
533  common->Printf( "%i dynamic temp buffers of %ik\n", NUM_VERTEX_FRAMES, frameBytes / 1024 );
534  common->Printf( "%5i active static headers\n", numActive );
535  common->Printf( "%5i free static headers\n", numFreeStaticHeaders );
536  common->Printf( "%5i free dynamic headers\n", numFreeDynamicHeaders );
537 
538  if ( !virtualMemory ) {
539  common->Printf( "Vertex cache is in ARB_vertex_buffer_object memory (FAST).\n");
540  } else {
541  common->Printf( "Vertex cache is in virtual memory (SLOW)\n" );
542  }
543 
544  if ( r_useIndexBuffers.GetBool() ) {
545  common->Printf( "Index buffers are accelerated.\n" );
546  } else {
547  common->Printf( "Index buffers are not used.\n" );
548  }
549 }
550 
551 /*
552 =============
553 idVertexCache::IsFast
554 
555 just for gfxinfo printing
556 =============
557 */
559  if ( virtualMemory ) {
560  return false;
561  }
562  return true;
563 }
idBlockAlloc< vertCache_t, 1024 > headerAllocator
Definition: VertexCache.h:131
idCVar r_useIndexBuffers("r_useIndexBuffers","0", CVAR_RENDERER|CVAR_ARCHIVE|CVAR_INTEGER,"use ARB_vertex_buffer_object for indexes", 0, 1, idCmdSystem::ArgCompletion_Integer< 0, 1 >)
void * virtMem
Definition: VertexCache.h:42
static idCVar r_showVertexCache
Definition: VertexCache.h:110
idCVar r_useVertexBuffers("r_useVertexBuffers","1", CVAR_RENDERER|CVAR_INTEGER,"use ARB_vertex_buffer_object for vertexes", 0, 1, idCmdSystem::ArgCompletion_Integer< 0, 1 >)
struct vertCache_s * next
Definition: VertexCache.h:50
#define GL_ARRAY_BUFFER_ARB
Definition: glext.h:695
#define GL_ELEMENT_ARRAY_BUFFER_ARB
Definition: glext.h:696
vertCache_t freeDynamicHeaders
Definition: VertexCache.h:134
PFNGLBUFFERSUBDATAARBPROC qglBufferSubDataARB
struct vertCache_s * prev
Definition: VertexCache.h:50
int dynamicCountThisFrame
Definition: VertexCache.h:119
PFNGLGENBUFFERSARBPROC qglGenBuffersARB
idCmdSystem * cmdSystem
Definition: CmdSystem.cpp:116
int i
Definition: process.py:33
int staticAllocTotal
Definition: VertexCache.h:114
bool ARBVertexBufferObjectAvailable
Definition: RenderSystem.h:73
idCommon * common
Definition: Common.cpp:206
#define NULL
Definition: Lib.h:88
void SetInteger(const int value)
Definition: CVarSystem.h:148
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:2853
GLuint buffer
Definition: glext.h:3108
int GetInteger(void) const
Definition: CVarSystem.h:143
GLuint vbo
Definition: VertexCache.h:41
virtual void virtual void FatalError(const char *fmt,...) id_attribute((format(printf
bool virtualMemory
Definition: VertexCache.h:124
int staticCountTotal
Definition: VertexCache.h:113
void Touch(vertCache_t *buffer)
void Mem_Free(void *ptr)
Definition: Heap.cpp:1087
PFNGLBUFFERDATAARBPROC qglBufferDataARB
vertCache_t dynamicHeaders
Definition: VertexCache.h:135
virtual void Printf(const char *fmt,...) id_attribute((format(printf
int staticCountThisFrame
Definition: VertexCache.h:117
static idCVar r_vertexBufferMegs
Definition: VertexCache.h:111
int staticAllocThisFrame
Definition: VertexCache.h:116
vertCache_t * AllocFrameTemp(void *data, int bytes)
virtual void VPCALL Memcpy(void *dst, const void *src, const int count)=0
void ActuallyFree(vertCache_t *block)
Definition: VertexCache.cpp:57
struct vertCache_s ** user
Definition: VertexCache.h:49
vertCache_t * tempBuffers[NUM_VERTEX_FRAMES]
Definition: VertexCache.h:128
vertCache_t freeStaticHeaders
Definition: VertexCache.h:133
ptrdiff_t GLsizeiptrARB
Definition: glext.h:2787
bool GetBool(void) const
Definition: CVarSystem.h:142
glconfig_t glConfig
void UnbindIndex()
void Alloc(void *data, int bytes, vertCache_t **buffer, bool indexBuffer=false)
unsigned char byte
Definition: Lib.h:75
void * Position(vertCache_t *buffer)
const int NUM_VERTEX_FRAMES
Definition: VertexCache.h:31
GLsizeiptr size
Definition: glext.h:3112
#define GL_DYNAMIC_DRAW_ARB
Definition: glext.h:721
PFNGLBINDBUFFERARBPROC qglBindBufferARB
int dynamicAllocThisFrame
Definition: VertexCache.h:118
idVertexCache vertexCache
Definition: VertexCache.cpp:41
idRenderSystemLocal tr
void SetBool(const bool value)
Definition: CVarSystem.h:147
void * Mem_Alloc(const int size)
Definition: Heap.cpp:1067
void Free(vertCache_t *buffer)
vertCache_t staticHeaders
Definition: VertexCache.h:137
bool indexBuffer
Definition: VertexCache.h:43
#define GL_STATIC_DRAW_ARB
Definition: glext.h:718
virtual void Error(const char *fmt,...) id_attribute((format(printf
#define GL_STREAM_DRAW_ARB
Definition: glext.h:715
virtual void AddCommand(const char *cmdName, cmdFunction_t function, int flags, const char *description, argCompletion_t argCompletion=NULL)=0
bool allocatingTempBuffer
Definition: VertexCache.h:126
vertCache_t deferredFreeList
Definition: VertexCache.h:136
idSIMDProcessor * SIMDProcessor
Definition: Simd.cpp:43