Skip to content

Commit

Permalink
- use a uniform / shader storage buffer to handle the texture matrices.
Browse files Browse the repository at this point in the history
Since these are relatively constant data, holding them in a buffer and only updating when needed will save some processing time.
It also removes 16 uniform values from the main attribute set which needs to be brought down to 32 for the Vulkan renderer.
With the texture matrix removed there are currently 55 uniforms in this set, i.e. 23 more still need to be refactored into buffers.
  • Loading branch information
coelckers committed Aug 18, 2018
1 parent 481e268 commit 17813b4
Show file tree
Hide file tree
Showing 23 changed files with 339 additions and 91 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Expand Up @@ -1047,6 +1047,7 @@ set (PCH_SOURCES
gl/data/gl_uniformbuffer.cpp
gl/data/gl_viewpointbuffer.cpp
gl/data/gl_modelbuffer.cpp
gl/data/gl_texturematrixbuffer.cpp
gl/dynlights/gl_lightbuffer.cpp
gl/dynlights/gl_shadowmap.cpp
gl/models/gl_models.cpp
Expand Down
4 changes: 3 additions & 1 deletion src/gl/data/gl_modelbuffer.cpp
Expand Up @@ -57,6 +57,8 @@ GLModelBuffer::GLModelBuffer()

GLModelBuffer::~GLModelBuffer()
{
glBindBuffer(GL_UNIFORM_BUFFER, mBufferId);
glUnmapBuffer(GL_UNIFORM_BUFFER);
glBindBuffer(mBufferType, 0);
glDeleteBuffers(1, &mBufferId);
}
Expand Down Expand Up @@ -132,7 +134,7 @@ void GLModelBuffer::Map()

void GLModelBuffer::Unmap()
{
if (!mPersistent)
if (!mPersistent && mBufferPointer)
{
glBindBuffer(mBufferType, mBufferId);
glUnmapBuffer(mBufferType);
Expand Down
192 changes: 192 additions & 0 deletions src/gl/data/gl_texturematrixbuffer.cpp
@@ -0,0 +1,192 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2018 Christoph Oelckers
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
/*
** gl_viewpointbuffer.cpp
** Buffer data maintenance for per viewpoint uniform data
**
**/

#include "gl_load/gl_system.h"
#include "gl_load/gl_interface.h"
#include "hwrenderer/data/shaderuniforms.h"
#include "gl_texturematrixbuffer.h"
#include "templates.h"
#include "g_levellocals.h"
#include "r_data/matrix.h"

static const int INITIAL_SECTOR_COUNT = 1024;
static const int ADDITIONAL_ENTRIES = 50; // for skyboxes

GLTextureMatrixBuffer::GLTextureMatrixBuffer()
{
mSectorCount = INITIAL_SECTOR_COUNT;

if (gl.flags & RFL_SHADER_STORAGE_BUFFER && !strstr(gl.vendorstring, "Intel"))
{
mBufferType = GL_SHADER_STORAGE_BUFFER;
mBlockAlign = 0;
mBlockSize = 0;
}
else
{
mBufferType = GL_UNIFORM_BUFFER;
// A matrix is always 64 bytes.
if (gl.maxuniformblock % sizeof(VSMatrix) == 0)
{
mBlockAlign = gl.maxuniformblock;
}
else
{
mBlockAlign = gl.uniformblockalignment; // this should really never happen.
}
mBlockSize = mBlockAlign / sizeof(VSMatrix);
}

mBufferId = 0;
mUploadIndex = 2;
Allocate();
mLastMappedIndex = UINT_MAX;

// Initialize the identity rotation.
Map();
}

GLTextureMatrixBuffer::~GLTextureMatrixBuffer()
{
Unload();
}

void GLTextureMatrixBuffer::Unload()
{
if (mBufferId != 0)
{
glBindBuffer(mBufferType, mBufferId);
glUnmapBuffer(mBufferType);
glBindBuffer(mBufferType, 0);
glDeleteBuffers(1, &mBufferId);
}
mBufferId = 0;
}

void GLTextureMatrixBuffer::Allocate()
{
Unload();

mBufferSize = mSectorCount * 2 + ADDITIONAL_ENTRIES;
mByteSize = mBufferSize * sizeof(VSMatrix);
glGenBuffers(1, &mBufferId);
glBindBufferBase(mBufferType, TEXMATRIX_BINDINGPOINT, mBufferId);
glBindBuffer(mBufferType, mBufferId);
if (gl.buffermethod == BM_PERSISTENT)
{
glBufferStorage(mBufferType, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
mBufferPointer = (VSMatrix*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
mPersistent = true;
}
else
{
glBufferData(mBufferType, mByteSize, NULL, GL_DYNAMIC_DRAW);
mBufferPointer = nullptr;
mPersistent = false;
}
Map();
// Index #0 is the identity matrix.
// Index #1 is a y-flip matrix for camera textures.
VSMatrix mat;
mat.loadIdentity();
mBufferPointer[0] = mat;
mat.scale(1.f, -1.f, 1.f);
mat.translate(0.f, 1.f, 0.0f);
mBufferPointer[1] = mat;
Unmap();
}

void GLTextureMatrixBuffer::ValidateSize(int numsectors)
{
if (mSectorCount < numsectors)
{
mSectorCount = MAX<unsigned>(mSectorCount*2, numsectors);
Allocate();
}
// Mark all indices as uninitialized.
int tmindex = -ADDITIONAL_ENTRIES;
for (auto &sec : level.sectors)
{
sec.planes[sector_t::floor].ubIndexMatrix = tmindex--;
sec.planes[sector_t::ceiling].ubIndexMatrix = tmindex--;
}
}

void GLTextureMatrixBuffer::Map()
{
if (!mPersistent)
{
glBindBuffer(mBufferType, mBufferId);
mBufferPointer = (VSMatrix*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT);
}
}

void GLTextureMatrixBuffer::Unmap()
{
if (!mPersistent && mBufferPointer)
{
glBindBuffer(mBufferType, mBufferId);
glUnmapBuffer(mBufferType);
mBufferPointer = nullptr;
}
}

int GLTextureMatrixBuffer::Upload(const VSMatrix &mat, int index)
{
assert(mBufferPointer != nullptr); // May only be called when the buffer is mapped.
if (mBufferPointer == nullptr) return 0;

if (index == -1) // dynamic case for sky domes.
{
// The dynamic case for sky domes uses a simple circular buffer. 48 sky domes in a single map are highly unlikely and due to the serial nature of portal rendering
// even then has little chance on stomping on data still in flight.
// This can only happen in non-multithreaded parts right now so no synchronization is needed here.
index = mUploadIndex++;
if (index >= 50)
{
index = 2;
}
}

mBufferPointer[index] = mat;
return index;
}

// When this gets called we already have ruled out the case where rebinding is not needed.
int GLTextureMatrixBuffer::DoBind(unsigned int index)
{
unsigned int offset = (index*sizeof(VSMatrix) / mBlockAlign) * mBlockAlign;

if (offset != mLastMappedIndex)
{
mLastMappedIndex = offset;
glBindBufferRange(GL_UNIFORM_BUFFER, TEXMATRIX_BINDINGPOINT, mBufferId, offset, MIN(mBlockAlign, mByteSize - offset));
}
return index - (offset/sizeof(VSMatrix));
}


48 changes: 48 additions & 0 deletions src/gl/data/gl_texturematrixbuffer.h
@@ -0,0 +1,48 @@
#pragma once

#include <atomic>
#include <mutex>
#include "hwrenderer/data/shaderuniforms.h"
#include "r_data/matrix.h"

class VSMatrix;

class GLTextureMatrixBuffer
{
bool mPersistent;
unsigned int mBufferId;
unsigned int mBufferType;
unsigned int mBufferSize;
unsigned int mBlockAlign;
unsigned int mUploadIndex;
unsigned int mLastMappedIndex;
VSMatrix * mBufferPointer;

unsigned int mBlockSize;
unsigned int mByteSize;
unsigned int mSectorCount;

void Allocate();
int DoBind(unsigned index);
void Unload();

public:

GLTextureMatrixBuffer();
~GLTextureMatrixBuffer();
void ValidateSize(int numsectors);
int Upload(const VSMatrix &mat, int index);
void Map();
void Unmap();
int Bind(unsigned int index)
{
if (mBlockAlign > 0) return DoBind(index);
return index;
}
unsigned int GetBlockSize() const
{
return mBlockSize;
}

};

4 changes: 3 additions & 1 deletion src/gl/data/gl_viewpointbuffer.cpp
Expand Up @@ -45,6 +45,8 @@ GLViewpointBuffer::GLViewpointBuffer()

GLViewpointBuffer::~GLViewpointBuffer()
{
glBindBuffer(GL_UNIFORM_BUFFER, mBufferId);
glUnmapBuffer(GL_UNIFORM_BUFFER);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glDeleteBuffers(1, &mBufferId);
}
Expand Down Expand Up @@ -103,7 +105,7 @@ void GLViewpointBuffer::Map()

void GLViewpointBuffer::Unmap()
{
if (!mPersistent)
if (!mPersistent && mBufferPointer)
{
glBindBuffer(GL_UNIFORM_BUFFER, mBufferId);
glUnmapBuffer(GL_UNIFORM_BUFFER);
Expand Down
4 changes: 0 additions & 4 deletions src/gl/models/gl_models.cpp
Expand Up @@ -70,8 +70,6 @@ void FGLModelRenderer::BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, con

void FGLModelRenderer::EndDrawModel(AActor *actor, FSpriteModelFrame *smf)
{
gl_RenderState.EnableModelMatrix(false);

glDepthFunc(GL_LESS);
if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal]) && !(smf->flags & MDL_DONTCULLBACKFACES))
glDisable(GL_CULL_FACE);
Expand All @@ -94,8 +92,6 @@ void FGLModelRenderer::BeginDrawHUDModel(AActor *actor, const VSMatrix *objectTo

void FGLModelRenderer::EndDrawHUDModel(AActor *actor)
{
gl_RenderState.EnableModelMatrix(false);

glDepthFunc(GL_LESS);
if (!(actor->RenderStyle == LegacyRenderStyles[STYLE_Normal]))
glDisable(GL_CULL_FACE);
Expand Down
11 changes: 6 additions & 5 deletions src/gl/renderer/gl_renderer.cpp
Expand Up @@ -58,6 +58,7 @@
#include "gl/dynlights/gl_lightbuffer.h"
#include "gl/data/gl_viewpointbuffer.h"
#include "gl/data/gl_modelbuffer.h"
#include "gl/data/gl_texturematrixbuffer.h"
#include "r_videoscale.h"

EXTERN_CVAR(Int, screenblocks)
Expand Down Expand Up @@ -106,6 +107,7 @@ void FGLRenderer::Initialize(int width, int height)
mLights = new FLightBuffer();
mViewpoints = new GLViewpointBuffer;
mModelMatrix = new GLModelBuffer;
mTextureMatrices = new GLTextureMatrixBuffer;
gl_RenderState.SetVertexBuffer(mVBO);
mFBID = 0;
mOldFBID = 0;
Expand All @@ -127,6 +129,7 @@ FGLRenderer::~FGLRenderer()
if (mLights != nullptr) delete mLights;
if (mViewpoints != nullptr) delete mViewpoints;
if (mModelMatrix != nullptr) delete mModelMatrix;
if (mTextureMatrices != nullptr) delete mTextureMatrices;
if (mFBID != 0) glDeleteFramebuffers(1, &mFBID);
if (mVAOID != 0)
{
Expand Down Expand Up @@ -161,6 +164,7 @@ void FGLRenderer::ResetSWScene()
void FGLRenderer::SetupLevel()
{
mVBO->CreateVBO();
mTextureMatrices->ValidateSize(level.sectors.Size());
}

//===========================================================================
Expand Down Expand Up @@ -509,10 +513,7 @@ void FGLRenderer::Draw2D(F2DDrawer *drawer)
// Canvas textures are stored upside down
if (cmd.mTexture->bHasCanvas)
{
gl_RenderState.mTextureMatrix.loadIdentity();
gl_RenderState.mTextureMatrix.scale(1.f, -1.f, 1.f);
gl_RenderState.mTextureMatrix.translate(0.f, 1.f, 0.0f);
gl_RenderState.EnableTextureMatrix(true);
gl_RenderState.SetTexMatrixIndex(1);
}
}
else
Expand All @@ -538,7 +539,7 @@ void FGLRenderer::Draw2D(F2DDrawer *drawer)
}
gl_RenderState.SetObjectColor(0xffffffff);
gl_RenderState.SetObjectColor2(0);
gl_RenderState.EnableTextureMatrix(false);
gl_RenderState.SetTexMatrixIndex(0);
}
glDisable(GL_SCISSOR_TEST);

Expand Down
2 changes: 2 additions & 0 deletions src/gl/renderer/gl_renderer.h
Expand Up @@ -37,6 +37,7 @@ class FCustomPostProcessShaders;
class SWSceneDrawer;
class GLViewpointBuffer;
class GLModelBuffer;
class GLTextureMatrixBuffer;
struct FRenderViewpoint;
#define NOQUEUE nullptr // just some token to be used as a placeholder

Expand Down Expand Up @@ -82,6 +83,7 @@ class FGLRenderer
FLightBuffer *mLights = nullptr;
GLViewpointBuffer *mViewpoints = nullptr;
GLModelBuffer *mModelMatrix = nullptr;
GLTextureMatrixBuffer *mTextureMatrices = nullptr;
SWSceneDrawer *swdrawer = nullptr;

FPortalSceneState mPortalState;
Expand Down

2 comments on commit 17813b4

@alexey-lysiuk
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should NUM_TEXMATRICES be defined on C++ side? Because I've got the following errors with this commit

Init Shader 'Default':
Vertex shader:
ERROR: 0:48: Use of undeclared identifier 'NUM_TEXMATRICES'
ERROR: 0:75: Use of undeclared identifier 'TextureMatrices'

Fragment shader:
ERROR: 0:48: Use of undeclared identifier 'NUM_TEXMATRICES'

@coelckers
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback case hasn't been fully tested yet. I'll do that once I got all buffers (two more yet to be done) and have consolidated the source files for them.

Please sign in to comment.