- let the light buffer use IDataBuffer as well.

This commit is contained in:
Christoph Oelckers 2018-10-28 15:04:57 +01:00
parent 54de0bf59f
commit bd7df13200
7 changed files with 58 additions and 100 deletions

View file

@ -25,8 +25,6 @@
** **
**/ **/
#include "gl_load/gl_system.h"
#include "gl/shaders/gl_shader.h"
#include "gl/dynlights/gl_lightbuffer.h" #include "gl/dynlights/gl_lightbuffer.h"
#include "hwrenderer/utility/hw_clock.h" #include "hwrenderer/utility/hw_clock.h"
#include "hwrenderer/dynlights/hw_dynlightdata.h" #include "hwrenderer/dynlights/hw_dynlightdata.h"
@ -40,42 +38,30 @@ FLightBuffer::FLightBuffer()
{ {
int maxNumberOfLights = 40000; int maxNumberOfLights = 40000;
mPersistentBuffer = screen->BuffersArePersistent();
mBufferSize = maxNumberOfLights * ELEMENTS_PER_LIGHT; mBufferSize = maxNumberOfLights * ELEMENTS_PER_LIGHT;
mByteSize = mBufferSize * ELEMENT_SIZE; mByteSize = mBufferSize * ELEMENT_SIZE;
// Hack alert: On Intel's GL driver SSBO's perform quite worse than UBOs. // Hack alert: On Intel's GL driver SSBO's perform quite worse than UBOs.
// We only want to disable using SSBOs for lights but not disable the feature entirely. // We only want to disable using SSBOs for lights but not disable the feature entirely.
// Note that using an uniform buffer here will limit the number of lights per surface so it isn't done for NVidia and AMD. // Note that using an uniform buffer here will limit the number of lights per surface so it isn't done for NVidia and AMD.
if (gl.flags & RFL_SHADER_STORAGE_BUFFER && !strstr(gl.vendorstring, "Intel")) if (screen->hwcaps & RFL_SHADER_STORAGE_BUFFER && !strstr(screen->gl_vendorstring, "Intel"))
{ {
mBufferType = GL_SHADER_STORAGE_BUFFER; mBufferType = true;
mBlockAlign = 0; mBlockAlign = 0;
mBlockSize = mBufferSize; mBlockSize = mBufferSize;
mMaxUploadSize = mBlockSize; mMaxUploadSize = mBlockSize;
} }
else else
{ {
mBufferType = GL_UNIFORM_BUFFER; mBufferType = false;
mBlockSize = gl.maxuniformblock / ELEMENT_SIZE; mBlockSize = screen->maxuniformblock / ELEMENT_SIZE;
mBlockAlign = gl.uniformblockalignment / ELEMENT_SIZE; mBlockAlign = screen->uniformblockalignment / ELEMENT_SIZE;
mMaxUploadSize = (mBlockSize - mBlockAlign); mMaxUploadSize = (mBlockSize - mBlockAlign);
mByteSize += gl.maxuniformblock; // to avoid mapping beyond the end of the buffer. mByteSize += screen->maxuniformblock; // to avoid mapping beyond the end of the buffer.
} }
glGenBuffers(1, &mBufferId); mBuffer = screen->CreateDataBuffer(LIGHTBUF_BINDINGPOINT, mBufferType);
glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, mBufferId); mBuffer->SetData(mByteSize, nullptr, false);
glBindBuffer(mBufferType, mBufferId); // Note: Some older AMD drivers don't do that in glBindBufferBase, as they should.
if (mPersistentBuffer)
{
glBufferStorage(mBufferType, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
}
else
{
glBufferData(mBufferType, mByteSize, NULL, GL_DYNAMIC_DRAW);
mBufferPointer = NULL;
}
Clear(); Clear();
mLastMappedIndex = UINT_MAX; mLastMappedIndex = UINT_MAX;
@ -83,8 +69,7 @@ FLightBuffer::FLightBuffer()
FLightBuffer::~FLightBuffer() FLightBuffer::~FLightBuffer()
{ {
glBindBuffer(mBufferType, 0); delete mBuffer;
glDeleteBuffers(1, &mBufferId);
} }
void FLightBuffer::Clear() void FLightBuffer::Clear()
@ -94,20 +79,7 @@ void FLightBuffer::Clear()
void FLightBuffer::CheckSize() void FLightBuffer::CheckSize()
{ {
// reallocate the buffer with twice the size // create the new buffer's storage (at least twice as large as the old one)
unsigned int newbuffer;
// first unmap the old buffer
glBindBuffer(mBufferType, mBufferId);
glUnmapBuffer(mBufferType);
// create and bind the new buffer, bind the old one to a copy target (too bad that DSA is not yet supported well enough to omit this crap.)
glGenBuffers(1, &newbuffer);
glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, newbuffer);
glBindBuffer(mBufferType, newbuffer); // Note: Some older AMD drivers don't do that in glBindBufferBase, as they should.
glBindBuffer(GL_COPY_READ_BUFFER, mBufferId);
// create the new buffer's storage (twice as large as the old one)
int oldbytesize = mByteSize; int oldbytesize = mByteSize;
unsigned int bufferbytesize = mBufferedData.Size() * 4; unsigned int bufferbytesize = mBufferedData.Size() * 4;
if (bufferbytesize > mByteSize) if (bufferbytesize > mByteSize)
@ -119,27 +91,12 @@ void FLightBuffer::CheckSize()
mByteSize *= 2; mByteSize *= 2;
} }
mBufferSize = mByteSize / ELEMENT_SIZE; mBufferSize = mByteSize / ELEMENT_SIZE;
mBuffer->Resize(mByteSize);
if (mPersistentBuffer) Map();
{ memcpy(((float*)mBuffer->Memory()) + mBlockSize*4, &mBufferedData[0], bufferbytesize);
glBufferStorage(mBufferType, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
}
else
{
glBufferData(mBufferType, mByteSize, NULL, GL_DYNAMIC_DRAW);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT);
}
// copy contents and delete the old buffer.
glCopyBufferSubData(GL_COPY_READ_BUFFER, mBufferType, 0, 0, mByteSize/2);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glDeleteBuffers(1, &mBufferId);
mBufferId = newbuffer;
Begin();
memcpy(mBufferPointer + mBlockSize*4, &mBufferedData[0], bufferbytesize);
mBufferedData.Clear(); mBufferedData.Clear();
Finish(); Unmap();
} }
int FLightBuffer::UploadLights(FDynLightData &data) int FLightBuffer::UploadLights(FDynLightData &data)
@ -168,6 +125,7 @@ int FLightBuffer::UploadLights(FDynLightData &data)
totalsize = size0 + size1 + size2 + 1; totalsize = size0 + size1 + size2 + 1;
} }
float *mBufferPointer = (float*)mBuffer->Memory();
assert(mBufferPointer != nullptr); assert(mBufferPointer != nullptr);
if (mBufferPointer == nullptr) return -1; if (mBufferPointer == nullptr) return -1;
if (totalsize <= 1) return -1; // there are no lights if (totalsize <= 1) return -1; // there are no lights
@ -200,35 +158,15 @@ int FLightBuffer::UploadLights(FDynLightData &data)
} }
} }
void FLightBuffer::Begin() int FLightBuffer::DoBindUBO(unsigned int index)
{
if (!mPersistentBuffer)
{
glBindBuffer(mBufferType, mBufferId);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT);
}
}
void FLightBuffer::Finish()
{
if (!mPersistentBuffer)
{
glBindBuffer(mBufferType, mBufferId);
glUnmapBuffer(mBufferType);
mBufferPointer = NULL;
}
if (mBufferedData.Size() > 0) CheckSize();
}
int FLightBuffer::BindUBO(unsigned int index)
{ {
// this function will only get called if a uniform buffer is used. For a shader storage buffer we only need to bind the buffer once at the start.
unsigned int offset = (index / mBlockAlign) * mBlockAlign; unsigned int offset = (index / mBlockAlign) * mBlockAlign;
if (offset != mLastMappedIndex) if (offset != mLastMappedIndex)
{ {
// this will only get called if a uniform buffer is used. For a shader storage buffer we only need to bind the buffer once at the start to all shader programs
mLastMappedIndex = offset; mLastMappedIndex = offset;
glBindBufferRange(GL_UNIFORM_BUFFER, LIGHTBUF_BINDINGPOINT, mBufferId, offset * ELEMENT_SIZE, mBlockSize * ELEMENT_SIZE); mBuffer->BindRange(offset * ELEMENT_SIZE, mBlockSize * ELEMENT_SIZE);
} }
return (index - offset); return (index - offset);
} }

View file

@ -3,15 +3,17 @@
#include "tarray.h" #include "tarray.h"
#include "hwrenderer/dynlights/hw_dynlightdata.h" #include "hwrenderer/dynlights/hw_dynlightdata.h"
#include "hwrenderer/data/buffers.h"
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
class FRenderState;
class FLightBuffer class FLightBuffer
{ {
unsigned int mBufferId; IDataBuffer *mBuffer;
float * mBufferPointer;
unsigned int mBufferType; bool mBufferType;
std::atomic<unsigned int> mIndex; std::atomic<unsigned int> mIndex;
unsigned int mLastMappedIndex; unsigned int mLastMappedIndex;
unsigned int mBlockAlign; unsigned int mBlockAlign;
@ -19,23 +21,24 @@ class FLightBuffer
unsigned int mBufferSize; unsigned int mBufferSize;
unsigned int mByteSize; unsigned int mByteSize;
unsigned int mMaxUploadSize; unsigned int mMaxUploadSize;
bool mPersistentBuffer;
std::mutex mBufferMutex; std::mutex mBufferMutex;
TArray<float> mBufferedData; TArray<float> mBufferedData;
void CheckSize();
public: public:
FLightBuffer(); FLightBuffer();
~FLightBuffer(); ~FLightBuffer();
void Clear(); void Clear();
int UploadLights(FDynLightData &data); int UploadLights(FDynLightData &data);
void Begin(); void Map() { mBuffer->Map(); }
void Finish(); void Unmap() { mBuffer->Unmap(); if (mBufferedData.Size() > 0) CheckSize(); }
void CheckSize();
int BindUBO(unsigned int index);
unsigned int GetBlockSize() const { return mBlockSize; } unsigned int GetBlockSize() const { return mBlockSize; }
unsigned int GetBufferType() const { return mBufferType; } bool GetBufferType() const { return mBufferType; }
int DoBindUBO(unsigned int index);
int ShaderIndex(unsigned int index) const int ShaderIndex(unsigned int index) const
{ {
@ -45,6 +48,22 @@ public:
return int(index-offset); return int(index-offset);
} }
// Only relevant for OpenGL, so this does not need access to the render state.
int BindUBO(unsigned int index)
{
if (!mBufferType && index > -1)
{
index = DoBindUBO(index);
}
return index;
}
// The parameter is a reminder for Vulkan.
void BindBase(FRenderState &state)
{
mBuffer->BindBase();
}
}; };
int gl_SetDynModelLight(AActor *self, int dynlightindex); int gl_SetDynModelLight(AActor *self, int dynlightindex);

View file

@ -200,11 +200,7 @@ bool FGLRenderState::ApplyShader()
matrixToGL(identityMatrix, activeShader->normalmodelmatrix_index); matrixToGL(identityMatrix, activeShader->normalmodelmatrix_index);
} }
auto index = mLightIndex; auto index = GLRenderer->mLights->BindUBO(mLightIndex);
if (index > -1 && GLRenderer->mLights->GetBufferType() == GL_UNIFORM_BUFFER)
{
index = GLRenderer->mLights->BindUBO(index);
}
activeShader->muLightIndex.Set(index); activeShader->muLightIndex.Set(index);
return true; return true;

View file

@ -105,7 +105,7 @@ void FDrawInfo::CreateScene()
// clip the scene and fill the drawlists // clip the scene and fill the drawlists
Bsp.Clock(); Bsp.Clock();
screen->mVertexData->Map(); screen->mVertexData->Map();
GLRenderer->mLights->Begin(); GLRenderer->mLights->Map();
// Give the DrawInfo the viewpoint in fixed point because that's what the nodes are. // Give the DrawInfo the viewpoint in fixed point because that's what the nodes are.
viewx = FLOAT2FIXED(vp.Pos.X); viewx = FLOAT2FIXED(vp.Pos.X);
@ -130,7 +130,7 @@ void FDrawInfo::CreateScene()
HandleHackedSubsectors(); // open sector hacks for deep water HandleHackedSubsectors(); // open sector hacks for deep water
ProcessSectorStacks(in_area); // merge visplanes of sector stacks ProcessSectorStacks(in_area); // merge visplanes of sector stacks
PrepareUnhandledMissingTextures(); PrepareUnhandledMissingTextures();
GLRenderer->mLights->Finish(); GLRenderer->mLights->Unmap();
screen->mVertexData->Unmap(); screen->mVertexData->Unmap();
ProcessAll.Unclock(); ProcessAll.Unclock();
@ -153,6 +153,7 @@ void FDrawInfo::RenderScene(int recursion)
glDepthMask(true); glDepthMask(true);
if (!gl_no_skyclear) screen->mPortalState->RenderFirstSkyPortal(recursion, this); if (!gl_no_skyclear) screen->mPortalState->RenderFirstSkyPortal(recursion, this);
GLRenderer->mLights->BindBase(gl_RenderState); // not needed for OpenGL but necessary for Vulkan command buffers to do it here!
gl_RenderState.EnableFog(true); gl_RenderState.EnableFog(true);
gl_RenderState.SetRenderStyle(STYLE_Source); gl_RenderState.SetRenderStyle(STYLE_Source);

View file

@ -168,9 +168,9 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
assert(GLRenderer->mLights != NULL); assert(GLRenderer->mLights != NULL);
unsigned int lightbuffertype = GLRenderer->mLights->GetBufferType(); bool lightbuffertype = GLRenderer->mLights->GetBufferType();
unsigned int lightbuffersize = GLRenderer->mLights->GetBlockSize(); unsigned int lightbuffersize = GLRenderer->mLights->GetBlockSize();
if (lightbuffertype == GL_UNIFORM_BUFFER) if (!lightbuffertype)
{ {
vp_comb.Format("#version 330 core\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize); vp_comb.Format("#version 330 core\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize);
} }
@ -344,7 +344,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
texturematrix_index = glGetUniformLocation(hShader, "TextureMatrix"); texturematrix_index = glGetUniformLocation(hShader, "TextureMatrix");
normalmodelmatrix_index = glGetUniformLocation(hShader, "NormalModelMatrix"); normalmodelmatrix_index = glGetUniformLocation(hShader, "NormalModelMatrix");
if (lightbuffertype == GL_UNIFORM_BUFFER) if (!lightbuffertype)
{ {
int tempindex = glGetUniformBlockIndex(hShader, "LightBufferUBO"); int tempindex = glGetUniformBlockIndex(hShader, "LightBufferUBO");
if (tempindex != -1) glUniformBlockBinding(hShader, tempindex, LIGHTBUF_BINDINGPOINT); if (tempindex != -1) glUniformBlockBinding(hShader, tempindex, LIGHTBUF_BINDINGPOINT);

View file

@ -119,6 +119,8 @@ void OpenGLFrameBuffer::InitializeState()
hwcaps = gl.flags; hwcaps = gl.flags;
glslversion = gl.glslversion; glslversion = gl.glslversion;
uniformblockalignment = gl.uniformblockalignment; uniformblockalignment = gl.uniformblockalignment;
maxuniformblock = gl.maxuniformblock;
gl_vendorstring = gl.vendorstring;
if (first) if (first)
{ {

View file

@ -374,6 +374,8 @@ public:
int stencilValue = 0; // Global stencil test value int stencilValue = 0; // Global stencil test value
bool enable_quadbuffered = false; // Quad-buffered stereo available? bool enable_quadbuffered = false; // Quad-buffered stereo available?
unsigned int uniformblockalignment = 256; // Hardware dependent uniform buffer alignment. unsigned int uniformblockalignment = 256; // Hardware dependent uniform buffer alignment.
unsigned int maxuniformblock = 65536;
const char *gl_vendorstring; // On OpenGL (not Vulkan) we have to account for some issues with Intel.
FPortalSceneState *mPortalState; // global portal state. FPortalSceneState *mPortalState; // global portal state.
FSkyVertexBuffer *mSkyData = nullptr; // the sky vertex buffer FSkyVertexBuffer *mSkyData = nullptr; // the sky vertex buffer
FFlatVertexBuffer *mVertexData = nullptr; // Global vertex data FFlatVertexBuffer *mVertexData = nullptr; // Global vertex data