Skip to content

Commit

Permalink
Implement geometry cache
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
VReaperV committed Jan 29, 2025
1 parent b18a26e commit 60407b8
Show file tree
Hide file tree
Showing 13 changed files with 305 additions and 21 deletions.
2 changes: 2 additions & 0 deletions src.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ set(RENDERERLIST
${ENGINE_DIR}/renderer/tr_fbo.cpp
${ENGINE_DIR}/renderer/tr_flares.cpp
${ENGINE_DIR}/renderer/tr_font.cpp
${ENGINE_DIR}/renderer/GeometryCache.cpp
${ENGINE_DIR}/renderer/GeometryCache.h
${ENGINE_DIR}/renderer/InternalImage.cpp
${ENGINE_DIR}/renderer/InternalImage.h
${ENGINE_DIR}/renderer/Material.cpp
Expand Down
103 changes: 103 additions & 0 deletions src/engine/renderer/GeometryCache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
===========================================================================
Daemon BSD Source Code
Copyright (c) 2025 Daemon Developers
All rights reserved.
This file is part of the Daemon BSD Source Code (Daemon Source Code).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Daemon developers nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
===========================================================================
*/
// GeometryCache.cpp

#include "GeometryCache.h"

#include "tr_local.h"

GeometryCache geometryCache;

void GeometryCache::Bind() {
VAO.Bind();
}

void GeometryCache::InitGLBuffers() {
inputVBO.GenBuffer();
VBO.GenBuffer();
IBO.GenBuffer();

VAO.GenVAO();
}

void GeometryCache::FreeGLBuffers() {
inputVBO.DelBuffer();
VBO.DelBuffer();
IBO.DelBuffer();

VAO.DelVAO();
}

void GeometryCache::Free() {
}

void GeometryCache::AllocBuffers() {
VBO.BufferData( mapVerticesNumber * 8, nullptr, GL_STATIC_DRAW );

IBO.BufferData( mapIndicesNumber, nullptr, GL_STATIC_DRAW );
}

void GeometryCache::AddMapGeometry( const uint32_t verticesNumber, const uint32_t indicesNumber,
const vertexAttributeSpec_t* attrBegin, const vertexAttributeSpec_t* attrEnd,
const glIndex_t* indices ) {
mapVerticesNumber = verticesNumber;
mapIndicesNumber = indicesNumber;

VAO.Bind();

AllocBuffers();

VAO.SetAttrs( attrBegin, attrEnd );

VAO.SetVertexBuffer( VBO, 0 );
VAO.SetIndexBuffer( IBO );

VBO.BufferStorage( mapVerticesNumber * 8, 1, nullptr );
VBO.MapAll();
uint32_t* VBOVerts = VBO.GetData();
for ( const vertexAttributeSpec_t* spec = attrBegin; spec < attrEnd; spec++ ) {
vboAttributeLayout_t& attr = VAO.attrs[spec->attrIndex];

CopyVertexAttribute( attr, *spec, mapVerticesNumber, ( byte* ) VBOVerts );
}
VBO.UnmapBuffer();

IBO.BufferStorage( mapIndicesNumber, 1, nullptr );
IBO.MapAll();
uint32_t* IBOIndices = IBO.GetData();
memcpy( IBOIndices, indices, mapIndicesNumber * sizeof( uint32_t ) );
IBO.UnmapBuffer();

glBindVertexArray( backEnd.currentVAO );
}
70 changes: 70 additions & 0 deletions src/engine/renderer/GeometryCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
===========================================================================
Daemon BSD Source Code
Copyright (c) 2025 Daemon Developers
All rights reserved.
This file is part of the Daemon BSD Source Code (Daemon Source Code).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Daemon developers nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
===========================================================================
*/
// GeometryCache.h

#ifndef GEOMETRY_CACHE_H
#define GEOMETRY_CACHE_H

#include "gl_shader.h"
#include "Material.h"

class GeometryCache {
public:
void Bind();

void InitGLBuffers();
void FreeGLBuffers();

void Free();

void AllocBuffers();
void AddMapGeometry( const uint32_t verticesNumber, const uint32_t indicesNumber,
const vertexAttributeSpec_t* attrBegin,
const vertexAttributeSpec_t* attrEnd,
const glIndex_t* indices );

private:
uint32_t mapVerticesNumber;
uint32_t mapIndicesNumber;

GLVAO VAO = GLVAO( 0 );

GLBuffer inputVBO = GLBuffer( "geometryCacheInputVBO", Util::ordinal( BufferBind::GEOMETRY_CACHE_INPUT_VBO ), GL_MAP_WRITE_BIT, GL_MAP_INVALIDATE_RANGE_BIT );
GLBuffer VBO = GLBuffer( "geometryCacheVBO", Util::ordinal( BufferBind::GEOMETRY_CACHE_VBO ), GL_MAP_WRITE_BIT, GL_MAP_FLUSH_EXPLICIT_BIT );
GLBuffer IBO = GLBuffer( "geometryCacheIBO", Util::ordinal( BufferBind::UNUSED ), GL_MAP_WRITE_BIT, GL_MAP_INVALIDATE_RANGE_BIT );
};

extern GeometryCache geometryCache;

#endif // GEOMETRY_CACHE_H
17 changes: 5 additions & 12 deletions src/engine/renderer/Material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "tr_local.h"
#include "Material.h"
#include "ShadeCommon.h"
#include "GeometryCache.h"

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

material.vbo = glState.currentVBO;
material.ibo = glState.currentIBO;

material.bspSurface = drawSurf->bspSurface;
pStage->materialProcessor( &material, pStage, drawSurf );
pStage->paddedSize = material.shader->GetPaddedSize();
Expand Down Expand Up @@ -1457,11 +1455,6 @@ void MaterialSystem::GenerateWorldMaterials() {
continue;
}

// The verts aren't used; it's only to get the VBO/IBO.
Tess_Begin( Tess_StageIteratorDummy, shader, nullptr, true, -1, 0 );
rb_surfaceTable[Util::ordinal( *( drawSurf->surface ) )]( drawSurf->surface );
Tess_Clear();

// Only add the main surface for surfaces with depth pre-pass or fog to the total count
if ( !drawSurf->materialSystemSkip ) {
totalDrawSurfs++;
Expand Down Expand Up @@ -2070,6 +2063,8 @@ void MaterialSystem::RenderMaterials( const shaderSort_t fromSort, const shaderS

materialsUBO.BindBufferBase();

geometryCache.Bind();

for ( MaterialPack& materialPack : materialPacks ) {
if ( materialPack.fromSort >= fromSort && materialPack.toSort <= toSort ) {
for ( Material& material : materialPack.materials ) {
Expand All @@ -2079,6 +2074,8 @@ void MaterialSystem::RenderMaterials( const shaderSort_t fromSort, const shaderS
}
}

glBindVertexArray( backEnd.currentVAO );

// Draw the skybox here because we skipped R_AddWorldSurfaces()
const bool environmentFogDraw = ( fromSort <= shaderSort_t::SS_ENVIRONMENT_FOG ) && ( toSort >= shaderSort_t::SS_ENVIRONMENT_FOG );
const bool environmentNoFogDraw = ( fromSort <= shaderSort_t::SS_ENVIRONMENT_NOFOG ) && toSort >= ( shaderSort_t::SS_ENVIRONMENT_NOFOG );
Expand Down Expand Up @@ -2162,10 +2159,6 @@ void MaterialSystem::RenderMaterial( Material& material, const uint32_t viewID )

material.shaderBinder( &material );

R_BindVBO( material.vbo );
R_BindIBO( material.ibo );
material.shader->SetRequiredVertexPointers();

if ( !material.texturesResident ) {
for ( Texture* texture : material.textures ) {
if ( !texture->IsResident() ) {
Expand Down
10 changes: 5 additions & 5 deletions src/engine/renderer/Material.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,6 @@ struct Material {

bool usePolygonOffset = false;

VBO_t* vbo;
IBO_t* ibo;

fog_t* fog = nullptr;

std::vector<drawSurf_t*> drawSurfs;
Expand All @@ -129,7 +126,7 @@ struct Material {
std::vector<Texture*> textures;

bool operator==( const Material& other ) {
return program == other.program && stateBits == other.stateBits && vbo == other.vbo && ibo == other.ibo
return program == other.program && stateBits == other.stateBits
&& fog == other.fog && cullType == other.cullType && usePolygonOffset == other.usePolygonOffset;
}

Expand Down Expand Up @@ -292,7 +289,10 @@ enum class BufferBind {
COMMAND_COUNTERS_ATOMIC = 0,
COMMAND_COUNTERS_STORAGE = 4, // Avoid needlessly rebinding buffers
PORTAL_SURFACES = 5,
DEBUG = 10
GEOMETRY_CACHE_INPUT_VBO = 6,
GEOMETRY_CACHE_VBO = 7,
DEBUG = 10,
UNUSED = INT32_MAX
};

class MaterialSystem {
Expand Down
74 changes: 74 additions & 0 deletions src/engine/renderer/gl_shader.h
Original file line number Diff line number Diff line change
Expand Up @@ -1370,6 +1370,8 @@ class GLUniformBlock

class GLBuffer {
public:
friend class GLVAO;

std::string name;
const GLuint64 SYNC_TIMEOUT = 10000000000; // 10 seconds

Expand Down Expand Up @@ -1530,6 +1532,78 @@ class GLAtomicCounterBuffer : public GLBuffer {
}
};

class GLVAO {
public:
vboAttributeLayout_t attrs[ATTR_INDEX_MAX];
uint32_t enabledAttrs;

GLVAO( const GLuint newVBOBindingPoint ) :
VBOBindingPoint( newVBOBindingPoint ) {
}

~GLVAO() = default;

void Bind() {
glBindVertexArray( id );
}

void SetAttrs( const vertexAttributeSpec_t* attrBegin, const vertexAttributeSpec_t* attrEnd ) {
uint32_t ofs = 0;
for ( const vertexAttributeSpec_t* spec = attrBegin; spec < attrEnd; spec++ ) {
vboAttributeLayout_t& attr = attrs[spec->attrIndex];
ASSERT_NQ( spec->numComponents, 0U );
// vbo->attribBits |= 1 << spec->attrIndex;
attr.componentType = spec->componentStorageType;
if ( attr.componentType == GL_HALF_FLOAT && !glConfig2.halfFloatVertexAvailable ) {
attr.componentType = GL_FLOAT;
}
attr.numComponents = spec->numComponents;
attr.normalize = spec->attrOptions & ATTR_OPTION_NORMALIZE ? GL_TRUE : GL_FALSE;

attr.ofs = ofs;
ofs += attr.numComponents * ComponentSize( attr.componentType );
ofs = ( ofs + 3 ) & ~3; // 4 is minimum alignment for any vertex attribute

enabledAttrs |= 1 << spec->attrIndex;
}

stride = ofs;

for ( const vertexAttributeSpec_t* spec = attrBegin; spec < attrEnd; spec++ ) {
const int index = spec->attrIndex;
vboAttributeLayout_t& attr = attrs[index];

attr.stride = stride;

glEnableVertexArrayAttrib( id, index );
glVertexArrayAttribFormat( id, index, attr.numComponents, attr.componentType,
attr.normalize, attr.ofs );
glVertexArrayAttribBinding( id, index, VBOBindingPoint );
}
}

void SetVertexBuffer( const GLBuffer buffer, const GLuint offset ) {
glVertexArrayVertexBuffer( id, VBOBindingPoint, buffer.id, offset, stride );
}

void SetIndexBuffer( const GLBuffer buffer ) {
glVertexArrayElementBuffer( id, buffer.id );
}

void GenVAO() {
glGenVertexArrays( 1, &id );
}

void DelVAO() {
glDeleteVertexArrays( 1, &id );
}

private:
GLuint id;
const GLuint VBOBindingPoint;
GLuint stride;
};

class GLCompileMacro
{
private:
Expand Down
5 changes: 5 additions & 0 deletions src/engine/renderer/tr_bsp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// tr_bsp.c
#include "tr_local.h"
#include "framework/CommandSystem.h"
#include "GeometryCache.h"

/*
========================================================
Expand Down Expand Up @@ -3208,6 +3209,10 @@ static void R_CreateWorldVBO()
{ ATTR_INDEX_TEXCOORD, GL_FLOAT, GL_HALF_FLOAT, &vboVerts[ 0 ].st, 4, sizeof( *vboVerts ), 0 },
};

if ( glConfig2.usingGeometryCache ) {
geometryCache.AddMapGeometry( vboNumVerts, vboNumIndexes, std::begin( attrs ), std::end( attrs ), vboIdxs );
}

s_worldData.vbo = R_CreateStaticVBO(
"staticWorld_VBO", std::begin( attrs ), std::end( attrs ), vboNumVerts );
s_worldData.ibo = R_CreateStaticIBO2( "staticWorld_IBO", numTriangles, vboIdxs );
Expand Down
Loading

0 comments on commit 60407b8

Please sign in to comment.