Skip to content

Commit 299a185

Browse files
committed
Implement geometry cache
The geometry cache represents 3 buffers: a VBO, IBO, and inputVBO. These buffers will replace the old separate input VBOs and IBOs when enabled. This allows binding only a VAO before rendering, rather than setting up vertex pointers with the correct offset and formats every time. Additionally, it means that there's no need to switch between different VBOs/IBOs. The `inputVBO` is currently unused, but it will be needed to make models work with the geometry cache (model animations and world transform will be processed in a compute shader). Currently this is only used by the material system, but it should be possible to make the core renderer use it as well.
1 parent 9803886 commit 299a185

File tree

13 files changed

+302
-21
lines changed

13 files changed

+302
-21
lines changed

src.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ set(RENDERERLIST
115115
${ENGINE_DIR}/renderer/tr_fbo.cpp
116116
${ENGINE_DIR}/renderer/tr_flares.cpp
117117
${ENGINE_DIR}/renderer/tr_font.cpp
118+
${ENGINE_DIR}/renderer/GeometryCache.cpp
119+
${ENGINE_DIR}/renderer/GeometryCache.h
118120
${ENGINE_DIR}/renderer/InternalImage.cpp
119121
${ENGINE_DIR}/renderer/InternalImage.h
120122
${ENGINE_DIR}/renderer/Material.cpp

src/engine/renderer/GeometryCache.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
===========================================================================
3+
4+
Daemon BSD Source Code
5+
Copyright (c) 2025 Daemon Developers
6+
All rights reserved.
7+
8+
This file is part of the Daemon BSD Source Code (Daemon Source Code).
9+
10+
Redistribution and use in source and binary forms, with or without
11+
modification, are permitted provided that the following conditions are met:
12+
* Redistributions of source code must retain the above copyright
13+
notice, this list of conditions and the following disclaimer.
14+
* Redistributions in binary form must reproduce the above copyright
15+
notice, this list of conditions and the following disclaimer in the
16+
documentation and/or other materials provided with the distribution.
17+
* Neither the name of the Daemon developers nor the
18+
names of its contributors may be used to endorse or promote products
19+
derived from this software without specific prior written permission.
20+
21+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24+
DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY
25+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
32+
===========================================================================
33+
*/
34+
// GeometryCache.cpp
35+
36+
#include "GeometryCache.h"
37+
38+
#include "tr_local.h"
39+
40+
GeometryCache geometryCache;
41+
42+
void GeometryCache::Bind() {
43+
VAO.Bind();
44+
}
45+
46+
void GeometryCache::InitGLBuffers() {
47+
inputVBO.GenBuffer();
48+
VBO.GenBuffer();
49+
IBO.GenBuffer();
50+
51+
VAO.GenVAO();
52+
}
53+
54+
void GeometryCache::FreeGLBuffers() {
55+
inputVBO.DelBuffer();
56+
VBO.DelBuffer();
57+
IBO.DelBuffer();
58+
59+
VAO.DelVAO();
60+
}
61+
62+
void GeometryCache::Free() {
63+
}
64+
65+
void GeometryCache::AllocBuffers() {
66+
VBO.BufferData( mapVerticesNumber * 8, nullptr, GL_STATIC_DRAW );
67+
68+
IBO.BufferData( mapIndicesNumber, nullptr, GL_STATIC_DRAW );
69+
}
70+
71+
void GeometryCache::AddMapGeometry( const uint32_t verticesNumber, const uint32_t indicesNumber,
72+
const vertexAttributeSpec_t* attrBegin, const vertexAttributeSpec_t* attrEnd,
73+
const glIndex_t* indices ) {
74+
mapVerticesNumber = verticesNumber;
75+
mapIndicesNumber = indicesNumber;
76+
77+
VAO.Bind();
78+
79+
AllocBuffers();
80+
81+
VAO.SetAttrs( attrBegin, attrEnd );
82+
83+
VAO.SetVertexBuffer( VBO, 0 );
84+
VAO.SetIndexBuffer( IBO );
85+
86+
uint32_t* VBOVerts = VBO.MapBufferRange( mapVerticesNumber * 8 );
87+
memset( VBOVerts, 0, mapVerticesNumber * 8 * sizeof( uint32_t ) );
88+
for ( const vertexAttributeSpec_t* spec = attrBegin; spec < attrEnd; spec++ ) {
89+
vboAttributeLayout_t& attr = VAO.attrs[spec->attrIndex];
90+
91+
CopyVertexAttribute( attr, *spec, mapVerticesNumber, ( byte* ) VBOVerts );
92+
}
93+
VBO.UnmapBuffer();
94+
95+
uint32_t* VBOIndices = IBO.MapBufferRange( mapIndicesNumber );
96+
memcpy( VBOIndices, indices, mapIndicesNumber * sizeof( uint32_t ) );
97+
IBO.UnmapBuffer();
98+
99+
glBindVertexArray( backEnd.currentVAO );
100+
}

src/engine/renderer/GeometryCache.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
===========================================================================
3+
4+
Daemon BSD Source Code
5+
Copyright (c) 2025 Daemon Developers
6+
All rights reserved.
7+
8+
This file is part of the Daemon BSD Source Code (Daemon Source Code).
9+
10+
Redistribution and use in source and binary forms, with or without
11+
modification, are permitted provided that the following conditions are met:
12+
* Redistributions of source code must retain the above copyright
13+
notice, this list of conditions and the following disclaimer.
14+
* Redistributions in binary form must reproduce the above copyright
15+
notice, this list of conditions and the following disclaimer in the
16+
documentation and/or other materials provided with the distribution.
17+
* Neither the name of the Daemon developers nor the
18+
names of its contributors may be used to endorse or promote products
19+
derived from this software without specific prior written permission.
20+
21+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24+
DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY
25+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
32+
===========================================================================
33+
*/
34+
// GeometryCache.h
35+
36+
#ifndef GEOMETRY_CACHE_H
37+
#define GEOMETRY_CACHE_H
38+
39+
#include "gl_shader.h"
40+
#include "Material.h"
41+
42+
class GeometryCache {
43+
public:
44+
void Bind();
45+
46+
void InitGLBuffers();
47+
void FreeGLBuffers();
48+
49+
void Free();
50+
51+
void AllocBuffers();
52+
void AddMapGeometry( const uint32_t verticesNumber, const uint32_t indicesNumber,
53+
const vertexAttributeSpec_t* attrBegin,
54+
const vertexAttributeSpec_t* attrEnd,
55+
const glIndex_t* indices );
56+
57+
private:
58+
uint32_t mapVerticesNumber;
59+
uint32_t mapIndicesNumber;
60+
61+
GLVAO VAO = GLVAO( 0 );
62+
63+
GLBuffer inputVBO = GLBuffer( "geometryCacheInputVBO", Util::ordinal( BufferBind::GEOMETRY_CACHE_INPUT_VBO ), GL_MAP_WRITE_BIT, GL_MAP_INVALIDATE_RANGE_BIT );
64+
GLBuffer VBO = GLBuffer( "geometryCacheVBO", Util::ordinal( BufferBind::GEOMETRY_CACHE_VBO ), GL_MAP_WRITE_BIT, GL_MAP_FLUSH_EXPLICIT_BIT );
65+
GLBuffer IBO = GLBuffer( "geometryCacheIBO", Util::ordinal( BufferBind::UNUSED ), GL_MAP_WRITE_BIT, GL_MAP_INVALIDATE_RANGE_BIT );
66+
};
67+
68+
extern GeometryCache geometryCache;
69+
70+
#endif // GEOMETRY_CACHE_H

src/engine/renderer/Material.cpp

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3636
#include "tr_local.h"
3737
#include "Material.h"
3838
#include "ShadeCommon.h"
39+
#include "GeometryCache.h"
3940

4041
GLUBO materialsUBO( "materials", Util::ordinal( BufferBind::MATERIALS ), GL_MAP_WRITE_BIT, GL_MAP_INVALIDATE_RANGE_BIT );
4142
GLBuffer texDataBuffer( "texData", Util::ordinal( BufferBind::TEX_DATA ), GL_MAP_WRITE_BIT, GL_MAP_FLUSH_EXPLICIT_BIT );
@@ -1353,9 +1354,6 @@ void MaterialSystem::ProcessStage( drawSurf_t* drawSurf, shaderStage_t* pStage,
13531354
material.cullType = shader->cullType;
13541355
material.usePolygonOffset = shader->polygonOffset;
13551356

1356-
material.vbo = glState.currentVBO;
1357-
material.ibo = glState.currentIBO;
1358-
13591357
material.bspSurface = drawSurf->bspSurface;
13601358
pStage->materialProcessor( &material, pStage, drawSurf );
13611359
pStage->paddedSize = material.shader->GetPaddedSize();
@@ -1454,11 +1452,6 @@ void MaterialSystem::GenerateWorldMaterials() {
14541452
continue;
14551453
}
14561454

1457-
// The verts aren't used; it's only to get the VBO/IBO.
1458-
Tess_Begin( Tess_StageIteratorDummy, shader, nullptr, true, -1, 0 );
1459-
rb_surfaceTable[Util::ordinal( *( drawSurf->surface ) )]( drawSurf->surface );
1460-
Tess_Clear();
1461-
14621455
// Only add the main surface for surfaces with depth pre-pass or fog to the total count
14631456
if ( !drawSurf->materialSystemSkip ) {
14641457
totalDrawSurfs++;
@@ -2058,6 +2051,8 @@ void MaterialSystem::RenderMaterials( const shaderSort_t fromSort, const shaderS
20582051

20592052
materialsUBO.BindBufferBase();
20602053

2054+
geometryCache.Bind();
2055+
20612056
for ( MaterialPack& materialPack : materialPacks ) {
20622057
if ( materialPack.fromSort >= fromSort && materialPack.toSort <= toSort ) {
20632058
for ( Material& material : materialPack.materials ) {
@@ -2067,6 +2062,8 @@ void MaterialSystem::RenderMaterials( const shaderSort_t fromSort, const shaderS
20672062
}
20682063
}
20692064

2065+
glBindVertexArray( backEnd.currentVAO );
2066+
20702067
// Draw the skybox here because we skipped R_AddWorldSurfaces()
20712068
const bool environmentFogDraw = ( fromSort <= shaderSort_t::SS_ENVIRONMENT_FOG ) && ( toSort >= shaderSort_t::SS_ENVIRONMENT_FOG );
20722069
const bool environmentNoFogDraw = ( fromSort <= shaderSort_t::SS_ENVIRONMENT_NOFOG ) && toSort >= ( shaderSort_t::SS_ENVIRONMENT_NOFOG );
@@ -2150,10 +2147,6 @@ void MaterialSystem::RenderMaterial( Material& material, const uint32_t viewID )
21502147

21512148
material.shaderBinder( &material );
21522149

2153-
R_BindVBO( material.vbo );
2154-
R_BindIBO( material.ibo );
2155-
material.shader->SetRequiredVertexPointers();
2156-
21572150
if ( !material.texturesResident ) {
21582151
for ( Texture* texture : material.textures ) {
21592152
if ( !texture->IsResident() ) {

src/engine/renderer/Material.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,6 @@ struct Material {
114114

115115
bool usePolygonOffset = false;
116116

117-
VBO_t* vbo;
118-
IBO_t* ibo;
119-
120117
fog_t* fog = nullptr;
121118

122119
std::vector<drawSurf_t*> drawSurfs;
@@ -125,7 +122,7 @@ struct Material {
125122
std::vector<Texture*> textures;
126123

127124
bool operator==( const Material& other ) {
128-
return program == other.program && stateBits == other.stateBits && vbo == other.vbo && ibo == other.ibo
125+
return program == other.program && stateBits == other.stateBits
129126
&& fog == other.fog && cullType == other.cullType && usePolygonOffset == other.usePolygonOffset;
130127
}
131128

@@ -288,7 +285,10 @@ enum class BufferBind {
288285
COMMAND_COUNTERS_ATOMIC = 0,
289286
COMMAND_COUNTERS_STORAGE = 4, // Avoid needlessly rebinding buffers
290287
PORTAL_SURFACES = 5,
291-
DEBUG = 10
288+
GEOMETRY_CACHE_INPUT_VBO = 6,
289+
GEOMETRY_CACHE_VBO = 7,
290+
DEBUG = 10,
291+
UNUSED = INT32_MAX
292292
};
293293

294294
class MaterialSystem {

src/engine/renderer/gl_shader.h

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,8 @@ class GLUniformBlock
13701370

13711371
class GLBuffer {
13721372
public:
1373+
friend class GLVAO;
1374+
13731375
std::string name;
13741376
const GLuint64 SYNC_TIMEOUT = 10000000000; // 10 seconds
13751377

@@ -1530,6 +1532,78 @@ class GLAtomicCounterBuffer : public GLBuffer {
15301532
}
15311533
};
15321534

1535+
class GLVAO {
1536+
public:
1537+
vboAttributeLayout_t attrs[ATTR_INDEX_MAX];
1538+
uint32_t enabledAttrs;
1539+
1540+
GLVAO( const GLuint newVBOBindingPoint ) :
1541+
VBOBindingPoint( newVBOBindingPoint ) {
1542+
}
1543+
1544+
~GLVAO() = default;
1545+
1546+
void Bind() {
1547+
glBindVertexArray( id );
1548+
}
1549+
1550+
void SetAttrs( const vertexAttributeSpec_t* attrBegin, const vertexAttributeSpec_t* attrEnd ) {
1551+
uint32_t ofs = 0;
1552+
for ( const vertexAttributeSpec_t* spec = attrBegin; spec < attrEnd; spec++ ) {
1553+
vboAttributeLayout_t& attr = attrs[spec->attrIndex];
1554+
ASSERT_NQ( spec->numComponents, 0U );
1555+
// vbo->attribBits |= 1 << spec->attrIndex;
1556+
attr.componentType = spec->componentStorageType;
1557+
if ( attr.componentType == GL_HALF_FLOAT && !glConfig2.halfFloatVertexAvailable ) {
1558+
attr.componentType = GL_FLOAT;
1559+
}
1560+
attr.numComponents = spec->numComponents;
1561+
attr.normalize = spec->attrOptions & ATTR_OPTION_NORMALIZE ? GL_TRUE : GL_FALSE;
1562+
1563+
attr.ofs = ofs;
1564+
ofs += attr.numComponents * ComponentSize( attr.componentType );
1565+
ofs = ( ofs + 3 ) & ~3; // 4 is minimum alignment for any vertex attribute
1566+
1567+
enabledAttrs |= 1 << spec->attrIndex;
1568+
}
1569+
1570+
stride = ofs;
1571+
1572+
for ( const vertexAttributeSpec_t* spec = attrBegin; spec < attrEnd; spec++ ) {
1573+
const int index = spec->attrIndex;
1574+
vboAttributeLayout_t& attr = attrs[index];
1575+
1576+
attr.stride = stride;
1577+
1578+
glEnableVertexArrayAttrib( id, index );
1579+
glVertexArrayAttribFormat( id, index, attr.numComponents, attr.componentType,
1580+
attr.normalize, attr.ofs );
1581+
glVertexArrayAttribBinding( id, index, VBOBindingPoint );
1582+
}
1583+
}
1584+
1585+
void SetVertexBuffer( const GLBuffer buffer, const GLuint offset ) {
1586+
glVertexArrayVertexBuffer( id, VBOBindingPoint, buffer.id, offset, stride );
1587+
}
1588+
1589+
void SetIndexBuffer( const GLBuffer buffer ) {
1590+
glVertexArrayElementBuffer( id, buffer.id );
1591+
}
1592+
1593+
void GenVAO() {
1594+
glGenVertexArrays( 1, &id );
1595+
}
1596+
1597+
void DelVAO() {
1598+
glDeleteVertexArrays( 1, &id );
1599+
}
1600+
1601+
private:
1602+
GLuint id;
1603+
const GLuint VBOBindingPoint;
1604+
GLuint stride;
1605+
};
1606+
15331607
class GLCompileMacro
15341608
{
15351609
private:

src/engine/renderer/tr_bsp.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2424
// tr_bsp.c
2525
#include "tr_local.h"
2626
#include "framework/CommandSystem.h"
27+
#include "GeometryCache.h"
2728

2829
/*
2930
========================================================
@@ -3208,6 +3209,10 @@ static void R_CreateWorldVBO()
32083209
{ ATTR_INDEX_TEXCOORD, GL_FLOAT, GL_HALF_FLOAT, &vboVerts[ 0 ].st, 4, sizeof( *vboVerts ), 0 },
32093210
};
32103211

3212+
if ( glConfig2.usingGeometryCache ) {
3213+
geometryCache.AddMapGeometry( vboNumVerts, vboNumIndexes, std::begin( attrs ), std::end( attrs ), vboIdxs );
3214+
}
3215+
32113216
s_worldData.vbo = R_CreateStaticVBO(
32123217
"staticWorld_VBO", std::begin( attrs ), std::end( attrs ), vboNumVerts );
32133218
s_worldData.ibo = R_CreateStaticIBO2( "staticWorld_IBO", numTriangles, vboIdxs );

0 commit comments

Comments
 (0)