VBO code
This commit is contained in:
parent
07b04e8cdb
commit
827204fd68
4 changed files with 682 additions and 30 deletions
41
bumpdriver.h
41
bumpdriver.h
|
@ -1,28 +1,32 @@
|
||||||
|
|
||||||
|
DriverPtr GL_WrapUserPointer(void *p);
|
||||||
|
DriverPtr GL_OffsetDriverPtr(DriverPtr p, int offset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives a definition of the vertices passed to the shedule* functions.
|
* Gives a definition of the vertices passed to the shedule* functions.
|
||||||
* Use NULL if the data is not available/applicable to the suff you send.
|
* Use NULL if the data is not available/applicable to the suff you send.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
float *vertices;
|
DriverPtr vertices;
|
||||||
int vertexstride;
|
int vertexstride;
|
||||||
|
|
||||||
float *texcoords;
|
DriverPtr texcoords;
|
||||||
int texcoordstride;
|
int texcoordstride;
|
||||||
|
|
||||||
float *lightmapcoords;
|
DriverPtr lightmapcoords;
|
||||||
int lightmapstride;
|
int lightmapstride;
|
||||||
|
|
||||||
float *tangents;
|
DriverPtr tangents;
|
||||||
int tangentstride;
|
int tangentstride;
|
||||||
|
|
||||||
float *binormals;
|
DriverPtr binormals;
|
||||||
int binormalstride;
|
int binormalstride;
|
||||||
|
|
||||||
float *normals;
|
DriverPtr normals;
|
||||||
int normalstride;
|
int normalstride;
|
||||||
|
|
||||||
unsigned char *colors;
|
DriverPtr colors;
|
||||||
int colorstride;
|
int colorstride;
|
||||||
|
|
||||||
} vertexdef_t;
|
} vertexdef_t;
|
||||||
|
@ -33,18 +37,6 @@ typedef struct {
|
||||||
vec3_t objectvieworg;
|
vec3_t objectvieworg;
|
||||||
} lightobject_t;
|
} lightobject_t;
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines driver managed memory types
|
|
||||||
*/
|
|
||||||
typedef enum {DM_SLOWREADWRITE, DM_SLOWREAD, DM_NORMAL} drivermem_t;
|
|
||||||
|
|
||||||
//DM_SLOWREADWRITE: the memory is slow in writing and reading, won't be updated outside of
|
|
||||||
//the driver much
|
|
||||||
//DM_SLOWREAD: the memory is slow in reading (uncached or worse...), won't be read outside
|
|
||||||
//of the driver much. It supports decent writing speeds. (This is probably what you'll want
|
|
||||||
//most of the time.)
|
|
||||||
//DM_NORMAL: Fast reading and writing
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a generic bumpdriver struct, it contains al the driver routines
|
* This is a generic bumpdriver struct, it contains al the driver routines
|
||||||
*/
|
*/
|
||||||
|
@ -56,18 +48,7 @@ typedef struct {
|
||||||
void (*initDriver) (void);
|
void (*initDriver) (void);
|
||||||
void (*freeDriver) (void);
|
void (*freeDriver) (void);
|
||||||
|
|
||||||
//gets a pointer to driver memory, it can just return system memory too if the driver
|
|
||||||
//doesn't support it.
|
|
||||||
void *(*getDriverMem) (size_t size, drivermem_t hint);
|
|
||||||
|
|
||||||
//frees all driver mem
|
|
||||||
//FIXME: do we need real deallocation support
|
|
||||||
void (*freeAllDriverMem) (void);
|
|
||||||
|
|
||||||
//FIXME: Do we need fence like support?
|
|
||||||
|
|
||||||
//drawing code
|
//drawing code
|
||||||
|
|
||||||
void (*drawTriangleListBase) (vertexdef_t *verts, int *indecies, int numIndecies, shader_t *shader, int lightmapIndex);//-1 for no lightmap
|
void (*drawTriangleListBase) (vertexdef_t *verts, int *indecies, int numIndecies, shader_t *shader, int lightmapIndex);//-1 for no lightmap
|
||||||
void (*drawTriangleListBump) (const vertexdef_t *verts, int *indecies, int numIndecies, shader_t *shader, const transform_t *tr, const lightobject_t *lo);
|
void (*drawTriangleListBump) (const vertexdef_t *verts, int *indecies, int numIndecies, shader_t *shader, const transform_t *tr, const lightobject_t *lo);
|
||||||
void (*drawTriangleListSys) (vertexdef_t *verts, int *indecies, int numIndecies, shader_t *shader);
|
void (*drawTriangleListSys) (vertexdef_t *verts, int *indecies, int numIndecies, shader_t *shader);
|
||||||
|
|
382
gl_drivermem.c
Normal file
382
gl_drivermem.c
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2003 Tenebrae Team
|
||||||
|
|
||||||
|
This program 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 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Driver managed vertex memory
|
||||||
|
|
||||||
|
The all routines return DriverPtr's that can be translated and used trough
|
||||||
|
the use of these routines.
|
||||||
|
A segment is just a single opengl VBO object, this should abstract the memory management
|
||||||
|
spanning difference VBO objects because of driver maximum VBO sizes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "quakedef.h"
|
||||||
|
|
||||||
|
//number of segments to keep track of
|
||||||
|
#define MAX_SEGMENTS 256
|
||||||
|
//maximum size of a segment (this maybe driver dependent)
|
||||||
|
#define MAX_SEGMENTSIZE (1024*1024*16)
|
||||||
|
//size of the allocated segments
|
||||||
|
#define OPTIMAL_SEGMENTSIZE (1024*1024*4)
|
||||||
|
|
||||||
|
DriverPtr nullDriver = {0, 0}; //Segment
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int size;
|
||||||
|
int freeOffset;
|
||||||
|
int mapCount;
|
||||||
|
qboolean isSystemMem; //This segment is system memory and not vbo mem (not supported or no free vbo space left)
|
||||||
|
void *nonVboData; //only nonull if isSystemMem is true
|
||||||
|
void *mapData; //only nonull if isSystemMem is true
|
||||||
|
} segmentdescriptor_t;
|
||||||
|
|
||||||
|
static segmentdescriptor_t segmentDescriptors[MAX_SEGMENTS];
|
||||||
|
static int numSegments = 0;
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
|
||||||
|
Our own little abstraction layer that abstracts vbo and system mem
|
||||||
|
if vbo is not available.
|
||||||
|
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
static int GL_CreateSegment(size_t size, void *data, int usage) {
|
||||||
|
void *nonVboData;
|
||||||
|
qboolean isSystemMem = false;
|
||||||
|
|
||||||
|
if (numSegments >= MAX_SEGMENTS)
|
||||||
|
Sys_Error("GL_CreateSegment: No segments left\n");
|
||||||
|
|
||||||
|
if (gl_vbo) {
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, numSegments);
|
||||||
|
qglBufferDataARB(GL_ARRAY_BUFFER_ARB, size, data,usage);
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
nonVboData = NULL;
|
||||||
|
//fixme check for OUT_OF_MEMORY error
|
||||||
|
} else {
|
||||||
|
nonVboData = malloc(size);
|
||||||
|
isSystemMem = true;
|
||||||
|
if (data) {
|
||||||
|
memcpy(nonVboData,data, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentDescriptors[numSegments].mapCount = 0;
|
||||||
|
segmentDescriptors[numSegments].nonVboData = nonVboData;
|
||||||
|
segmentDescriptors[numSegments].isSystemMem = isSystemMem;
|
||||||
|
segmentDescriptors[numSegments].size = size;
|
||||||
|
segmentDescriptors[numSegments].freeOffset = 0;
|
||||||
|
numSegments++;
|
||||||
|
return numSegments-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void GL_FreeSegment(int segment) {
|
||||||
|
segmentdescriptor_t *s;
|
||||||
|
|
||||||
|
if (segment != (numSegments-1))
|
||||||
|
Sys_Error("GL_FreeSegment: Only last created segment can be freed\n");
|
||||||
|
|
||||||
|
s = &segmentDescriptors[segment];
|
||||||
|
if (s->isSystemMem) {
|
||||||
|
if (s->nonVboData)
|
||||||
|
free(s->nonVboData);
|
||||||
|
} else {
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, segment);
|
||||||
|
qglBufferDataARB(GL_ARRAY_BUFFER_ARB, 0, NULL, GL_STREAM_DRAW_ARB);
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
numSegments--;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
void *GL_MapToUserSpace(DriverPtr p) {
|
||||||
|
byte *data;
|
||||||
|
segmentdescriptor_t *s;
|
||||||
|
|
||||||
|
if ((p.segment < 0) || (p.segment >= numSegments)) Sys_Error("GL_MapToUserSpace: Invalid segment");
|
||||||
|
s = &segmentDescriptors[p.segment];
|
||||||
|
|
||||||
|
if (s->isSystemMem) {
|
||||||
|
return ((byte *)s->nonVboData)+p.offset;
|
||||||
|
} else {
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, p.segment);
|
||||||
|
data = qglMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB);
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
return data+p.offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_UnmapFromUserSpace(DriverPtr p) {
|
||||||
|
segmentdescriptor_t *s;
|
||||||
|
|
||||||
|
if ((p.segment < 0) || (p.segment >= numSegments)) Sys_Error("GL_UnmapFromUserSpace: Invalid segment");
|
||||||
|
s = &segmentDescriptors[p.segment];
|
||||||
|
if (!s->isSystemMem) {
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, p.segment);
|
||||||
|
if (!qglUnmapBufferARB(GL_ARRAY_BUFFER_ARB)) {
|
||||||
|
Sys_Error("GL_UnmapFromUserSpace: Buffer data store was corrupted\n");
|
||||||
|
}
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
Maps the driver mem pointer to the proces's adress space
|
||||||
|
*/
|
||||||
|
void *GL_MapToUserSpace(DriverPtr p) {
|
||||||
|
byte *data;
|
||||||
|
segmentdescriptor_t *s;
|
||||||
|
|
||||||
|
if ((p.segment < 0) || (p.segment >= numSegments)) Sys_Error("GL_MapToUserSpace: Invalid segment");
|
||||||
|
s = &segmentDescriptors[p.segment];
|
||||||
|
|
||||||
|
if (s->isSystemMem) {
|
||||||
|
return ((byte *)s->nonVboData)+p.offset;
|
||||||
|
} else {
|
||||||
|
if (s->mapCount == 0) {
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, p.segment);
|
||||||
|
s->mapData = qglMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB);
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
}
|
||||||
|
s->mapCount++;
|
||||||
|
return (byte *)s->mapData+p.offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Unmaps the driver mem pointer from the adress space
|
||||||
|
This MUST! be called before the driver can use the memory
|
||||||
|
*/
|
||||||
|
void GL_UnmapFromUserSpace(DriverPtr p) {
|
||||||
|
segmentdescriptor_t *s;
|
||||||
|
|
||||||
|
if ((p.segment < 0) || (p.segment >= numSegments)) Sys_Error("GL_UnmapFromUserSpace: Invalid segment");
|
||||||
|
s = &segmentDescriptors[p.segment];
|
||||||
|
if (!s->isSystemMem) {
|
||||||
|
s->mapCount--;
|
||||||
|
if (s->mapCount == 0) {
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, p.segment);
|
||||||
|
if (!qglUnmapBufferARB(GL_ARRAY_BUFFER_ARB)) {
|
||||||
|
Sys_Error("GL_UnmapFromUserSpace: Buffer data store was corrupted\n");
|
||||||
|
}
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Converts a normal pointer to a VBO compatible pointer (used for some of the geometry that is still in system mem)
|
||||||
|
*/
|
||||||
|
DriverPtr GL_WrapUserPointer(void *p) {
|
||||||
|
DriverPtr dr;
|
||||||
|
dr.segment = 0;
|
||||||
|
dr.offset = (byte *)p-NULL;
|
||||||
|
return dr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This is probably faster than doing a Map and then a memcpy and then an unmap
|
||||||
|
*/
|
||||||
|
void drivermemcpy(DriverPtr dest, void *src, size_t size) {
|
||||||
|
segmentdescriptor_t *s;
|
||||||
|
|
||||||
|
if ((dest.segment < 0) || (dest.segment >= numSegments)) Sys_Error("GL_UnmapFromUserSpace: Invalid segment");
|
||||||
|
s = &segmentDescriptors[dest.segment];
|
||||||
|
if (!s->isSystemMem) {
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, dest.segment);
|
||||||
|
qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, dest.offset, size, src);
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
} else {
|
||||||
|
memcpy((byte *)s->nonVboData+dest.offset, src, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//This will have less vbo state changes, but the spec mentions binding another is cheap anyway
|
||||||
|
//it's the pointer changing that is expensive
|
||||||
|
#define RESET_VBO 1
|
||||||
|
|
||||||
|
void GL_TexCoordPointer(GLint size, GLenum type, GLsizei stride, DriverPtr p) {
|
||||||
|
segmentdescriptor_t *s;
|
||||||
|
if ((p.segment < 0) || (p.segment >= numSegments)) Sys_Error("GL_MapToUserSpace: Invalid segment");
|
||||||
|
s = &segmentDescriptors[p.segment];
|
||||||
|
|
||||||
|
if (s->isSystemMem) {
|
||||||
|
if (gl_vbo) qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
glTexCoordPointer(size, type, stride, (byte *)s->nonVboData+p.offset);
|
||||||
|
} else {
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, p.segment);
|
||||||
|
glTexCoordPointer(size, type, stride, (byte *)s->nonVboData+p.offset);
|
||||||
|
#ifdef RESET_VBO
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_VertexPointer(GLint size, GLenum type, GLsizei stride, DriverPtr p) {
|
||||||
|
segmentdescriptor_t *s;
|
||||||
|
if ((p.segment < 0) || (p.segment >= numSegments)) Sys_Error("GL_MapToUserSpace: Invalid segment");
|
||||||
|
s = &segmentDescriptors[p.segment];
|
||||||
|
|
||||||
|
if (s->isSystemMem) {
|
||||||
|
if (gl_vbo) qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
glVertexPointer(size, type, stride, (byte *)s->nonVboData+p.offset);
|
||||||
|
} else {
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, p.segment);
|
||||||
|
glVertexPointer(size, type, stride, (byte *)s->nonVboData+p.offset);
|
||||||
|
#ifdef RESET_VBO
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_NormalPointer(GLenum type, GLsizei stride, DriverPtr p) {
|
||||||
|
segmentdescriptor_t *s;
|
||||||
|
if ((p.segment < 0) || (p.segment >= numSegments)) Sys_Error("GL_MapToUserSpace: Invalid segment");
|
||||||
|
s = &segmentDescriptors[p.segment];
|
||||||
|
|
||||||
|
if (s->isSystemMem) {
|
||||||
|
if (gl_vbo) qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
glNormalPointer(type, stride, (byte *)s->nonVboData+p.offset);
|
||||||
|
} else {
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, p.segment);
|
||||||
|
glNormalPointer(type, stride, (byte *)s->nonVboData+p.offset);
|
||||||
|
#ifdef RESET_VBO
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_ColorPointer(GLint size, GLenum type, GLsizei stride, DriverPtr p) {
|
||||||
|
segmentdescriptor_t *s;
|
||||||
|
if ((p.segment < 0) || (p.segment >= numSegments)) Sys_Error("GL_MapToUserSpace: Invalid segment");
|
||||||
|
s = &segmentDescriptors[p.segment];
|
||||||
|
|
||||||
|
if (s->isSystemMem) {
|
||||||
|
if (gl_vbo) qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
glColorPointer(size, type, stride, (byte *)s->nonVboData+p.offset);
|
||||||
|
} else {
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, p.segment);
|
||||||
|
glColorPointer(size, type, stride, (byte *)s->nonVboData+p.offset);
|
||||||
|
#ifdef RESET_VBO
|
||||||
|
qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DriverPtr GL_OffsetDriverPtr (DriverPtr p, int offset) {
|
||||||
|
p.offset+=offset;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
|
||||||
|
Driver memory public interface
|
||||||
|
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
Allocates size bytes of memory and copies data to the block.
|
||||||
|
Returns a pointer to driver memory.
|
||||||
|
This memory is "slow" if you use the map routines, it should
|
||||||
|
not be written or read often.
|
||||||
|
This memory cannot be freed, it is freed automatically when
|
||||||
|
GL_FreeAll is called.
|
||||||
|
*/
|
||||||
|
DriverPtr GL_StaticAlloc(size_t size, void *data) {
|
||||||
|
segmentdescriptor_t *s;
|
||||||
|
int i, segment;
|
||||||
|
DriverPtr p;
|
||||||
|
|
||||||
|
if (size > MAX_SEGMENTSIZE)
|
||||||
|
Sys_Error("GL_StaticAlloc: Tried to allocate more than MAX_SEGMENTSIZE vertex buffer memory\n");
|
||||||
|
|
||||||
|
//If it's to big put it in a separate segment
|
||||||
|
if (size > OPTIMAL_SEGMENTSIZE-(OPTIMAL_SEGMENTSIZE/4)) {
|
||||||
|
int segment = GL_CreateSegment(size, data, GL_STATIC_DRAW_ARB);
|
||||||
|
segmentDescriptors[segment].freeOffset = size;
|
||||||
|
p.segment = segment;
|
||||||
|
p.offset = 0;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Find space in an existing segment
|
||||||
|
for (i=0, s=segmentDescriptors; i<numSegments; i++, s++) {
|
||||||
|
if (size < (s->size - s->freeOffset)) {
|
||||||
|
p.segment = i;
|
||||||
|
p.offset = s->freeOffset;
|
||||||
|
s->freeOffset += size;
|
||||||
|
drivermemcpy(p, data, size);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Allocate a new segment
|
||||||
|
segment = GL_CreateSegment(OPTIMAL_SEGMENTSIZE, NULL, GL_STATIC_DRAW_ARB);
|
||||||
|
segmentDescriptors[segment].freeOffset = size;
|
||||||
|
p.segment = segment;
|
||||||
|
p.offset = 0;
|
||||||
|
drivermemcpy(p, data, size);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Allocates size bytes of memory and copies data to the block.
|
||||||
|
Returns a pointer to driver memory.
|
||||||
|
This is write once every few frames use multiple times every frame
|
||||||
|
sort of memory, so reasonably fast to write.
|
||||||
|
This memory cannot be freed, it is freed automatically when
|
||||||
|
GL_FreeAll is called (Use the vertex cache for dynamically changing
|
||||||
|
memory)
|
||||||
|
*/
|
||||||
|
DriverPtr GL_DynamicAlloc(size_t size, void *data) {
|
||||||
|
//Allocate a new segment
|
||||||
|
int segment = GL_CreateSegment(size, data, GL_DYNAMIC_DRAW_ARB);
|
||||||
|
DriverPtr p;
|
||||||
|
segmentDescriptors[segment].freeOffset = size;
|
||||||
|
p.segment = segment;
|
||||||
|
p.offset = 0;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Frees all driver memory.
|
||||||
|
(This should be called at level changes for example)
|
||||||
|
*/
|
||||||
|
void GL_FreeAll(void) {
|
||||||
|
int i;
|
||||||
|
for (i=numSegments-1; i>=0; i--) {
|
||||||
|
//free this segment
|
||||||
|
GL_FreeSegment(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_InitDriverMem(void) {
|
||||||
|
//vbo with id 0 is a special case in opengl make a fake entry in our segment table
|
||||||
|
segmentDescriptors[0].freeOffset = 0;
|
||||||
|
segmentDescriptors[0].size = 0;
|
||||||
|
segmentDescriptors[0].nonVboData = 0;
|
||||||
|
segmentDescriptors[0].isSystemMem = true;
|
||||||
|
numSegments = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_FreeDriverMem(void) {
|
||||||
|
GL_FreeAll();
|
||||||
|
}
|
20
gl_drivermem.h
Normal file
20
gl_drivermem.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
extern DriverPtr nullDriver;
|
||||||
|
#define DRVNULL nullDriver
|
||||||
|
|
||||||
|
#define IsNullDriver(d) (((d).segment == nullDriver.segment) && ((d).offset == nullDriver.offset))
|
||||||
|
|
||||||
|
|
||||||
|
void *GL_MapToUserSpace(DriverPtr p);
|
||||||
|
void GL_UnmapFromUserSpace(DriverPtr p);
|
||||||
|
void drivermemcpy(DriverPtr dest, void *src, size_t size);
|
||||||
|
DriverPtr GL_StaticAlloc(size_t size, void *data);
|
||||||
|
DriverPtr GL_DynamicAlloc(size_t size, void *data);
|
||||||
|
void GL_FreeAll(void);
|
||||||
|
void GL_InitDriverMem(void);
|
||||||
|
void GL_FreeDriverMem(void);
|
||||||
|
|
||||||
|
void GL_InitVertexCache(void);
|
||||||
|
void GL_FreeVertexCache(void);
|
||||||
|
void GL_AllocVertexCache(const size_t size, DriverPtr *owner);
|
||||||
|
void GL_FlushVertexCache(void);
|
269
gl_vertexcache.c
Normal file
269
gl_vertexcache.c
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2003 Tenebrae Team
|
||||||
|
|
||||||
|
This program 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 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program 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 this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Vertex memory cache, this is used for dynamically changing stuff, like
|
||||||
|
skinned meshes or interpolated keyframes. (Wich are all done on the
|
||||||
|
cpu because of shadow volumes)
|
||||||
|
Oh, and this code is based on the Q1 surface cache... (A rover that
|
||||||
|
throws away old stuff untill it has enough space)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "quakedef.h"
|
||||||
|
|
||||||
|
typedef struct vertexcacheitem_s
|
||||||
|
{
|
||||||
|
struct vertexcacheitem_s *next;
|
||||||
|
DriverPtr *owner;// NULL is an empty chunk of memory
|
||||||
|
size_t size; // of data part
|
||||||
|
DriverPtr buffer; //Actual memory
|
||||||
|
} vertexcacheitem_t;
|
||||||
|
|
||||||
|
//A single vertex cache, a cache is never bigger than 65k verts so we may have
|
||||||
|
//multiple caches
|
||||||
|
typedef struct {
|
||||||
|
DriverPtr buffer; //Actual memory this cache is controlling
|
||||||
|
vertexcacheitem_t *items;
|
||||||
|
vertexcacheitem_t *rover;
|
||||||
|
size_t size; //Size in bytes of this cache
|
||||||
|
size_t free; //Free memory in this cache, it may be fragmented tough
|
||||||
|
} vertexcache_t;
|
||||||
|
|
||||||
|
#define VERTEXCACHE_DEBUG 1
|
||||||
|
#define GUARDSIZE 4
|
||||||
|
|
||||||
|
//These are used to detect if we are thrasing the cache
|
||||||
|
static qboolean gl_roverwrapped;
|
||||||
|
static size_t gl_initial_offset;
|
||||||
|
|
||||||
|
static vertexcacheitem_t *AllocCacheItem(void) {
|
||||||
|
return malloc(sizeof(vertexcacheitem_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FreeCacheItem(vertexcacheitem_t *i) {
|
||||||
|
free(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This may be dead slow, if it's uncached mem: debug only
|
||||||
|
*/
|
||||||
|
static void VC_CheckCacheGuard (vertexcache_t *c)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
byte *s = GL_MapToUserSpace(c->buffer);
|
||||||
|
|
||||||
|
s += c->size;
|
||||||
|
for (i=0 ; i<GUARDSIZE ; i++) {
|
||||||
|
if (s[i] != (byte)i)
|
||||||
|
Sys_Error ("GL_CheckCacheGuard failed: Vertex cache is corrupted");
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_UnmapFromUserSpace(c->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This may be dead slow, if it's uncached mem: debug only
|
||||||
|
*/
|
||||||
|
static void VC_ClearCacheGuard (vertexcache_t *c)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
byte *s = GL_MapToUserSpace(c->buffer);
|
||||||
|
|
||||||
|
s += c->size;
|
||||||
|
for (i=0 ; i<GUARDSIZE ; i++) {
|
||||||
|
s[i] = (byte)i;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_UnmapFromUserSpace(c->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void VC_InitVertexCache(vertexcache_t *c, size_t size)
|
||||||
|
{
|
||||||
|
vertexcacheitem_t *i;
|
||||||
|
|
||||||
|
Con_Printf ("%ik vertex cache\n", size/1024);
|
||||||
|
|
||||||
|
c->buffer = GL_DynamicAlloc(size + GUARDSIZE, NULL);
|
||||||
|
c->size = size;
|
||||||
|
c->free = size;
|
||||||
|
c->items = i = AllocCacheItem();
|
||||||
|
c->rover = i;
|
||||||
|
|
||||||
|
i->next = NULL;
|
||||||
|
i->owner = NULL;
|
||||||
|
i->size = size;
|
||||||
|
i->buffer = c->buffer;
|
||||||
|
|
||||||
|
VC_ClearCacheGuard(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void VC_FlushVertexCache(vertexcache_t *c)
|
||||||
|
{
|
||||||
|
vertexcacheitem_t *i, *n;
|
||||||
|
|
||||||
|
i=c->items;
|
||||||
|
while(i)
|
||||||
|
{
|
||||||
|
if (i->owner)
|
||||||
|
*i->owner = DRVNULL;
|
||||||
|
n=i->next;
|
||||||
|
FreeCacheItem(i);
|
||||||
|
i = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->items = i = AllocCacheItem();
|
||||||
|
i->next = NULL;
|
||||||
|
i->owner = NULL;
|
||||||
|
i->size = c->size;
|
||||||
|
i->buffer = c->buffer;
|
||||||
|
c->free = c->size;
|
||||||
|
c->rover = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void VC_FreeVertexCache(vertexcache_t *c)
|
||||||
|
{
|
||||||
|
VC_FlushVertexCache(c);
|
||||||
|
FreeCacheItem(c->items);
|
||||||
|
c->items = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static vertexcacheitem_t *VC_VertecCacheAlloc (vertexcache_t *c, size_t size)
|
||||||
|
{
|
||||||
|
vertexcacheitem_t *new;
|
||||||
|
qboolean wrapped_this_time;
|
||||||
|
|
||||||
|
if ((size <= 0) || (size > 0x400000)) //4 megabytes santity check
|
||||||
|
Sys_Error ("GL_VertecCacheAlloc: bad cache size %d\n", size);
|
||||||
|
|
||||||
|
size = (size + 3) & ~3;
|
||||||
|
if (size > c->size)
|
||||||
|
Sys_Error ("GL_VertecCacheAlloc: %i > cache size",size);
|
||||||
|
|
||||||
|
if (size > c->free)
|
||||||
|
Sys_Error ("GL_VertecCacheAlloc: %i > cache free",size);
|
||||||
|
|
||||||
|
// if there is not size bytes after the rover, reset to the start
|
||||||
|
wrapped_this_time = false;
|
||||||
|
|
||||||
|
if ( !c->rover || c->rover->buffer.offset > c->size - size)
|
||||||
|
{
|
||||||
|
if (c->rover)
|
||||||
|
{
|
||||||
|
wrapped_this_time = true;
|
||||||
|
}
|
||||||
|
c->rover = c->items;
|
||||||
|
}
|
||||||
|
|
||||||
|
// colect and free surfcache_t blocks until the rover block is large enough
|
||||||
|
new = c->rover;
|
||||||
|
if (c->rover->owner)
|
||||||
|
*c->rover->owner = DRVNULL;
|
||||||
|
|
||||||
|
while (new->size < size)
|
||||||
|
{
|
||||||
|
// free another
|
||||||
|
vertexcacheitem_t *old = c->rover;
|
||||||
|
c->rover = c->rover->next;
|
||||||
|
FreeCacheItem(old);
|
||||||
|
if (!c->rover)
|
||||||
|
Sys_Error ("GL_VertecCacheAlloc: hit the end of memory");
|
||||||
|
if (c->rover->owner)
|
||||||
|
*c->rover->owner = DRVNULL;
|
||||||
|
|
||||||
|
new->size += c->rover->size;
|
||||||
|
new->next = c->rover->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a fragment out of any leftovers
|
||||||
|
if (new->size - size > 256)
|
||||||
|
{
|
||||||
|
c->rover = AllocCacheItem();
|
||||||
|
c->rover->size = new->size - size;
|
||||||
|
c->rover->next = new->next;
|
||||||
|
c->rover->owner = NULL;
|
||||||
|
c->rover->buffer.segment = new->buffer.segment;
|
||||||
|
c->rover->buffer.offset = new->buffer.offset+size;
|
||||||
|
new->next = c->rover;
|
||||||
|
new->size = size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
c->rover = new->next;
|
||||||
|
|
||||||
|
new->owner = NULL; // should be set properly after return
|
||||||
|
|
||||||
|
if (gl_roverwrapped)
|
||||||
|
{
|
||||||
|
if (wrapped_this_time || (c->rover->buffer.offset >= gl_initial_offset))
|
||||||
|
r_cache_thrash = true;
|
||||||
|
}
|
||||||
|
else if (wrapped_this_time)
|
||||||
|
{
|
||||||
|
gl_roverwrapped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef VERTEXCACHE_DEBUG
|
||||||
|
//Only in debug as this can be very slow due to the driver mem being uncached
|
||||||
|
VC_CheckCacheGuard (c);
|
||||||
|
#endif
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void VC_VertecCacheDump (vertexcache_t *c)
|
||||||
|
{
|
||||||
|
vertexcacheitem_t *test;
|
||||||
|
|
||||||
|
for (test = c->items; test; test=test->next) {
|
||||||
|
if (test == c->rover)
|
||||||
|
Con_Printf("ROVER:\n");
|
||||||
|
Con_Printf("%p : segment(%i) offset(%i) bytes(%i) owner(%p)\n",test, test->buffer.segment, test->buffer.offset, test->size, test->owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************
|
||||||
|
|
||||||
|
Public interface
|
||||||
|
|
||||||
|
*************************/
|
||||||
|
|
||||||
|
#define VERTEX_CACHE_SIZE (1024*1024*8)
|
||||||
|
vertexcache_t cache;
|
||||||
|
|
||||||
|
void GL_InitVertexCache(void) {
|
||||||
|
VC_InitVertexCache(&cache, VERTEX_CACHE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_FreeVertexCache(void) {
|
||||||
|
VC_FreeVertexCache(&cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Owner will we overwritten with the pointer, if the cache later decides to free the allocated
|
||||||
|
cache spot, owner will be overwritten with NULL
|
||||||
|
So owner should point to "stable" memory, no temporaries on the stack please!
|
||||||
|
*/
|
||||||
|
void GL_AllocVertexCache(const size_t size, DriverPtr *owner) {
|
||||||
|
vertexcacheitem_t *r = VC_VertecCacheAlloc(&cache, size);
|
||||||
|
*owner = r->buffer;
|
||||||
|
r->owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_FlushVertexCache(void) {
|
||||||
|
VC_FlushVertexCache(&cache);
|
||||||
|
}
|
Loading…
Reference in a new issue