diff --git a/neo/renderer/VertexCache.cpp b/neo/renderer/VertexCache.cpp
index ec0184e59..fd3ba5225 100644
--- a/neo/renderer/VertexCache.cpp
+++ b/neo/renderer/VertexCache.cpp
@@ -1,561 +1,627 @@
-/*
-===========================================================================
-
-Doom 3 GPL Source Code
-Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
-
-This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
-
-Doom 3 Source Code is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Doom 3 Source Code is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Doom 3 Source Code. If not, see .
-
-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.
-
-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.
-
-===========================================================================
-*/
-
-#include "sys/platform.h"
-#include "framework/Common.h"
-#include "renderer/tr_local.h"
-
-#include "renderer/VertexCache.h"
-
-static const int FRAME_MEMORY_BYTES = 0x200000;
-static const int EXPAND_HEADERS = 1024;
-
-idCVar idVertexCache::r_showVertexCache( "r_showVertexCache", "0", CVAR_INTEGER|CVAR_RENDERER, "" );
-idCVar idVertexCache::r_vertexBufferMegs( "r_vertexBufferMegs", "32", CVAR_INTEGER|CVAR_RENDERER, "" );
-
-idVertexCache vertexCache;
-
-/*
-==============
-R_ListVertexCache_f
-==============
-*/
-static void R_ListVertexCache_f( const idCmdArgs &args ) {
- vertexCache.List();
-}
-
-/*
-==============
-idVertexCache::ActuallyFree
-==============
-*/
-void idVertexCache::ActuallyFree( vertCache_t *block ) {
- if (!block) {
- common->Error( "idVertexCache Free: NULL pointer" );
- }
-
- if ( block->user ) {
- // let the owner know we have purged it
- *block->user = NULL;
- block->user = NULL;
- }
-
- // temp blocks are in a shared space that won't be freed
- if ( block->tag != TAG_TEMP ) {
- staticAllocTotal -= block->size;
- staticCountTotal--;
-
- if ( block->vbo ) {
-#if 0 // this isn't really necessary, it will be reused soon enough
- // filling with zero length data is the equivalent of freeing
- qglBindBufferARB(GL_ARRAY_BUFFER_ARB, block->vbo);
- qglBufferDataARB(GL_ARRAY_BUFFER_ARB, 0, 0, GL_DYNAMIC_DRAW_ARB);
-#endif
- } else if ( block->virtMem ) {
- Mem_Free( block->virtMem );
- block->virtMem = NULL;
- }
- }
- block->tag = TAG_FREE; // mark as free
-
- // unlink stick it back on the free list
- block->next->prev = block->prev;
- block->prev->next = block->next;
-
-#if 1
- // stick it on the front of the free list so it will be reused immediately
- block->next = freeStaticHeaders.next;
- block->prev = &freeStaticHeaders;
-#else
- // stick it on the back of the free list so it won't be reused soon (just for debugging)
- block->next = &freeStaticHeaders;
- block->prev = freeStaticHeaders.prev;
-#endif
-
- block->next->prev = block;
- block->prev->next = block;
-}
-
-/*
-==============
-idVertexCache::Position
-
-this will be a real pointer with virtual memory,
-but it will be an int offset cast to a pointer with
-ARB_vertex_buffer_object
-
-The ARB_vertex_buffer_object will be bound
-==============
-*/
-void *idVertexCache::Position( vertCache_t *buffer ) {
- if ( !buffer || buffer->tag == TAG_FREE ) {
- common->FatalError( "idVertexCache::Position: bad vertCache_t" );
- }
-
- // the ARB vertex object just uses an offset
- if ( buffer->vbo ) {
- if ( r_showVertexCache.GetInteger() == 2 ) {
- if ( buffer->tag == TAG_TEMP ) {
- common->Printf( "GL_ARRAY_BUFFER_ARB = %i + %zd (%i bytes)\n", buffer->vbo, buffer->offset, buffer->size );
- } else {
- common->Printf( "GL_ARRAY_BUFFER_ARB = %i (%i bytes)\n", buffer->vbo, buffer->size );
- }
- }
- if ( buffer->indexBuffer ) {
- qglBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, buffer->vbo );
- } else {
- qglBindBufferARB( GL_ARRAY_BUFFER_ARB, buffer->vbo );
- }
- return (void *)buffer->offset;
- }
-
- // virtual memory is a real pointer
- return (void *)((byte *)buffer->virtMem + buffer->offset);
-}
-
-void idVertexCache::UnbindIndex() {
- qglBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 );
-}
-
-
-//================================================================================
-
-/*
-===========
-idVertexCache::Init
-===========
-*/
-void idVertexCache::Init() {
- cmdSystem->AddCommand( "listVertexCache", R_ListVertexCache_f, CMD_FL_RENDERER, "lists vertex cache" );
-
- if ( r_vertexBufferMegs.GetInteger() < 8 ) {
- r_vertexBufferMegs.SetInteger( 8 );
- }
-
- virtualMemory = false;
-
- // use ARB_vertex_buffer_object unless explicitly disabled
- if( r_useVertexBuffers.GetInteger() && glConfig.ARBVertexBufferObjectAvailable ) {
- common->Printf( "using ARB_vertex_buffer_object memory\n" );
- } else {
- virtualMemory = true;
- r_useIndexBuffers.SetBool( false );
- common->Printf( "WARNING: vertex array range in virtual memory (SLOW)\n" );
- }
-
- // initialize the cache memory blocks
- freeStaticHeaders.next = freeStaticHeaders.prev = &freeStaticHeaders;
- staticHeaders.next = staticHeaders.prev = &staticHeaders;
- freeDynamicHeaders.next = freeDynamicHeaders.prev = &freeDynamicHeaders;
- dynamicHeaders.next = dynamicHeaders.prev = &dynamicHeaders;
- deferredFreeList.next = deferredFreeList.prev = &deferredFreeList;
-
- // set up the dynamic frame memory
- frameBytes = FRAME_MEMORY_BYTES;
- staticAllocTotal = 0;
-
- byte *junk = (byte *)Mem_Alloc( frameBytes );
- for ( int i = 0 ; i < NUM_VERTEX_FRAMES ; i++ ) {
- allocatingTempBuffer = true; // force the alloc to use GL_STREAM_DRAW_ARB
- Alloc( junk, frameBytes, &tempBuffers[i] );
- allocatingTempBuffer = false;
- tempBuffers[i]->tag = TAG_FIXED;
- // unlink these from the static list, so they won't ever get purged
- tempBuffers[i]->next->prev = tempBuffers[i]->prev;
- tempBuffers[i]->prev->next = tempBuffers[i]->next;
- }
- Mem_Free( junk );
-
- EndFrame();
-}
-
-/*
-===========
-idVertexCache::PurgeAll
-
-Used when toggling vertex programs on or off, because
-the cached data isn't valid
-===========
-*/
-void idVertexCache::PurgeAll() {
- while( staticHeaders.next != &staticHeaders ) {
- ActuallyFree( staticHeaders.next );
- }
-}
-
-/*
-===========
-idVertexCache::Shutdown
-===========
-*/
-void idVertexCache::Shutdown() {
-// PurgeAll(); // !@#: also purge the temp buffers
-
- headerAllocator.Shutdown();
-}
-
-/*
-===========
-idVertexCache::Alloc
-===========
-*/
-void idVertexCache::Alloc( void *data, int size, vertCache_t **buffer, bool indexBuffer ) {
- vertCache_t *block;
-
- if ( size <= 0 ) {
- common->Error( "idVertexCache::Alloc: size = %i\n", size );
- }
-
- // if we can't find anything, it will be NULL
- *buffer = NULL;
-
- // if we don't have any remaining unused headers, allocate some more
- if ( freeStaticHeaders.next == &freeStaticHeaders ) {
-
- for ( int i = 0; i < EXPAND_HEADERS; i++ ) {
- block = headerAllocator.Alloc();
- block->next = freeStaticHeaders.next;
- block->prev = &freeStaticHeaders;
- block->next->prev = block;
- block->prev->next = block;
-
- if( !virtualMemory ) {
- qglGenBuffersARB( 1, & block->vbo );
- }
- }
- }
-
- // move it from the freeStaticHeaders list to the staticHeaders list
- block = freeStaticHeaders.next;
- block->next->prev = block->prev;
- block->prev->next = block->next;
- block->next = staticHeaders.next;
- block->prev = &staticHeaders;
- block->next->prev = block;
- block->prev->next = block;
-
- block->size = size;
- block->offset = 0;
- block->tag = TAG_USED;
-
- // save data for debugging
- staticAllocThisFrame += block->size;
- staticCountThisFrame++;
- staticCountTotal++;
- staticAllocTotal += block->size;
-
- // this will be set to zero when it is purged
- block->user = buffer;
- *buffer = block;
-
- // allocation doesn't imply used-for-drawing, because at level
- // load time lots of things may be created, but they aren't
- // referenced by the GPU yet, and can be purged if needed.
- block->frameUsed = currentFrame - NUM_VERTEX_FRAMES;
-
- block->indexBuffer = indexBuffer;
-
- // copy the data
- if ( block->vbo ) {
- if ( indexBuffer ) {
- qglBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, block->vbo );
- qglBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, (GLsizeiptrARB)size, data, GL_STATIC_DRAW_ARB );
- } else {
- qglBindBufferARB( GL_ARRAY_BUFFER_ARB, block->vbo );
- if ( allocatingTempBuffer ) {
- qglBufferDataARB( GL_ARRAY_BUFFER_ARB, (GLsizeiptrARB)size, data, GL_STREAM_DRAW_ARB );
- } else {
- qglBufferDataARB( GL_ARRAY_BUFFER_ARB, (GLsizeiptrARB)size, data, GL_STATIC_DRAW_ARB );
- }
- }
- } else {
- block->virtMem = Mem_Alloc( size );
- SIMDProcessor->Memcpy( block->virtMem, data, size );
- }
-}
-
-/*
-===========
-idVertexCache::Touch
-===========
-*/
-void idVertexCache::Touch( vertCache_t *block ) {
- if ( !block ) {
- common->Error( "idVertexCache Touch: NULL pointer" );
- }
-
- if ( block->tag == TAG_FREE ) {
- common->FatalError( "idVertexCache Touch: freed pointer" );
- }
- if ( block->tag == TAG_TEMP ) {
- common->FatalError( "idVertexCache Touch: temporary pointer" );
- }
-
- block->frameUsed = currentFrame;
-
- // move to the head of the LRU list
- block->next->prev = block->prev;
- block->prev->next = block->next;
-
- block->next = staticHeaders.next;
- block->prev = &staticHeaders;
- staticHeaders.next->prev = block;
- staticHeaders.next = block;
-}
-
-/*
-===========
-idVertexCache::Free
-===========
-*/
-void idVertexCache::Free( vertCache_t *block ) {
- if (!block) {
- return;
- }
-
- if ( block->tag == TAG_FREE ) {
- common->FatalError( "idVertexCache Free: freed pointer" );
- }
- if ( block->tag == TAG_TEMP ) {
- common->FatalError( "idVertexCache Free: temporary pointer" );
- }
-
- // this block still can't be purged until the frame count has expired,
- // but it won't need to clear a user pointer when it is
- block->user = NULL;
-
- block->next->prev = block->prev;
- block->prev->next = block->next;
-
- block->next = deferredFreeList.next;
- block->prev = &deferredFreeList;
- deferredFreeList.next->prev = block;
- deferredFreeList.next = block;
-}
-
-/*
-===========
-idVertexCache::AllocFrameTemp
-
-A frame temp allocation must never be allowed to fail due to overflow.
-We can't simply sync with the GPU and overwrite what we have, because
-there may still be future references to dynamically created surfaces.
-===========
-*/
-vertCache_t *idVertexCache::AllocFrameTemp( void *data, int size ) {
- vertCache_t *block;
-
- if ( size <= 0 ) {
- common->Error( "idVertexCache::AllocFrameTemp: size = %i\n", size );
- }
-
- if ( dynamicAllocThisFrame + size > frameBytes ) {
- // if we don't have enough room in the temp block, allocate a static block,
- // but immediately free it so it will get freed at the next frame
- tempOverflow = true;
- Alloc( data, size, &block );
- Free( block);
- return block;
- }
-
- // this data is just going on the shared dynamic list
-
- // if we don't have any remaining unused headers, allocate some more
- if ( freeDynamicHeaders.next == &freeDynamicHeaders ) {
-
- for ( int i = 0; i < EXPAND_HEADERS; i++ ) {
- block = headerAllocator.Alloc();
- block->next = freeDynamicHeaders.next;
- block->prev = &freeDynamicHeaders;
- block->next->prev = block;
- block->prev->next = block;
- }
- }
-
- // move it from the freeDynamicHeaders list to the dynamicHeaders list
- block = freeDynamicHeaders.next;
- block->next->prev = block->prev;
- block->prev->next = block->next;
- block->next = dynamicHeaders.next;
- block->prev = &dynamicHeaders;
- block->next->prev = block;
- block->prev->next = block;
-
- block->size = size;
- block->tag = TAG_TEMP;
- block->indexBuffer = false;
- block->offset = dynamicAllocThisFrame;
- dynamicAllocThisFrame += block->size;
- dynamicCountThisFrame++;
- block->user = NULL;
- block->frameUsed = 0;
-
- // copy the data
- block->virtMem = tempBuffers[listNum]->virtMem;
- block->vbo = tempBuffers[listNum]->vbo;
-
- if ( block->vbo ) {
- qglBindBufferARB( GL_ARRAY_BUFFER_ARB, block->vbo );
- qglBufferSubDataARB( GL_ARRAY_BUFFER_ARB, block->offset, (GLsizeiptrARB)size, data );
- } else {
- SIMDProcessor->Memcpy( (byte *)block->virtMem + block->offset, data, size );
- }
-
- return block;
-}
-
-/*
-===========
-idVertexCache::EndFrame
-===========
-*/
-void idVertexCache::EndFrame() {
- // display debug information
- if ( r_showVertexCache.GetBool() ) {
- int staticUseCount = 0;
- int staticUseSize = 0;
-
- for ( vertCache_t *block = staticHeaders.next ; block != &staticHeaders ; block = block->next ) {
- if ( block->frameUsed == currentFrame ) {
- staticUseCount++;
- staticUseSize += block->size;
- }
- }
-
- const char *frameOverflow = tempOverflow ? "(OVERFLOW)" : "";
-
- common->Printf( "vertex dynamic:%i=%ik%s, static alloc:%i=%ik used:%i=%ik total:%i=%ik\n",
- dynamicCountThisFrame, dynamicAllocThisFrame/1024, frameOverflow,
- staticCountThisFrame, staticAllocThisFrame/1024,
- staticUseCount, staticUseSize/1024,
- staticCountTotal, staticAllocTotal/1024 );
- }
-
-#if 0
- // if our total static count is above our working memory limit, start purging things
- while ( staticAllocTotal > r_vertexBufferMegs.GetInteger() * 1024 * 1024 ) {
- // free the least recently used
-
- }
-#endif
-
- if( !virtualMemory ) {
- // unbind vertex buffers so normal virtual memory will be used in case
- // r_useVertexBuffers / r_useIndexBuffers
- qglBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
- qglBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 );
- }
-
-
- currentFrame = tr.frameCount;
- listNum = currentFrame % NUM_VERTEX_FRAMES;
- staticAllocThisFrame = 0;
- staticCountThisFrame = 0;
- dynamicAllocThisFrame = 0;
- dynamicCountThisFrame = 0;
- tempOverflow = false;
-
- // free all the deferred free headers
- while( deferredFreeList.next != &deferredFreeList ) {
- ActuallyFree( deferredFreeList.next );
- }
-
- // free all the frame temp headers
- vertCache_t *block = dynamicHeaders.next;
- if ( block != &dynamicHeaders ) {
- block->prev = &freeDynamicHeaders;
- dynamicHeaders.prev->next = freeDynamicHeaders.next;
- freeDynamicHeaders.next->prev = dynamicHeaders.prev;
- freeDynamicHeaders.next = block;
-
- dynamicHeaders.next = dynamicHeaders.prev = &dynamicHeaders;
- }
-}
-
-/*
-=============
-idVertexCache::List
-=============
-*/
-void idVertexCache::List( void ) {
- int numActive = 0;
- int frameStatic = 0;
- int totalStatic = 0;
-
- vertCache_t *block;
- for ( block = staticHeaders.next ; block != &staticHeaders ; block = block->next) {
- numActive++;
-
- totalStatic += block->size;
- if ( block->frameUsed == currentFrame ) {
- frameStatic += block->size;
- }
- }
-
- int numFreeStaticHeaders = 0;
- for ( block = freeStaticHeaders.next ; block != &freeStaticHeaders ; block = block->next ) {
- numFreeStaticHeaders++;
- }
-
- int numFreeDynamicHeaders = 0;
- for ( block = freeDynamicHeaders.next ; block != &freeDynamicHeaders ; block = block->next ) {
- numFreeDynamicHeaders++;
- }
-
- common->Printf( "%i megs working set\n", r_vertexBufferMegs.GetInteger() );
- common->Printf( "%i dynamic temp buffers of %ik\n", NUM_VERTEX_FRAMES, frameBytes / 1024 );
- common->Printf( "%5i active static headers\n", numActive );
- common->Printf( "%5i free static headers\n", numFreeStaticHeaders );
- common->Printf( "%5i free dynamic headers\n", numFreeDynamicHeaders );
-
- if ( !virtualMemory ) {
- common->Printf( "Vertex cache is in ARB_vertex_buffer_object memory (FAST).\n");
- } else {
- common->Printf( "Vertex cache is in virtual memory (SLOW)\n" );
- }
-
- if ( r_useIndexBuffers.GetBool() ) {
- common->Printf( "Index buffers are accelerated.\n" );
- } else {
- common->Printf( "Index buffers are not used.\n" );
- }
-}
-
-/*
-=============
-idVertexCache::IsFast
-
-just for gfxinfo printing
-=============
-*/
-bool idVertexCache::IsFast() {
- if ( virtualMemory ) {
- return false;
- }
- return true;
-}
+/*
+===========================================================================
+
+Doom 3 GPL Source Code
+Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
+
+This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
+
+Doom 3 Source Code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Doom 3 Source Code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Doom 3 Source Code. If not, see .
+
+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.
+
+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.
+
+===========================================================================
+*/
+
+#include "sys/platform.h"
+#include "framework/Common.h"
+#include "renderer/tr_local.h"
+#include "renderer/VertexCache.h"
+
+static const int FRAME_MEMORY_BYTES = 0x400000;
+static const int EXPAND_HEADERS = 1024;
+
+idCVar idVertexCache::r_showVertexCache( "r_showVertexCache", "0", CVAR_INTEGER | CVAR_RENDERER,
+ "show vertex cache, 0 = off, 1 = vbo debug, 2 = vbo mem",
+ idCmdSystem::ArgCompletion_Integer<0, 2> );
+idCVar idVertexCache::r_useArbBufferRange( "r_useArbBufferRange", "1", CVAR_BOOL | CVAR_RENDERER,
+ "use ARB_map_buffer_range for optimization, 0 = off, 1 = on",
+ idCmdSystem::ArgCompletion_Integer<0, 1> );
+idCVar
+idVertexCache::r_reuseVertexCacheSooner( "r_reuseVertexCacheSooner", "1", CVAR_BOOL | CVAR_RENDERER,
+ "reuse vertex buffers as soon as possible after freeing, 0 = off, 1 = on",
+ idCmdSystem::ArgCompletion_Integer<0, 1> );
+
+idVertexCache vertexCache;
+
+/*
+==============
+R_ShowVBOMem_f
+==============
+*/
+void R_ShowVBOMem_f( const idCmdArgs &args ) {
+ vertexCache.Show();
+}
+
+/*
+==============
+R_ListVBOMem_f
+==============
+*/
+void R_ListVBOMem_f( const idCmdArgs &args ) {
+ vertexCache.List();
+}
+
+/*
+==============
+idVertexCache::ActuallyFree
+==============
+*/
+void idVertexCache::ActuallyFree( vertCache_t *block ) {
+ if ( !block ) {
+ common->Error( "idVertexCache Free: NULL pointer" );
+ }
+
+ if ( block->user ) {
+ // let the owner know we have purged it
+ *block->user = NULL;
+ block->user = NULL;
+ }
+
+ // temp blocks are in a shared space that won't be freed
+ if ( block->tag != TAG_TEMP ) {
+ staticAllocTotal -= block->size;
+ staticCountTotal--;
+ }
+ block->tag = TAG_FREE; // mark as free
+
+ // unlink stick it back on the free list
+ block->next->prev = block->prev;
+ block->prev->next = block->next;
+
+ if ( r_reuseVertexCacheSooner.GetBool() ) {
+ // stick it on the front of the free list so it will be reused immediately
+ block->next = freeStaticHeaders.next;
+ block->prev = &freeStaticHeaders;
+ } else {
+ // stick it on the back of the free list so it won't be reused soon (just for debugging)
+ block->next = &freeStaticHeaders;
+ block->prev = freeStaticHeaders.prev;
+ }
+ block->next->prev = block;
+ block->prev->next = block;
+}
+
+/*
+==============
+idVertexCache::Position
+
+this will be a real pointer with virtual memory,
+but it will be an int offset cast to a pointer with
+ARB_vertex_buffer_object
+
+The ARB_vertex_buffer_object will be bound
+==============
+*/
+const void *idVertexCache::Position( vertCache_t *buffer ) {
+ if ( !buffer || buffer->tag == TAG_FREE ) {
+ common->FatalError( "idVertexCache::Position: bad vertCache_t" );
+ }
+
+ // the ARB vertex object just uses an offset
+ if ( buffer->vbo ) {
+ if ( r_showVertexCache.GetInteger() == 2 ) {
+ if ( buffer->tag == TAG_TEMP ) {
+ common->Printf( "GL_ARRAY_BUFFER_ARB = %i + %i (%i bytes)\n", buffer->vbo, buffer->offset, buffer->size );
+ } else {
+ common->Printf( "GL_ARRAY_BUFFER_ARB = %i (%i bytes)\n", buffer->vbo, buffer->size );
+ }
+ }
+ BindIndex( ( buffer->indexBuffer ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER ), buffer->vbo );
+ return reinterpret_cast( buffer->offset );
+ }
+ return NULL;
+}
+
+//================================================================================
+
+/*
+===========
+idVertexCache::BindIndex
+
+Makes sure it only allocates the right buffers once.
+===========
+*/
+void idVertexCache::BindIndex( GLenum target, GLuint vbo ) {
+ switch ( target ) {
+ case GL_ARRAY_BUFFER:
+ if ( vertexBuffer != vbo ) {
+ // this happens more often than you might think :(
+ qglBindBufferARB( target, vbo );
+ vertexBuffer = vbo;
+ return;
+ }
+ break;
+ case GL_ELEMENT_ARRAY_BUFFER:
+ if ( indexBuffer != vbo ) {
+ // this happens more often than you might think :(
+ qglBindBufferARB( target, vbo );
+ indexBuffer = vbo;
+ return;
+ }
+ break;
+ default:
+ common->FatalError( "BindIndex : unknown buffer target : %i\n", static_cast( target ) );
+ break;
+ }
+}
+
+/*
+===========
+idVertexCache::UnbindIndex
+
+Pass 0 to vbo to invalidate it.
+===========
+*/
+void idVertexCache::UnbindIndex( GLenum target ) {
+ BindIndex( target, 0 );
+}
+
+//================================================================================
+
+/*
+===========
+idVertexCache::Init
+===========
+*/
+void idVertexCache::Init() {
+ // well this would suck...
+ if ( !glConfig.ARBVertexBufferObjectAvailable ) {
+ common->FatalError( "idVertexCache::Init: Your card is to old\n" );
+ }
+ cmdSystem->AddCommand( "showVBOMem", R_ShowVBOMem_f, CMD_FL_RENDERER, "Shows Allocated Vertex Buffer Memory" );
+ cmdSystem->AddCommand( "ListVBOMem", R_ListVBOMem_f, CMD_FL_RENDERER, "lists Objects Allocated in Vertex Cache" );
+
+ // initialize the buffers
+ vertexBuffer = 0;
+ indexBuffer = 0;
+
+ // initialize the cache memory blocks
+ freeStaticHeaders.next = freeStaticHeaders.prev = &freeStaticHeaders;
+ staticHeaders.next = staticHeaders.prev = &staticHeaders;
+ freeDynamicHeaders.next = freeDynamicHeaders.prev = &freeDynamicHeaders;
+ dynamicHeaders.next = dynamicHeaders.prev = &dynamicHeaders;
+ deferredFreeList.next = deferredFreeList.prev = &deferredFreeList;
+
+ // set up the dynamic frame memory
+ frameBytes = FRAME_MEMORY_BYTES;
+ staticAllocTotal = 0;
+
+ // allocate a dummy buffer
+ byte *frameBuffer = new byte[frameBytes];
+
+ for ( int i = 0; i < NUM_VERTEX_FRAMES; i++ ) {
+ // force the alloc to use GL_STREAM_DRAW_ARB
+ allocatingTempBuffer = true;
+ Alloc( frameBuffer, frameBytes, &tempBuffers[i] );
+ allocatingTempBuffer = false;
+ tempBuffers[i]->tag = TAG_FIXED;
+
+ // unlink these from the static list, so they won't ever get purged
+ tempBuffers[i]->next->prev = tempBuffers[i]->prev;
+ tempBuffers[i]->prev->next = tempBuffers[i]->next;
+ }
+
+ // use C++ allocation
+ delete[] frameBuffer;
+ frameBuffer = NULL;
+ EndFrame();
+}
+
+/*
+===========
+idVertexCache::PurgeAll
+
+Used when toggling vertex programs on or off, because
+the cached data isn't valid
+===========
+*/
+void idVertexCache::PurgeAll() {
+ while ( staticHeaders.next != &staticHeaders ) {
+ ActuallyFree( staticHeaders.next );
+ }
+}
+
+/*
+===========
+idVertexCache::Shutdown
+===========
+*/
+void idVertexCache::Shutdown() {
+ headerAllocator.Shutdown();
+}
+
+/*
+===========
+idVertexCache::Alloc
+===========
+*/
+void idVertexCache::Alloc( void *data, int size, vertCache_t **buffer, bool doIndex ) {
+ vertCache_t *block = NULL;
+
+ if ( size <= 0 ) {
+ common->Error( "idVertexCache::Alloc: size = %i\n", size );
+ }
+
+ // if we can't find anything, it will be NULL
+ *buffer = NULL;
+
+ // if we don't have any remaining unused headers, allocate some more
+ if ( freeStaticHeaders.next == &freeStaticHeaders ) {
+ for ( int i = 0; i < EXPAND_HEADERS; i++ ) {
+ block = headerAllocator.Alloc();
+ qglGenBuffersARB( 1, &block->vbo );
+ block->size = 0;
+ block->next = freeStaticHeaders.next;
+ block->prev = &freeStaticHeaders;
+ block->next->prev = block;
+ block->prev->next = block;
+ }
+ }
+ GLenum target = ( doIndex ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER );
+ GLenum usage = ( allocatingTempBuffer ? GL_STREAM_DRAW : GL_STATIC_DRAW );
+
+ // try to find a matching block to replace so that we're not continually respecifying vbo data each frame
+ for ( vertCache_t *findblock = freeStaticHeaders.next; /**/; findblock = findblock->next ) {
+ if ( findblock == &freeStaticHeaders ) {
+ block = freeStaticHeaders.next;
+ break;
+ }
+
+ if ( findblock->target != target ) {
+ continue;
+ }
+
+ if ( findblock->usage != usage ) {
+ continue;
+ }
+
+ if ( findblock->size != size ) {
+ continue;
+ }
+ block = findblock;
+ break;
+ }
+
+ // move it from the freeStaticHeaders list to the staticHeaders list
+ block->target = target;
+ block->usage = usage;
+
+ if ( block->vbo ) {
+ // orphan the buffer in case it needs respecifying (it usually will)
+ BindIndex( target, block->vbo );
+ qglBufferDataARB( target, static_cast( size ), NULL, usage );
+ qglBufferDataARB( target, static_cast( size ), data, usage );
+ }
+ block->next->prev = block->prev;
+ block->prev->next = block->next;
+ block->next = staticHeaders.next;
+ block->prev = &staticHeaders;
+ block->next->prev = block;
+ block->prev->next = block;
+ block->size = size;
+ block->offset = 0;
+ block->tag = TAG_USED;
+
+ // save data for debugging
+ staticAllocThisFrame += block->size;
+ staticCountThisFrame++;
+ staticCountTotal++;
+ staticAllocTotal += block->size;
+
+ // this will be set to zero when it is purged
+ block->user = buffer;
+ *buffer = block;
+
+ // allocation doesn't imply used-for-drawing, because at level
+ // load time lots of things may be created, but they aren't
+ // referenced by the GPU yet, and can be purged if needed.
+ block->frameUsed = currentFrame - NUM_VERTEX_FRAMES;
+ block->indexBuffer = doIndex;
+}
+
+/*
+===========
+idVertexCache::Touch
+===========
+*/
+void idVertexCache::Touch( vertCache_t *block ) {
+ if ( !block ) {
+ common->Error( "idVertexCache Touch: NULL pointer" );
+ }
+
+ if ( block->tag == TAG_FREE ) {
+ common->FatalError( "idVertexCache Touch: freed pointer" );
+ }
+
+ if ( block->tag == TAG_TEMP ) {
+ common->FatalError( "idVertexCache Touch: temporary pointer" );
+ }
+ block->frameUsed = currentFrame;
+
+ // move to the head of the LRU list
+ block->next->prev = block->prev;
+ block->prev->next = block->next;
+ block->next = staticHeaders.next;
+ block->prev = &staticHeaders;
+
+ staticHeaders.next->prev = block;
+ staticHeaders.next = block;
+}
+
+/*
+===========
+idVertexCache::Free
+===========
+*/
+void idVertexCache::Free( vertCache_t *block ) {
+ if ( !block ) {
+ return;
+ }
+
+ if ( block->tag == TAG_FREE ) {
+ common->FatalError( "idVertexCache Free: freed pointer" );
+ }
+
+ if ( block->tag == TAG_TEMP ) {
+ common->FatalError( "idVertexCache Free: temporary pointer" );
+ }
+
+ // this block still can't be purged until the frame count has expired,
+ // but it won't need to clear a user pointer when it is
+ block->user = NULL;
+ block->next->prev = block->prev;
+ block->prev->next = block->next;
+ block->next = deferredFreeList.next;
+ block->prev = &deferredFreeList;
+
+ deferredFreeList.next->prev = block;
+ deferredFreeList.next = block;
+}
+
+/*
+===========
+idVertexCache::AllocFrameTemp
+
+A frame temp allocation must never be allowed to fail due to overflow.
+We can't simply sync with the GPU and overwrite what we have, because
+there may still be future references to dynamically created surfaces.
+===========
+*/
+vertCache_t *idVertexCache::AllocFrameTemp( void *data, int size ) {
+ vertCache_t *block = NULL;
+
+ if ( size <= 0 ) {
+ common->Error( "idVertexCache::AllocFrameTemp: size = %i\n", size );
+ }
+
+ if ( dynamicAllocThisFrame + size > frameBytes ) {
+ // if we don't have enough room in the temp block, allocate a static block,
+ // but immediately free it so it will get freed at the next frame
+ tempOverflow = true;
+ Alloc( data, size, &block );
+ Free( block );
+ return block;
+ }
+
+ // this data is just going on the shared dynamic list
+ // if we don't have any remaining unused headers, allocate some more
+ if ( freeDynamicHeaders.next == &freeDynamicHeaders ) {
+ for ( int i = 0; i < EXPAND_HEADERS; i++ ) {
+ block = headerAllocator.Alloc();
+ block->next = freeDynamicHeaders.next;
+ block->prev = &freeDynamicHeaders;
+ block->next->prev = block;
+ block->prev->next = block;
+ }
+ }
+
+ // move it from the freeDynamicHeaders list to the dynamicHeaders list
+ block = freeDynamicHeaders.next;
+ block->next->prev = block->prev;
+ block->prev->next = block->next;
+ block->next = dynamicHeaders.next;
+ block->prev = &dynamicHeaders;
+ block->next->prev = block;
+ block->prev->next = block;
+ block->size = size;
+ block->tag = TAG_TEMP;
+ block->indexBuffer = false;
+ block->offset = dynamicAllocThisFrame;
+
+ dynamicAllocThisFrame += block->size;
+ dynamicCountThisFrame++;
+
+ block->user = NULL;
+ block->frameUsed = 0;
+
+ // copy the data
+ block->vbo = tempBuffers[listNum]->vbo;
+
+ // mh code start
+ if ( block->vbo ) {
+ BindIndex( GL_ARRAY_BUFFER, block->vbo );
+
+ // try to get an unsynchronized map if at all possible
+ if ( r_useArbBufferRange.GetBool() && glConfig.ARBMapBufferRangeAvailable ) {
+ GLbitfield access = ( GL_MAP_WRITE_BIT | ( ( block->offset == 0 ) ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_RANGE_BIT ) );
+ GLvoid *dst = qglMapBufferRange( GL_ARRAY_BUFFER, block->offset, static_cast( size ), access );
+
+ // if the buffer has wrapped then we orphan it
+ if ( dst ) {
+ SIMDProcessor->Memcpy( reinterpret_cast( dst ), data, size );
+ qglUnmapBufferARB( GL_ARRAY_BUFFER );
+ return block;
+ } else {
+ qglBufferSubDataARB( GL_ARRAY_BUFFER, block->offset, static_cast( size ), data );
+ }
+ } else {
+ qglBufferSubDataARB( GL_ARRAY_BUFFER, block->offset, static_cast( size ), data );
+ }
+ }
+ return block;
+}
+
+/*
+===========
+idVertexCache::EndFrame
+===========
+*/
+void idVertexCache::EndFrame() {
+ // display debug information
+ if ( r_showVertexCache.GetBool() == 1 ) {
+ int staticUseCount = 0;
+ int staticUseSize = 0;
+
+ for ( vertCache_t *block = staticHeaders.next; block != &staticHeaders; block = block->next ) {
+ if ( block->frameUsed == currentFrame ) {
+ staticUseCount++;
+ staticUseSize += block->size;
+ }
+ }
+ const char *frameOverflow = tempOverflow ? "(OVERFLOW)" : "";
+ common->Printf( "vertex dynamic:%i=%ik%s, static alloc:%i=%ik used:%i=%ik total:%i=%ik\n", dynamicCountThisFrame,
+ dynamicAllocThisFrame / 1024, frameOverflow, staticCountThisFrame, staticAllocThisFrame / 1024,
+ staticUseCount, staticUseSize / 1024, staticCountTotal, staticAllocTotal / 1024 );
+ }
+
+ // unbind vertex buffers
+ UnbindIndex( GL_ARRAY_BUFFER_ARB );
+ UnbindIndex( GL_ELEMENT_ARRAY_BUFFER_ARB );
+
+ currentFrame = tr.frameCount;
+ listNum = currentFrame % NUM_VERTEX_FRAMES;
+ staticAllocThisFrame = 0;
+ staticCountThisFrame = 0;
+ dynamicAllocThisFrame = 0;
+ dynamicCountThisFrame = 0;
+ tempOverflow = false;
+
+ // free all the deferred free headers
+ while ( deferredFreeList.next != &deferredFreeList ) {
+ ActuallyFree( deferredFreeList.next );
+ }
+
+ // free all the frame temp headers
+ vertCache_t *block = dynamicHeaders.next;
+
+ if ( block != &dynamicHeaders ) {
+ block->prev = &freeDynamicHeaders;
+ dynamicHeaders.prev->next = freeDynamicHeaders.next;
+ freeDynamicHeaders.next->prev = dynamicHeaders.prev;
+ freeDynamicHeaders.next = block;
+ dynamicHeaders.next = dynamicHeaders.prev = &dynamicHeaders;
+ }
+}
+
+/*
+=============
+idVertexCache::List
+=============
+*/
+void idVertexCache::List( void ) {
+ int numActive = 0;
+ int frameStatic = 0;
+ int totalStatic = 0;
+ vertCache_t *block = NULL;
+
+ for ( block = staticHeaders.next; block != &staticHeaders; block = block->next ) {
+ numActive++;
+ totalStatic += block->size;
+
+ if ( block->frameUsed == currentFrame ) {
+ frameStatic += block->size;
+ }
+ }
+ int numFreeStaticHeaders = 0;
+
+ for ( block = freeStaticHeaders.next; block != &freeStaticHeaders; block = block->next ) {
+ numFreeStaticHeaders++;
+ }
+ int numFreeDynamicHeaders = 0;
+
+ for ( block = freeDynamicHeaders.next; block != &freeDynamicHeaders; block = block->next ) {
+ numFreeDynamicHeaders++;
+ }
+ common->Printf( "%i dynamic temp buffers of %ik\n", NUM_VERTEX_FRAMES, frameBytes / 1024 );
+ common->Printf( "%5i active static headers\n", numActive );
+ common->Printf( "%5i free static headers\n", numFreeStaticHeaders );
+ common->Printf( "%5i free dynamic headers\n", numFreeDynamicHeaders );
+}
+
+/*
+=============
+idVertexCache::Show
+
+Barnes,
+replaces the broken glconfig string version.
+Real performance killer here, use with care.
+=============
+*/
+void idVertexCache::Show( void ) {
+ GLint mem[4];
+
+ if ( glConfig.vendor == glvAMD ) {
+ common->Printf( "\nATI/AMD specific memory info:\n" );
+ common->Printf( "\n" );
+ qglGetIntegerv( GL_VBO_FREE_MEMORY_ATI, mem );
+ common->Printf( "VBO: total memory free in the pool %i MB\n", mem[0] >> 10 );
+ common->Printf( "VBO: largest available free block in the pool %i MB\n", mem[1] >> 10 );
+ common->Printf( "VBO: total auxiliary memory free %i MB\n", mem[2] >> 10 );
+ common->Printf( "VBO: largest auxiliary free block %i MB\n", mem[3] >> 10 );
+ qglGetIntegerv( GL_TEXTURE_FREE_MEMORY_ATI, mem );
+ common->Printf( "Texture: total memory free in the pool %i MB\n", mem[0] >> 10 );
+ common->Printf( "Texture: largest available free block in the pool %i MB\n", mem[1] >> 10 );
+ common->Printf( "Texture: total auxiliary memory free %i MB\n", mem[2] >> 10 );
+ common->Printf( "Texture: largest auxiliary free block %i MB\n", mem[3] >> 10 );
+ qglGetIntegerv( GL_RENDERBUFFER_FREE_MEMORY_ATI, mem );
+ common->Printf( "RenderBuffer: total memory free in the pool %i MB\n", mem[0] >> 10 );
+ common->Printf( "RenderBuffer: largest available free block in the pool %i MB\n", mem[1] >> 10 );
+ common->Printf( "RenderBuffer: total auxiliary memory free %i MB\n", mem[2] >> 10 );
+ common->Printf( "RenderBuffer: largest auxiliary free block %i MB\n", mem[3] >> 10 );
+ } else if ( glConfig.vendor == glvNVIDIA ) {
+ common->Printf( "\nNvidia specific memory info:\n" );
+ common->Printf( "\n" );
+ qglGetIntegerv( GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, mem );
+ common->Printf( "dedicated video memory %i MB\n", mem[0] >> 10 );
+ qglGetIntegerv( GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, mem );
+ common->Printf( "total available memory %i MB\n", mem[0] >> 10 );
+ qglGetIntegerv( GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, mem );
+ common->Printf( "currently unused GPU memory %i MB\n", mem[0] >> 10 );
+ qglGetIntegerv( GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX, mem );
+ common->Printf( "count of total evictions seen by system %i MB\n", mem[0] >> 10 );
+ qglGetIntegerv( GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX, mem );
+ common->Printf( "total video memory evicted %i MB\n", mem[0] >> 10 );
+ } else if ( glConfig.vendor == glvIntel ) {
+ common->Printf( "\nIntel cannot do memory info, sorry:\n" );
+ } else {
+ common->Printf( "\nUnsupported card cannot do memory info, sorry:\n" );
+ }
+}
diff --git a/neo/renderer/VertexCache.h b/neo/renderer/VertexCache.h
index a1d0a0d56..0a65dfa65 100644
--- a/neo/renderer/VertexCache.h
+++ b/neo/renderer/VertexCache.h
@@ -1,146 +1,151 @@
-/*
-===========================================================================
-
-Doom 3 GPL Source Code
-Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
-
-This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
-
-Doom 3 Source Code is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Doom 3 Source Code is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Doom 3 Source Code. If not, see .
-
-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.
-
-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.
-
-===========================================================================
-*/
-
-#include "framework/CVarSystem.h"
-#include "renderer/qgl.h"
-
-// vertex cache calls should only be made by the front end
-
-const int NUM_VERTEX_FRAMES = 2;
-
-typedef enum {
- TAG_FREE,
- TAG_USED,
- TAG_FIXED, // for the temp buffers
- TAG_TEMP // in frame temp area, not static area
-} vertBlockTag_t;
-
-typedef struct vertCache_s {
- GLuint vbo;
- void *virtMem; // only one of vbo / virtMem will be set
- bool indexBuffer; // holds indexes instead of vertexes
-
- intptr_t offset;
- int size; // may be larger than the amount asked for, due
- // to round up and minimum fragment sizes
- int tag; // a tag of 0 is a free block
- struct vertCache_s ** user; // will be set to zero when purged
- struct vertCache_s *next, *prev; // may be on the static list or one of the frame lists
- int frameUsed; // it can't be purged if near the current frame
-} vertCache_t;
-
-
-class idVertexCache {
-public:
- void Init();
- void Shutdown();
-
- // just for gfxinfo printing
- bool IsFast();
-
- // called when vertex programs are enabled or disabled, because
- // the cached data is no longer valid
- void PurgeAll();
-
- // Tries to allocate space for the given data in fast vertex
- // memory, and copies it over.
- // Alloc does NOT do a touch, which allows purging of things
- // created at level load time even if a frame hasn't passed yet.
- // These allocations can be purged, which will zero the pointer.
- void Alloc( void *data, int bytes, vertCache_t **buffer, bool indexBuffer = false );
-
- // This will be a real pointer with virtual memory,
- // but it will be an int offset cast to a pointer of ARB_vertex_buffer_object
- void * Position( vertCache_t *buffer );
-
- // if r_useIndexBuffers is enabled, but you need to draw something without
- // an indexCache, this must be called to reset GL_ELEMENT_ARRAY_BUFFER_ARB
- void UnbindIndex();
-
- // automatically freed at the end of the next frame
- // used for specular texture coordinates and gui drawing, which
- // will change every frame.
- // will return NULL if the vertex cache is completely full
- // As with Position(), this may not actually be a pointer you can access.
- vertCache_t * AllocFrameTemp( void *data, int bytes );
-
- // notes that a buffer is used this frame, so it can't be purged
- // out from under the GPU
- void Touch( vertCache_t *buffer );
-
- // this block won't have to zero a buffer pointer when it is purged,
- // but it must still wait for the frames to pass, in case the GPU
- // is still referencing it
- void Free( vertCache_t *buffer );
-
- // updates the counter for determining which temp space to use
- // and which blocks can be purged
- // Also prints debugging info when enabled
- void EndFrame();
-
- // listVertexCache calls this
- void List();
-
-private:
- void InitMemoryBlocks( int size );
- void ActuallyFree( vertCache_t *block );
-
- static idCVar r_showVertexCache;
- static idCVar r_vertexBufferMegs;
-
- int staticCountTotal;
- int staticAllocTotal; // for end of frame purging
-
- int staticAllocThisFrame; // debug counter
- int staticCountThisFrame;
- int dynamicAllocThisFrame;
- int dynamicCountThisFrame;
-
- int currentFrame; // for purgable block tracking
- int listNum; // currentFrame % NUM_VERTEX_FRAMES, determines which tempBuffers to use
-
- bool virtualMemory; // not fast stuff
-
- bool allocatingTempBuffer; // force GL_STREAM_DRAW_ARB
-
- vertCache_t *tempBuffers[NUM_VERTEX_FRAMES]; // allocated at startup
- bool tempOverflow; // had to alloc a temp in static memory
-
- idBlockAlloc headerAllocator;
-
- vertCache_t freeStaticHeaders; // head of doubly linked list
- vertCache_t freeDynamicHeaders; // head of doubly linked list
- vertCache_t dynamicHeaders; // head of doubly linked list
- vertCache_t deferredFreeList; // head of doubly linked list
- vertCache_t staticHeaders; // head of doubly linked list in MRU order,
- // staticHeaders.next is most recently used
-
- int frameBytes; // for each of NUM_VERTEX_FRAMES frames
-};
-
-extern idVertexCache vertexCache;
+/*
+===========================================================================
+
+Doom 3 GPL Source Code
+Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
+
+This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
+
+Doom 3 Source Code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Doom 3 Source Code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Doom 3 Source Code. If not, see .
+
+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.
+
+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.
+
+===========================================================================
+*/
+
+#include "framework/CVarSystem.h"
+#include "renderer/qgl.h"
+
+// vertex cache calls should only be made by the front end
+const int NUM_VERTEX_FRAMES = 2;
+
+typedef enum {
+ TAG_FREE,
+ TAG_USED,
+ TAG_FIXED, // for the temp buffers
+ TAG_TEMP // in frame temp area, not static area
+} vertBlockTag_t;
+
+typedef struct vertCache_s {
+ GLuint vbo;
+ GLenum target;
+ GLenum usage;
+ bool indexBuffer; // holds indexes instead of vertexes
+
+ intptr_t offset;
+ int size; // may be larger than the amount asked for, due
+ // to round up and minimum fragment sizes
+ int tag; // a tag of 0 is a free block
+ struct vertCache_s **user; // will be set to zero when purged
+ struct vertCache_s *next, *prev; // may be on the static list or one of the frame lists
+ int frameUsed; // it can't be purged if near the current frame
+} vertCache_t;
+
+class idVertexCache {
+public:
+ void Init();
+ void Shutdown();
+
+ // called when vertex programs are enabled or disabled, because
+ // the cached data is no longer valid
+ void PurgeAll();
+
+ // Tries to allocate space for the given data in fast vertex
+ // memory, and copies it over.
+ // Alloc does NOT do a touch, which allows purging of things
+ // created at level load time even if a frame hasn't passed yet.
+ // These allocations can be purged, which will zero the pointer.
+ void Alloc( void *data, int bytes, vertCache_t **buffer, bool indexBuffer = false );
+
+ // This will be a real pointer with virtual memory,
+ // but it will be an int offset cast to a pointer of ARB_vertex_buffer_object
+ const void *Position( vertCache_t *buffer );
+
+ // initialize the element array buffers
+ void BindIndex( GLenum target, GLuint vbo );
+
+ // if you need to draw something without an indexCache,
+ // this must be called to reset GL_ELEMENT_ARRAY_BUFFER_ARB
+ void UnbindIndex( GLenum target );
+
+ // automatically freed at the end of the next frame
+ // used for specular texture coordinates and gui drawing, which
+ // will change every frame.
+ // will return NULL if the vertex cache is completely full
+ // As with Position(), this may not actually be a pointer you can access.
+ vertCache_t *AllocFrameTemp( void *data, int bytes );
+
+ // notes that a buffer is used this frame, so it can't be purged
+ // out from under the GPU
+ void Touch( vertCache_t *buffer );
+
+ // this block won't have to zero a buffer pointer when it is purged,
+ // but it must still wait for the frames to pass, in case the GPU
+ // is still referencing it
+ void Free( vertCache_t *buffer );
+
+ // updates the counter for determining which temp space to use
+ // and which blocks can be purged
+ // Also prints debugging info when enabled
+ void EndFrame();
+
+ // listVBOMem calls this
+ void List();
+
+ // showVBOMem calls this
+ void Show();
+
+private:
+ void ActuallyFree( vertCache_t *block );
+
+ static idCVar r_showVertexCache;
+ static idCVar r_useArbBufferRange;
+ static idCVar r_reuseVertexCacheSooner;
+
+ GLuint vertexBuffer; // pointer into vertexbuffer
+ GLuint indexBuffer; // pointer into indexbuffer
+
+ int staticCountTotal;
+ int staticAllocTotal; // for end of frame purging
+
+ int staticAllocThisFrame; // debug counter
+ int staticCountThisFrame;
+ int dynamicAllocThisFrame;
+ int dynamicCountThisFrame;
+
+ int currentFrame; // for purgable block tracking
+ int listNum; // currentFrame % NUM_VERTEX_FRAMES, determines which tempBuffers to use
+
+ bool allocatingTempBuffer; // force GL_STREAM_DRAW_ARB
+
+ vertCache_t *tempBuffers[NUM_VERTEX_FRAMES]; // allocated at startup
+ bool tempOverflow; // had to alloc a temp in static memory
+
+ idBlockAlloc headerAllocator;
+
+ vertCache_t freeStaticHeaders; // head of doubly linked list
+ vertCache_t freeDynamicHeaders; // head of doubly linked list
+ vertCache_t dynamicHeaders; // head of doubly linked list
+ vertCache_t deferredFreeList; // head of doubly linked list
+ vertCache_t staticHeaders; // head of doubly linked list in MRU order, staticHeaders.next is most recently used
+ int frameBytes; // for each of NUM_VERTEX_FRAMES frames
+};
+
+extern idVertexCache vertexCache;
diff --git a/neo/renderer/tr_render.cpp b/neo/renderer/tr_render.cpp
index 2b7e10fce..2be64a75e 100644
--- a/neo/renderer/tr_render.cpp
+++ b/neo/renderer/tr_render.cpp
@@ -61,8 +61,8 @@ void RB_DrawElementsImmediate( const srfTriangles_t *tri ) {
backEnd.pc.c_drawRefVertexes += tri->numVerts;
}
}
-
qglBegin( GL_TRIANGLES );
+
for ( int i = 0 ; i < tri->numIndexes ; i++ ) {
qglTexCoord2fv( tri->verts[ tri->indexes[i] ].st.ToFloatPtr() );
qglVertex3fv( tri->verts[ tri->indexes[i] ].xyz.ToFloatPtr() );
@@ -86,20 +86,22 @@ void RB_DrawElementsWithCounters( const srfTriangles_t *tri ) {
if ( tri->indexes == tri->ambientSurface->indexes ) {
backEnd.pc.c_drawRefIndexes += tri->numIndexes;
}
+
if ( tri->verts == tri->ambientSurface->verts ) {
backEnd.pc.c_drawRefVertexes += tri->numVerts;
}
}
if ( tri->indexCache && r_useIndexBuffers.GetBool() ) {
+ // cast to wrong type int * but needs const GLvoid * the type however is const void * so just remove the cast
qglDrawElements( GL_TRIANGLES,
r_singleTriangle.GetBool() ? 3 : tri->numIndexes,
GL_INDEX_TYPE,
- (int *)vertexCache.Position( tri->indexCache ) );
+ vertexCache.Position( tri->indexCache ) );
backEnd.pc.c_vboIndexes += tri->numIndexes;
} else {
if ( r_useIndexBuffers.GetBool() ) {
- vertexCache.UnbindIndex();
+ vertexCache.UnbindIndex( GL_ELEMENT_ARRAY_BUFFER );
}
qglDrawElements( GL_TRIANGLES,
r_singleTriangle.GetBool() ? 3 : tri->numIndexes,
@@ -128,7 +130,7 @@ void RB_DrawShadowElementsWithCounters( const srfTriangles_t *tri, int numIndexe
backEnd.pc.c_vboIndexes += numIndexes;
} else {
if ( r_useIndexBuffers.GetBool() ) {
- vertexCache.UnbindIndex();
+ vertexCache.UnbindIndex( GL_ELEMENT_ARRAY_BUFFER );
}
qglDrawElements( GL_TRIANGLES,
r_singleTriangle.GetBool() ? 3 : numIndexes,
@@ -137,7 +139,6 @@ void RB_DrawShadowElementsWithCounters( const srfTriangles_t *tri, int numIndexe
}
}
-
/*
===============
RB_RenderTriangleSurface
@@ -150,9 +151,8 @@ void RB_RenderTriangleSurface( const srfTriangles_t *tri ) {
RB_DrawElementsImmediate( tri );
return;
}
+ idDrawVert *ac = ( idDrawVert * )vertexCache.Position( tri->ambientCache );
-
- idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache );
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
qglTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
@@ -232,8 +232,8 @@ be updated after the triangle function completes.
*/
void RB_RenderDrawSurfListWithFunction( drawSurf_t **drawSurfs, int numDrawSurfs,
void (*triFunc_)( const drawSurf_t *) ) {
- int i;
- const drawSurf_t *drawSurf;
+ int i;
+ const drawSurf_t *drawSurf;
backEnd.currentSpace = NULL;
@@ -256,10 +256,11 @@ void RB_RenderDrawSurfListWithFunction( drawSurf_t **drawSurfs, int numDrawSurfs
// change the scissor if needed
if ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals( drawSurf->scissorRect ) ) {
backEnd.currentScissor = drawSurf->scissorRect;
+
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
- backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
- backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
- backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
+ backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
+ backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
+ backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
}
// render it
@@ -268,7 +269,6 @@ void RB_RenderDrawSurfListWithFunction( drawSurf_t **drawSurfs, int numDrawSurfs
if ( drawSurf->space->weaponDepthHack || drawSurf->space->modelDepthHack != 0.0f ) {
RB_LeaveDepthHack();
}
-
backEnd.currentSpace = drawSurf->space;
}
}
@@ -279,8 +279,8 @@ RB_RenderDrawSurfChainWithFunction
======================
*/
void RB_RenderDrawSurfChainWithFunction( const drawSurf_t *drawSurfs,
- void (*triFunc_)( const drawSurf_t *) ) {
- const drawSurf_t *drawSurf;
+ void ( *triFunc_ )( const drawSurf_t *) ) {
+ const drawSurf_t *drawSurf;
backEnd.currentSpace = NULL;
@@ -301,10 +301,11 @@ void RB_RenderDrawSurfChainWithFunction( const drawSurf_t *drawSurfs,
// change the scissor if needed
if ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals( drawSurf->scissorRect ) ) {
backEnd.currentScissor = drawSurf->scissorRect;
+
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
- backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
- backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
- backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
+ backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
+ backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
+ backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
}
// render it
@@ -313,7 +314,6 @@ void RB_RenderDrawSurfChainWithFunction( const drawSurf_t *drawSurfs,
if ( drawSurf->space->weaponDepthHack || drawSurf->space->modelDepthHack != 0.0f ) {
RB_LeaveDepthHack();
}
-
backEnd.currentSpace = drawSurf->space;
}
}
@@ -324,7 +324,7 @@ RB_GetShaderTextureMatrix
======================
*/
void RB_GetShaderTextureMatrix( const float *shaderRegisters,
- const textureStage_t *texture, float matrix[16] ) {
+ const textureStage_t *texture, float matrix[16] ) {
matrix[0] = shaderRegisters[ texture->matrix[0][0] ];
matrix[4] = shaderRegisters[ texture->matrix[0][1] ];
matrix[8] = 0;
@@ -335,15 +335,14 @@ void RB_GetShaderTextureMatrix( const float *shaderRegisters,
if ( matrix[12] < -40 || matrix[12] > 40 ) {
matrix[12] -= (int)matrix[12];
}
-
matrix[1] = shaderRegisters[ texture->matrix[1][0] ];
matrix[5] = shaderRegisters[ texture->matrix[1][1] ];
matrix[9] = 0;
matrix[13] = shaderRegisters[ texture->matrix[1][2] ];
+
if ( matrix[13] < -40 || matrix[13] > 40 ) {
matrix[13] -= (int)matrix[13];
}
-
matrix[2] = 0;
matrix[6] = 0;
matrix[10] = 1;
@@ -364,6 +363,7 @@ void RB_LoadShaderTextureMatrix( const float *shaderRegisters, const textureStag
float matrix[16];
RB_GetShaderTextureMatrix( shaderRegisters, texture, matrix );
+
qglMatrixMode( GL_TEXTURE );
qglLoadMatrixf( matrix );
qglMatrixMode( GL_MODELVIEW );
@@ -397,91 +397,14 @@ void RB_BindVariableStageImage( const textureStage_t *texture, const float *shad
}
} else {
//FIXME: see why image is invalid
- if (texture->image) {
+ if ( texture->image ) {
texture->image->Bind();
}
}
}
-/*
-======================
-RB_BindStageTexture
-======================
-*/
-void RB_BindStageTexture( const float *shaderRegisters, const textureStage_t *texture, const drawSurf_t *surf ) {
- // image
- RB_BindVariableStageImage( texture, shaderRegisters );
-
- // texgens
- if ( texture->texgen == TG_DIFFUSE_CUBE ) {
- qglTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ((idDrawVert *)vertexCache.Position( surf->geo->ambientCache ))->normal.ToFloatPtr() );
- }
- if ( texture->texgen == TG_SKYBOX_CUBE || texture->texgen == TG_WOBBLESKY_CUBE ) {
- qglTexCoordPointer( 3, GL_FLOAT, 0, vertexCache.Position( surf->dynamicTexCoords ) );
- }
- if ( texture->texgen == TG_REFLECT_CUBE ) {
- qglEnable( GL_TEXTURE_GEN_S );
- qglEnable( GL_TEXTURE_GEN_T );
- qglEnable( GL_TEXTURE_GEN_R );
- qglTexGenf( GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT );
- qglTexGenf( GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT );
- qglTexGenf( GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT );
- qglEnableClientState( GL_NORMAL_ARRAY );
- qglNormalPointer( GL_FLOAT, sizeof( idDrawVert ), ((idDrawVert *)vertexCache.Position( surf->geo->ambientCache ))->normal.ToFloatPtr() );
-
- qglMatrixMode( GL_TEXTURE );
- float mat[16];
-
- R_TransposeGLMatrix( backEnd.viewDef->worldSpace.modelViewMatrix, mat );
-
- qglLoadMatrixf( mat );
- qglMatrixMode( GL_MODELVIEW );
- }
-
- // matrix
- if ( texture->hasMatrix ) {
- RB_LoadShaderTextureMatrix( shaderRegisters, texture );
- }
-}
-
-/*
-======================
-RB_FinishStageTexture
-======================
-*/
-void RB_FinishStageTexture( const textureStage_t *texture, const drawSurf_t *surf ) {
- if ( texture->texgen == TG_DIFFUSE_CUBE || texture->texgen == TG_SKYBOX_CUBE
- || texture->texgen == TG_WOBBLESKY_CUBE ) {
- qglTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ),
- (void *)&(((idDrawVert *)vertexCache.Position( surf->geo->ambientCache ))->st) );
- }
-
- if ( texture->texgen == TG_REFLECT_CUBE ) {
- qglDisable( GL_TEXTURE_GEN_S );
- qglDisable( GL_TEXTURE_GEN_T );
- qglDisable( GL_TEXTURE_GEN_R );
- qglTexGenf( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
- qglTexGenf( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
- qglTexGenf( GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
- qglDisableClientState( GL_NORMAL_ARRAY );
-
- qglMatrixMode( GL_TEXTURE );
- qglLoadIdentity();
- qglMatrixMode( GL_MODELVIEW );
- }
-
- if ( texture->hasMatrix ) {
- qglMatrixMode( GL_TEXTURE );
- qglLoadIdentity();
- qglMatrixMode( GL_MODELVIEW );
- }
-}
-
-
-
//=============================================================================================
-
/*
=================
RB_DetermineLightScale
@@ -511,30 +434,33 @@ void RB_DetermineLightScale( void ) {
// if there are no lights, this will remain at 1.0, so GUI-only
// rendering will not lose any bits of precision
- max = 1.0;
+ max = 1.0f;
for ( vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
// lights with no surfaces or shaderparms may still be present
// for debug display
- if ( !vLight->localInteractions && !vLight->globalInteractions
- && !vLight->translucentInteractions ) {
+ if ( !vLight->localInteractions &&
+ !vLight->globalInteractions &&
+ !vLight->translucentInteractions ) {
continue;
}
-
shader = vLight->lightShader;
numStages = shader->GetNumStages();
+
for ( i = 0 ; i < numStages ; i++ ) {
stage = shader->GetStage( i );
+
for ( j = 0 ; j < 3 ; j++ ) {
float v = r_lightScale.GetFloat() * vLight->shaderRegisters[ stage->color.registers[j] ];
+
if ( v > max ) {
max = v;
}
}
}
}
-
backEnd.pc.maxLightValue = max;
+
if ( max <= tr.backEndRendererMaxLight ) {
backEnd.lightScale = r_lightScale.GetFloat();
backEnd.overBright = 1.0;
@@ -553,8 +479,7 @@ Any mirrored or portaled views have already been drawn, so prepare
to actually render the visible surfaces for this view
=================
*/
-void RB_BeginDrawingView (void) {
-
+void RB_BeginDrawingView ( void ) {
const viewDef_t* viewDef = backEnd.viewDef;
// set the modelview matrix for the viewer
@@ -564,15 +489,16 @@ void RB_BeginDrawingView (void) {
// set the window clipping
qglViewport( tr.viewportOffset[0] + viewDef->viewport.x1,
- tr.viewportOffset[1] + viewDef->viewport.y1,
- viewDef->viewport.x2 + 1 - viewDef->viewport.x1,
- viewDef->viewport.y2 + 1 - viewDef->viewport.y1 );
+ tr.viewportOffset[1] + viewDef->viewport.y1,
+ viewDef->viewport.x2 + 1 - viewDef->viewport.x1,
+ viewDef->viewport.y2 + 1 - viewDef->viewport.y1 );
// the scissor may be smaller than the viewport for subviews
qglScissor( tr.viewportOffset[0] + viewDef->viewport.x1 + viewDef->scissor.x1,
- tr.viewportOffset[1] + viewDef->viewport.y1 + viewDef->scissor.y1,
- viewDef->scissor.x2 + 1 - viewDef->scissor.x1,
- viewDef->scissor.y2 + 1 - viewDef->scissor.y1 );
+ tr.viewportOffset[1] + viewDef->viewport.y1 + viewDef->scissor.y1,
+ viewDef->scissor.x2 + 1 - viewDef->scissor.x1,
+ viewDef->scissor.y2 + 1 - viewDef->scissor.y1 );
+
backEnd.currentScissor = viewDef->scissor;
// ensures that depth writes are enabled for the depth clear
@@ -590,10 +516,9 @@ void RB_BeginDrawingView (void) {
qglDisable( GL_DEPTH_TEST );
qglDisable( GL_STENCIL_TEST );
}
-
backEnd.glState.faceCulling = -1; // force face culling to set next time
- GL_Cull( CT_FRONT_SIDED );
+ GL_Cull( CT_FRONT_SIDED );
}
/*
@@ -604,6 +529,7 @@ R_SetDrawInteractions
void R_SetDrawInteraction( const shaderStage_t *surfaceStage, const float *surfaceRegs,
idImage **image, idVec4 matrix[2], float color[4] ) {
*image = surfaceStage->texture.image;
+
if ( surfaceStage->texture.hasMatrix ) {
matrix[0][0] = surfaceRegs[surfaceStage->texture.matrix[0][0]];
matrix[0][1] = surfaceRegs[surfaceStage->texture.matrix[0][1]];
@@ -620,6 +546,7 @@ void R_SetDrawInteraction( const shaderStage_t *surfaceStage, const float *surfa
if ( matrix[0][3] < -40 || matrix[0][3] > 40 ) {
matrix[0][3] -= (int)matrix[0][3];
}
+
if ( matrix[1][3] < -40 || matrix[1][3] > 40 ) {
matrix[1][3] -= (int)matrix[1][3];
}
@@ -638,6 +565,7 @@ void R_SetDrawInteraction( const shaderStage_t *surfaceStage, const float *surfa
if ( color ) {
for ( int i = 0 ; i < 4 ; i++ ) {
color[i] = surfaceRegs[surfaceStage->color.registers[i]];
+
// clamp here, so card with greater range don't look different.
// we could perform overbrighting like we do for lights, but
// it doesn't currently look worth it.
@@ -663,21 +591,24 @@ static void RB_SubmittInteraction( drawInteraction_t *din, void (*DrawInteractio
if ( !din->diffuseImage || r_skipDiffuse.GetBool() ) {
din->diffuseImage = globalImages->blackImage;
}
+
if ( !din->specularImage || r_skipSpecular.GetBool() || din->ambientLight ) {
din->specularImage = globalImages->blackImage;
}
+
if ( !din->bumpImage || r_skipBump.GetBool() ) {
din->bumpImage = globalImages->flatNormalMap;
}
// if we wouldn't draw anything, don't call the Draw function
- if (
- ( ( din->diffuseColor[0] > 0 ||
- din->diffuseColor[1] > 0 ||
- din->diffuseColor[2] > 0 ) && din->diffuseImage != globalImages->blackImage )
- || ( ( din->specularColor[0] > 0 ||
- din->specularColor[1] > 0 ||
- din->specularColor[2] > 0 ) && din->specularImage != globalImages->blackImage ) ) {
+ if ( ( ( din->diffuseColor[0] > 0 ||
+ din->diffuseColor[1] > 0 ||
+ din->diffuseColor[2] > 0 ) &&
+ din->diffuseImage != globalImages->blackImage ) ||
+ ( ( din->specularColor[0] > 0 ||
+ din->specularColor[1] > 0 ||
+ din->specularColor[2] > 0 ) &&
+ din->specularImage != globalImages->blackImage ) ) {
DrawInteraction( din );
}
}
@@ -697,6 +628,7 @@ void RB_CreateSingleDrawInteractions( const drawSurf_t *surf, void (*DrawInterac
const idMaterial *lightShader = vLight->lightShader;
const float *lightRegs = vLight->shaderRegisters;
drawInteraction_t inter;
+
inter.diffuseMatrix[0].Zero();
inter.diffuseMatrix[1].Zero();
inter.specularMatrix[0].Zero();
@@ -709,16 +641,18 @@ void RB_CreateSingleDrawInteractions( const drawSurf_t *surf, void (*DrawInterac
// change the matrix and light projection vectors if needed
if ( surf->space != backEnd.currentSpace ) {
backEnd.currentSpace = surf->space;
+
qglLoadMatrixf( surf->space->modelViewMatrix );
}
// change the scissor if needed
if ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals( surf->scissorRect ) ) {
backEnd.currentScissor = surf->scissorRect;
+
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
- backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
- backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
- backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
+ backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
+ backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
+ backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
}
// hack depth range if needed
@@ -729,7 +663,6 @@ void RB_CreateSingleDrawInteractions( const drawSurf_t *surf, void (*DrawInterac
if ( surf->space->modelDepthHack ) {
RB_EnterModelDepthHack( surf->space->modelDepthHack );
}
-
inter.surf = surf;
inter.lightFalloffImage = vLight->falloffImage;
@@ -752,16 +685,15 @@ void RB_CreateSingleDrawInteractions( const drawSurf_t *surf, void (*DrawInterac
if ( !lightRegs[ lightStage->conditionRegister ] ) {
continue;
}
-
inter.lightImage = lightStage->texture.image;
memcpy( inter.lightProjection, lightProject, sizeof( inter.lightProjection ) );
+
// now multiply the texgen by the light texture matrix
if ( lightStage->texture.hasMatrix ) {
RB_GetShaderTextureMatrix( lightRegs, &lightStage->texture, backEnd.lightTextureMatrix );
- RB_BakeTextureMatrixIntoTexgen( reinterpret_cast(inter.lightProjection), backEnd.lightTextureMatrix );
+ RB_BakeTextureMatrixIntoTexgen( reinterpret_cast( inter.lightProjection ), backEnd.lightTextureMatrix );
}
-
inter.bumpImage = NULL;
inter.specularImage = NULL;
inter.diffuseImage = NULL;
@@ -807,7 +739,7 @@ void RB_CreateSingleDrawInteractions( const drawSurf_t *surf, void (*DrawInterac
RB_SubmittInteraction( &inter, DrawInteraction );
}
R_SetDrawInteraction( surfaceStage, surfaceRegs, &inter.diffuseImage,
- inter.diffuseMatrix, inter.diffuseColor.ToFloatPtr() );
+ inter.diffuseMatrix, inter.diffuseColor.ToFloatPtr() );
inter.diffuseColor[0] *= lightColor[0];
inter.diffuseColor[1] *= lightColor[1];
inter.diffuseColor[2] *= lightColor[2];
@@ -824,7 +756,7 @@ void RB_CreateSingleDrawInteractions( const drawSurf_t *surf, void (*DrawInterac
RB_SubmittInteraction( &inter, DrawInteraction );
}
R_SetDrawInteraction( surfaceStage, surfaceRegs, &inter.specularImage,
- inter.specularMatrix, inter.specularColor.ToFloatPtr() );
+ inter.specularMatrix, inter.specularColor.ToFloatPtr() );
inter.specularColor[0] *= lightColor[0];
inter.specularColor[1] *= lightColor[1];
inter.specularColor[2] *= lightColor[2];
@@ -874,12 +806,9 @@ void RB_DrawView( const void *data ) {
parms->connectedAreas = origParms.connectedAreas;
for( viewEntity_t* vModel = parms->viewEntitys ; vModel ; vModel = vModel->next ) {
- myGlMultMatrix( vModel->modelMatrix,
- parms->worldSpace.modelViewMatrix,
- vModel->modelViewMatrix );
+ R_MatrixMultiply( vModel->modelMatrix, parms->worldSpace.modelViewMatrix, vModel->modelViewMatrix );
}
}
-
backEnd.viewDef = cmd->viewDef;
// we will need to do a new copyTexSubImage of the screen
@@ -903,7 +832,6 @@ void RB_DrawView( const void *data ) {
if ( r_skipRenderContext.GetBool() && backEnd.viewDef->viewEntitys ) {
GLimp_DeactivateContext();
}
-
backEnd.pc.c_surfaces += backEnd.viewDef->numDrawSurfs;
RB_ShowOverdraw();