Jump to content
The Dark Mod Forums

TDM Engine Development Page


zergrush

Recommended Posts

code for MH's improved VBO here.

 

/*
===========================================================================
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 <http://www.gnu.org/licenses/>.
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 "precompiled.h"
#include "tr_local.h"
static const int  FRAME_MEMORY_BYTES = 0x400000;
static const int  EXPAND_HEADERS = 32;
// in case r_useArbBufferRange fails (happens on some AMD cards) turn it off.
idCVar idVertexCache::r_showVertexCache("r_showVertexCache", "0", CVAR_INTEGER | CVAR_RENDERER, "show vertex cache");
idCVar idVertexCache::r_useArbBufferRange("r_useArbBufferRange", "1", CVAR_BOOL | CVAR_RENDERER, "use ARB_map_buffer_range for optimization");
idCVar idVertexCache::r_reuseVertexCacheSooner("r_reuseVertexCacheSooner", "1", CVAR_BOOL | CVAR_RENDERER, "reuse vertex buffers as soon as possible after freeing");
idVertexCache	 vertexCache;
/*
==============
R_ListVBOMem_f
==============
*/
static 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--;
 // only free the buffer if its still active.
 if (virtualMemory) {
  common->DPrintf("Destroying Virtual Memory\n");
  delete[] 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 (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
==============
*/
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 (void *)buffer->offset;
}
// virtual memory is a real pointer
return (void *)((byte *)buffer->virtMem + buffer->offset);
}
//================================================================================
// dont make these static or the engine will crash.
GLuint vertexBuffer = 0;
GLuint indexBuffer = 0;
/*
===========
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 
  glBindBufferARB(target, vbo);
  vertexBuffer = vbo;
  return;
 }
 break;
case GL_ELEMENT_ARRAY_BUFFER:
 if (indexBuffer != vbo) {
  // this happens more often than you might think 
  glBindBufferARB(target, vbo);
  indexBuffer = vbo;
  return;
 }
 break;
default:
 common->FatalError("BindIndex : unknown buffer target : %i\n", static_cast<int>(target));
 break;
}
}
/*
===========
idVertexCache::UnbindIndex
Makes sure it only deallocates the right buffers once.
===========
*/
void idVertexCache::UnbindIndex(GLenum target) {
switch (target) {
case GL_ARRAY_BUFFER:
 if (vertexBuffer != 0) {
  // this happens more often than you might think 
  glBindBufferARB(target, 0);
  vertexBuffer = 0;
  return;
 }
 break;
case GL_ELEMENT_ARRAY_BUFFER:
 if (indexBuffer != 0) {
  // this happens more often than you might think 
  glBindBufferARB(target, 0);
  indexBuffer = 0;
  return;
 }
 break;
default:
 common->FatalError("UnbindIndex : unknown buffer target : %i\n", static_cast<int>(target));
 break;
}
}
//================================================================================
/*
===========
idVertexCache::Init
===========
*/
void idVertexCache::Init() {
cmdSystem->AddCommand("ListVBOMem", R_ListVBOMem_f, CMD_FL_RENDERER, "lists Objects Allocated in Vertex Cache");
// use ARB_vertex_buffer_object unless explicitly disabled
if (glConfig.ARBVertexBufferObjectAvailable) {
 virtualMemory = false;
 r_useIndexBuffers.SetBool(true);
 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;
// 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();
  if (!virtualMemory) {
   glGenBuffers(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);
 glBufferDataARB(target, static_cast<GLsizeiptr>(size), NULL, usage);
 glBufferDataARB(target, static_cast<GLsizeiptr>(size), data, usage);
}
else {
 // use C++ allocation
 block->virtMem = new byte[size];
 SIMDProcessor->Memcpy(block->virtMem, data, size);
}
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::MapBufferRange
MH's Version fast on Nvidia But may fail on AMD.
===========
*/
void idVertexCache::MapBufferRange(vertCache_t *buffer, void *data, int size) {
BindIndex(GL_ARRAY_BUFFER, buffer->vbo);
if (glConfig.ARBMapBufferRangeAvailable && r_useArbBufferRange.GetBool()) {
 GLbitfield access = (GL_MAP_WRITE_BIT | ((buffer->offset == 0) ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_RANGE_BIT));
 GLvoid	  *ptr = glMapBufferRange(GL_ARRAY_BUFFER, static_cast<GLintptr>(buffer->offset), static_cast<GLsizeiptr>(size), access);
 // try to get an unsynchronized map if at all possible
 if (ptr) {
  // if the buffer has wrapped then we orphan it
  SIMDProcessor->Memcpy(static_cast<byte *>(ptr), data, size);
  glUnmapBuffer(GL_ARRAY_BUFFER);
 }
 else {
  glBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptrARB>(buffer->offset), static_cast<GLsizeiptr>(size), data);
 }
}
else {
 // just upload the whole shebang.
 glBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptrARB>(buffer->offset), static_cast<GLsizeiptr>(size), data);
}
}
/*
===========
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;
// mh code start
if (block->vbo) {
 MapBufferRange(block, data, size);
}
else if (block->virtMem) {
 SIMDProcessor->Memcpy(static_cast<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);
}
// unbind vertex buffers so normal virtual memory will be used
if (!virtualMemory) {
 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;
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);
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");
}
common->Printf("Index buffers are accelerated.\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 <http://www.gnu.org/licenses/>.
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.
===========================================================================
*/
// 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;
void    *virtMem;		 // only one of vbo / virtMem will be set
bool    indexBuffer;	  // holds indexes instead of vertexes
int	 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);
// 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);
// MH's MapBufferRange.
void   MapBufferRange(vertCache_t *buffer, void *data, int size);
// 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();
private:
void   ActuallyFree(vertCache_t *block);
static idCVar   r_showVertexCache;
static idCVar   r_useArbBufferRange;
static idCVar   r_reuseVertexCacheSooner;
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<vertCache_t, 1024> 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;

 

Keep in mind this is from revelation which uses glew as the opengl call wrapper so in case you dont want to use glew you have to prefix the opengl calls with a q like this qglBufferSubData etc.

the buffer range protos are also not present in vanilla so you have to make pointers for them in rendersystem_init.cpp qgl.h and where not.

So just use glew if you are not :) will save you a ton of work the next time you want to add some new opengl functionality.

Link to comment
Share on other sites

Serpentine did some changes to our Vertex Cache code too so we'd have to look into whether his changes are relevant after this so

we may possibly benefit from both.

Please visit TDM's IndieDB site and help promote the mod:

 

http://www.indiedb.com/mods/the-dark-mod

 

(Yeah, shameless promotion... but traffic is traffic folks...)

Link to comment
Share on other sites

A proper patch to the TDM engine would be a lot more helpful than these random bits of code that can't be just integrated. I don't think we have anyone who would have the time to clean up this mess.

 

Edit: By mess I mean both the "mess" in the D3 engine, the "d3 engine plus some TDM modificatons" and the new code, which looks quite a bit experimental. The mix of these three is a lot of ugh :)

Edited by Tels

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

A proper patch to the TDM engine would be a lot more helpful than these random bits of code that can't be just integrated. I don't think we have anyone who would have the time to clean up this mess.

 

It would be even better if someone forked TDM, implemented whatever they keep talking about and throwing random code snippets, make a test case to show what's going on visually, and how different it is from the original TDM, gauge performance impact, then integrate back to TDM, if it's a benefit on all fronts. Otherwise it's no use.

Link to comment
Share on other sites

I guess, if Revelator wouldn't mind, giving him SVN access and having him make a TDM branch with the changes he recommends might be the best way to go

since we wouldn't have to keep searching for dependencies in the supplied parts (etc)? Mh's VBO fix was originally isolated to the VertexCache.cpp and VertexCache.h

blocks (as were Serpentine's changes) so there's no dependency hunting there but if Revelator's code is better then perhaps it's better to get a full fledged branch made?

 

Edit:

 

I also made a clone of the Read Only SVN to github last week so he could make a branch there:

 

https://github.com/nbohr1more/TheDarkMod_SVN_Unofficial-1

Edited by nbohr1more
  • Like 1

Please visit TDM's IndieDB site and help promote the mod:

 

http://www.indiedb.com/mods/the-dark-mod

 

(Yeah, shameless promotion... but traffic is traffic folks...)

Link to comment
Share on other sites

Do you want GLEW support btw ? as said it makes it easier on the next guy to yank in new opengl functionality.

 

I would say yes, I believe we took some steps to modernize the included OpenGL foundations in v1.08 so that would be

a natural progression. That will also make the game better suited to Linux \ Cross Platform correct?

Please visit TDM's IndieDB site and help promote the mod:

 

http://www.indiedb.com/mods/the-dark-mod

 

(Yeah, shameless promotion... but traffic is traffic folks...)

Link to comment
Share on other sites

I guess, if Revelator wouldn't mind, giving him SVN access and having him make a TDM branch with the changes he recommends might be the best way to go

since we wouldn't have to keep searching for dependencies in the supplied parts (etc)? Mh's VBO fix was originally isolated to the VertexCache.cpp and VertexCache.h

blocks (as were Serpentine's changes) so there's no dependency hunting there but if Revelator's code is better then perhaps it's better to get a full fledged branch made?

 

Yeah, SVN branches are exactly for this type of experimentation. And when revelators work does work, we can just merge the branch back in and more developers can test it.

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Do you want GLEW support btw ? as said it makes it easier on the next guy to yank in new opengl functionality.

 

Do we want GLEW support? Actually, I would have to google what GLEW is :D But I guess when it works on all platforms, it can't hurt.

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Btw, revelator, if you want a testmap with a huge amount of entities, than just post here. I can dust of (or recrete) a SEED map, that would show some bottlenecks in rendering performance.

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Btw, revelator, if you want a testmap with a huge amount of entities, than just post here. I can dust of (or recrete) a SEED map, that would show some bottlenecks in rendering performance.

 

Btw, something just occurred to me. Doom 3 culls on per-surface basis. If you have one massive surface, and only a little bit of it in the view, afaik engine won't cull it. That might be the reason SEED is slow, if all meshes are combined into one surface. I don't know how SEED works exactly, but it would make sense to combine all of the models into one render entity, not into one surface. Again, it just occurred to me, and I might be in the wrong :)

Link to comment
Share on other sites

Btw, something just occurred to me. Doom 3 culls on per-surface basis. If you have one massive surface, and only a little bit of it in the view, afaik engine won't cull it. That might be the reason SEED is slow, if all meshes are combined into one surface. I don't know how SEED works exactly, but it would make sense to combine all of the models into one render entity, not into one surface. Again, it just occurred to me, and I might be in the wrong :)

 

Well, yeah, back then the ModelGenerator was a giant hack and it hasn't been revisited since then. So it still merges everything into one surface. I'm not sure if merging everything into one renderentity would have been faster.

 

The "slowness" of the combination isn't during rendering - this is a lot faster than not merging (and also overcomes the render model limit (which was since then raised) and the entity limit.

 

What is so slow is rebuilding the model when one of the "submodels" change their LOD stage. If none of them ever change, everything is just as fast as always.

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

... So it still merges everything into one surface. ...

 

What is so slow is rebuilding the model when one of the "submodels" change their LOD stage. If none of them ever change, everything is just as fast as always.

 

It seems like it would rebuild entire surface, and with render entity, it would only update that particular model (surface). So it seems like it would be faster.

Link to comment
Share on other sites

It seems like it would rebuild entire surface, and with render entity, it would only update that particular model (surface). So it seems like it would be faster.

 

Yeah, that's true. But if it would still render as fast in that case is not known. One would have to implement and try it. But I think the modelmanager is the wrong approach, anyway. The engine (renderer) should deal with that transparently in the backend.

 

However, back then, we didn't have the source to the renderer, so it was the best that we could do (I do keep repeating myself here :) And now we have the source, but as little or even less manpower.

"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recent Status Updates

    • taffernicus

      i am so euphoric to see new FMs keep coming out and I am keen to try it out in my leisure time, then suddenly my PC is spouting a couple of S.M.A.R.T errors...
      tbf i cannot afford myself to miss my network emulator image file&progress, important ebooks, hyper-v checkpoint & hyper-v export and the precious thief & TDM gamesaves. Don't fall yourself into & lay your hands on crappy SSD
       
      · 3 replies
    • OrbWeaver

      Does anyone actually use the Normalise button in the Surface inspector? Even after looking at the code I'm not quite sure what it's for.
      · 7 replies
    • Ansome

      Turns out my 15th anniversary mission idea has already been done once or twice before! I've been beaten to the punch once again, but I suppose that's to be expected when there's over 170 FMs out there, eh? I'm not complaining though, I love learning new tricks and taking inspiration from past FMs. Best of luck on your own fan missions!
      · 4 replies
    • The Black Arrow

      I wanna play Doom 3, but fhDoom has much better features than dhewm3, yet fhDoom is old, outdated and probably not supported. Damn!
      Makes me think that TDM engine for Doom 3 itself would actually be perfect.
      · 6 replies
    • Petike the Taffer

      Maybe a bit of advice ? In the FM series I'm preparing, the two main characters have the given names Toby and Agnes (it's the protagonist and deuteragonist, respectively), I've been toying with the idea of giving them family names as well, since many of the FM series have named protagonists who have surnames. Toby's from a family who were usually farriers, though he eventually wound up working as a cobbler (this serves as a daylight "front" for his night time thieving). Would it make sense if the man's popularly accepted family name was Farrier ? It's an existing, though less common English surname, and it directly refers to the profession practiced by his relatives. Your suggestions ?
      · 9 replies
×
×
  • Create New...