- 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 "hwrenderer/utility/hw_clock.h"
#include "hwrenderer/dynlights/hw_dynlightdata.h"
@ -40,42 +38,30 @@ FLightBuffer::FLightBuffer()
{
int maxNumberOfLights = 40000;
mPersistentBuffer = screen->BuffersArePersistent();
mBufferSize = maxNumberOfLights * ELEMENTS_PER_LIGHT;
mByteSize = mBufferSize * ELEMENT_SIZE;
// 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.
// 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;
mBlockSize = mBufferSize;
mMaxUploadSize = mBlockSize;
}
else
{
mBufferType = GL_UNIFORM_BUFFER;
mBlockSize = gl.maxuniformblock / ELEMENT_SIZE;
mBlockAlign = gl.uniformblockalignment / ELEMENT_SIZE;
mBufferType = false;
mBlockSize = screen->maxuniformblock / ELEMENT_SIZE;
mBlockAlign = screen->uniformblockalignment / ELEMENT_SIZE;
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);
glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, mBufferId);
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;
}
mBuffer = screen->CreateDataBuffer(LIGHTBUF_BINDINGPOINT, mBufferType);
mBuffer->SetData(mByteSize, nullptr, false);
Clear();
mLastMappedIndex = UINT_MAX;
@ -83,8 +69,7 @@ FLightBuffer::FLightBuffer()
FLightBuffer::~FLightBuffer()
{
glBindBuffer(mBufferType, 0);
glDeleteBuffers(1, &mBufferId);
delete mBuffer;
}
void FLightBuffer::Clear()
@ -94,20 +79,7 @@ void FLightBuffer::Clear()
void FLightBuffer::CheckSize()
{
// reallocate the buffer with twice the size
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)
// create the new buffer's storage (at least twice as large as the old one)
int oldbytesize = mByteSize;
unsigned int bufferbytesize = mBufferedData.Size() * 4;
if (bufferbytesize > mByteSize)
@ -119,27 +91,12 @@ void FLightBuffer::CheckSize()
mByteSize *= 2;
}
mBufferSize = mByteSize / ELEMENT_SIZE;
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 = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT);
}
mBuffer->Resize(mByteSize);
// 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);
Map();
memcpy(((float*)mBuffer->Memory()) + mBlockSize*4, &mBufferedData[0], bufferbytesize);
mBufferedData.Clear();
Finish();
Unmap();
}
int FLightBuffer::UploadLights(FDynLightData &data)
@ -168,6 +125,7 @@ int FLightBuffer::UploadLights(FDynLightData &data)
totalsize = size0 + size1 + size2 + 1;
}
float *mBufferPointer = (float*)mBuffer->Memory();
assert(mBufferPointer != nullptr);
if (mBufferPointer == nullptr) return -1;
if (totalsize <= 1) return -1; // there are no lights
@ -200,35 +158,15 @@ int FLightBuffer::UploadLights(FDynLightData &data)
}
}
void FLightBuffer::Begin()
{
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)
int FLightBuffer::DoBindUBO(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;
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;
glBindBufferRange(GL_UNIFORM_BUFFER, LIGHTBUF_BINDINGPOINT, mBufferId, offset * ELEMENT_SIZE, mBlockSize * ELEMENT_SIZE);
mBuffer->BindRange(offset * ELEMENT_SIZE, mBlockSize * ELEMENT_SIZE);
}
return (index - offset);
}

View file

@ -3,15 +3,17 @@
#include "tarray.h"
#include "hwrenderer/dynlights/hw_dynlightdata.h"
#include "hwrenderer/data/buffers.h"
#include <atomic>
#include <mutex>
class FRenderState;
class FLightBuffer
{
unsigned int mBufferId;
float * mBufferPointer;
IDataBuffer *mBuffer;
unsigned int mBufferType;
bool mBufferType;
std::atomic<unsigned int> mIndex;
unsigned int mLastMappedIndex;
unsigned int mBlockAlign;
@ -19,24 +21,25 @@ class FLightBuffer
unsigned int mBufferSize;
unsigned int mByteSize;
unsigned int mMaxUploadSize;
bool mPersistentBuffer;
std::mutex mBufferMutex;
TArray<float> mBufferedData;
void CheckSize();
public:
FLightBuffer();
~FLightBuffer();
void Clear();
int UploadLights(FDynLightData &data);
void Begin();
void Finish();
void CheckSize();
int BindUBO(unsigned int index);
void Map() { mBuffer->Map(); }
void Unmap() { mBuffer->Unmap(); if (mBufferedData.Size() > 0) CheckSize(); }
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
{
if (mBlockAlign == 0) return index;
@ -45,6 +48,22 @@ public:
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);

View file

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

View file

@ -105,7 +105,7 @@ void FDrawInfo::CreateScene()
// clip the scene and fill the drawlists
Bsp.Clock();
screen->mVertexData->Map();
GLRenderer->mLights->Begin();
GLRenderer->mLights->Map();
// Give the DrawInfo the viewpoint in fixed point because that's what the nodes are.
viewx = FLOAT2FIXED(vp.Pos.X);
@ -130,7 +130,7 @@ void FDrawInfo::CreateScene()
HandleHackedSubsectors(); // open sector hacks for deep water
ProcessSectorStacks(in_area); // merge visplanes of sector stacks
PrepareUnhandledMissingTextures();
GLRenderer->mLights->Finish();
GLRenderer->mLights->Unmap();
screen->mVertexData->Unmap();
ProcessAll.Unclock();
@ -153,6 +153,7 @@ void FDrawInfo::RenderScene(int recursion)
glDepthMask(true);
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.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);
unsigned int lightbuffertype = GLRenderer->mLights->GetBufferType();
bool lightbuffertype = GLRenderer->mLights->GetBufferType();
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);
}
@ -344,7 +344,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
texturematrix_index = glGetUniformLocation(hShader, "TextureMatrix");
normalmodelmatrix_index = glGetUniformLocation(hShader, "NormalModelMatrix");
if (lightbuffertype == GL_UNIFORM_BUFFER)
if (!lightbuffertype)
{
int tempindex = glGetUniformBlockIndex(hShader, "LightBufferUBO");
if (tempindex != -1) glUniformBlockBinding(hShader, tempindex, LIGHTBUF_BINDINGPOINT);

View file

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

View file

@ -374,6 +374,8 @@ public:
int stencilValue = 0; // Global stencil test value
bool enable_quadbuffered = false; // Quad-buffered stereo available?
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.
FSkyVertexBuffer *mSkyData = nullptr; // the sky vertex buffer
FFlatVertexBuffer *mVertexData = nullptr; // Global vertex data