mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-20 07:53:51 +00:00
Switch to shared layout, some rewording
This commit is contained in:
parent
cc31e1eea1
commit
d195602a9d
3 changed files with 175 additions and 38 deletions
|
@ -47,55 +47,81 @@ namespace SceneUtil
|
||||||
{
|
{
|
||||||
static int sLightId = 0;
|
static int sLightId = 0;
|
||||||
|
|
||||||
|
// Handles a GLSL shared layout by using configured offsets and strides to fill a continuous buffer, making the data upload to GPU simpler.
|
||||||
class LightBuffer : public osg::Referenced
|
class LightBuffer : public osg::Referenced
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
LightBuffer(int count) : mData(new osg::Vec4Array(3*count)), mEndian(osg::getCpuByteOrder()) {}
|
enum LayoutOffset
|
||||||
|
{
|
||||||
|
Diffuse,
|
||||||
|
DiffuseSign,
|
||||||
|
Ambient,
|
||||||
|
Specular,
|
||||||
|
Position,
|
||||||
|
AttenuationRadius
|
||||||
|
};
|
||||||
|
|
||||||
|
LightBuffer(int count)
|
||||||
|
: mData(new osg::FloatArray(3*4*count))
|
||||||
|
, mEndian(osg::getCpuByteOrder())
|
||||||
|
, mCount(count)
|
||||||
|
, mStride(12)
|
||||||
|
{
|
||||||
|
mOffsets[Diffuse] = 0;
|
||||||
|
mOffsets[Ambient] = 1;
|
||||||
|
mOffsets[Specular] = 2;
|
||||||
|
mOffsets[DiffuseSign] = 3;
|
||||||
|
mOffsets[Position] = 4;
|
||||||
|
mOffsets[AttenuationRadius] = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
LightBuffer(const LightBuffer& copy)
|
||||||
|
: osg::Referenced()
|
||||||
|
, mData(copy.mData)
|
||||||
|
, mEndian(copy.mEndian)
|
||||||
|
, mCount(copy.mCount)
|
||||||
|
, mStride(copy.mStride)
|
||||||
|
, mOffsets(copy.mOffsets)
|
||||||
|
{}
|
||||||
|
|
||||||
void setDiffuse(int index, const osg::Vec4& value)
|
void setDiffuse(int index, const osg::Vec4& value)
|
||||||
{
|
{
|
||||||
auto signedValue = value;
|
// Deal with negative lights (negative diffuse) by passing a sign bit in the unused alpha component
|
||||||
|
auto positiveColor = value;
|
||||||
float signBit = 1.0;
|
float signBit = 1.0;
|
||||||
if (value[0] < 0)
|
if (value[0] < 0)
|
||||||
{
|
{
|
||||||
signedValue *= -1.0;
|
positiveColor *= -1.0;
|
||||||
signBit = -1.0;
|
signBit = -1.0;
|
||||||
}
|
}
|
||||||
*(unsigned int*)(&(*mData)[3*index][0]) = asRGBA(signedValue);
|
*(unsigned int*)(&(*mData)[getOffset(index, Diffuse)]) = asRGBA(positiveColor);
|
||||||
*(int*)(&(*mData)[3*index][3]) = signBit;
|
*(int*)(&(*mData)[getOffset(index, DiffuseSign)]) = signBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAmbient(int index, const osg::Vec4& value)
|
void setAmbient(int index, const osg::Vec4& value)
|
||||||
{
|
{
|
||||||
*(unsigned int*)(&(*mData)[3*index][1]) = asRGBA(value);
|
*(unsigned int*)(&(*mData)[getOffset(index, Ambient)]) = asRGBA(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSpecular(int index, const osg::Vec4& value)
|
void setSpecular(int index, const osg::Vec4& value)
|
||||||
{
|
{
|
||||||
*(unsigned int*)(&(*mData)[3*index][2]) = asRGBA(value);
|
*(unsigned int*)(&(*mData)[getOffset(index, Specular)]) = asRGBA(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPosition(int index, const osg::Vec4& value)
|
void setPosition(int index, const osg::Vec4& value)
|
||||||
{
|
{
|
||||||
(*mData)[3*index+1] = value;
|
*(osg::Vec4*)(&(*mData)[getOffset(index, Position)]) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAttenuation(int index, float c, float l, float q)
|
void setAttenuationRadius(int index, const osg::Vec4& value)
|
||||||
{
|
{
|
||||||
(*mData)[3*index+2][0] = c;
|
*(osg::Vec4*)(&(*mData)[getOffset(index, AttenuationRadius)]) = value;
|
||||||
(*mData)[3*index+2][1] = l;
|
|
||||||
(*mData)[3*index+2][2] = q;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setRadius(int index, float value)
|
|
||||||
{
|
|
||||||
(*mData)[3*index+2][3] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto getPosition(int index)
|
auto getPosition(int index)
|
||||||
{
|
{
|
||||||
return (*mData)[3*index+1];
|
return *(osg::Vec4*)(&(*mData)[getOffset(index, Position)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& getData()
|
auto& getData()
|
||||||
|
@ -118,8 +144,40 @@ namespace SceneUtil
|
||||||
return mEndian == osg::BigEndian ? value.asABGR() : value.asRGBA();
|
return mEndian == osg::BigEndian ? value.asABGR() : value.asRGBA();
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Vec4Array> mData;
|
int getOffset(int index, LayoutOffset slot)
|
||||||
|
{
|
||||||
|
return mStride * index + mOffsets[slot];
|
||||||
|
}
|
||||||
|
|
||||||
|
void configureLayout(int offsetColors, int offsetPosition, int offsetAttenuationRadius, int size, int stride)
|
||||||
|
{
|
||||||
|
static constexpr auto sizeofVec4 = sizeof(GL_FLOAT) * osg::Vec4::num_components;
|
||||||
|
static constexpr auto sizeofFloat = sizeof(GL_FLOAT);
|
||||||
|
|
||||||
|
mOffsets[Diffuse] = offsetColors / sizeofFloat;
|
||||||
|
mOffsets[Ambient] = mOffsets[Diffuse] + 1;
|
||||||
|
mOffsets[Specular] = mOffsets[Diffuse] + 2;
|
||||||
|
mOffsets[DiffuseSign] = mOffsets[Diffuse] + 3;
|
||||||
|
mOffsets[Position] = offsetPosition / sizeofFloat;
|
||||||
|
mOffsets[AttenuationRadius] = offsetAttenuationRadius / sizeofFloat;
|
||||||
|
mStride = (offsetAttenuationRadius + sizeofVec4 + stride) / 4;
|
||||||
|
|
||||||
|
// Copy over previous buffers light data. Buffers populate before we know the layout.
|
||||||
|
LightBuffer oldBuffer = LightBuffer(*this);
|
||||||
|
for (int i = 0; i < oldBuffer.mCount; ++i)
|
||||||
|
{
|
||||||
|
*(osg::Vec4*)(&(*mData)[getOffset(i, Diffuse)]) = *(osg::Vec4*)(&(*mData)[oldBuffer.getOffset(i, Diffuse)]);
|
||||||
|
*(osg::Vec4*)(&(*mData)[getOffset(i, Position)]) = *(osg::Vec4*)(&(*mData)[oldBuffer.getOffset(i, Position)]);
|
||||||
|
*(osg::Vec4*)(&(*mData)[getOffset(i, AttenuationRadius)]) = *(osg::Vec4*)(&(*mData)[oldBuffer.getOffset(i, AttenuationRadius)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
osg::ref_ptr<osg::FloatArray> mData;
|
||||||
osg::Endian mEndian;
|
osg::Endian mEndian;
|
||||||
|
int mCount;
|
||||||
|
int mStride;
|
||||||
|
std::unordered_map<LayoutOffset, int> mOffsets;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LightStateCache
|
class LightStateCache
|
||||||
|
@ -165,8 +223,8 @@ namespace SceneUtil
|
||||||
buffer->setPosition(0, light->getPosition());
|
buffer->setPosition(0, light->getPosition());
|
||||||
|
|
||||||
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
|
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
|
||||||
buffer->mData->setBufferObject(ubo);
|
buffer->getData()->setBufferObject(ubo);
|
||||||
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), buffer->mData.get(), 0, buffer->mData->getTotalDataSize());
|
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), buffer->getData().get(), 0, buffer->getData()->getTotalDataSize());
|
||||||
|
|
||||||
stateset->setAttributeAndModes(ubb, mode);
|
stateset->setAttributeAndModes(ubb, mode);
|
||||||
|
|
||||||
|
@ -572,11 +630,24 @@ namespace SceneUtil
|
||||||
class LightManagerStateAttribute : public osg::StateAttribute
|
class LightManagerStateAttribute : public osg::StateAttribute
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LightManagerStateAttribute() : mLightManager(nullptr) {}
|
LightManagerStateAttribute()
|
||||||
LightManagerStateAttribute(LightManager* lightManager) : mLightManager(lightManager) {}
|
: mLightManager(nullptr) {}
|
||||||
|
|
||||||
LightManagerStateAttribute(const LightManagerStateAttribute& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
|
LightManagerStateAttribute(LightManager* lightManager)
|
||||||
: osg::StateAttribute(copy,copyop),mLightManager(copy.mLightManager) {}
|
: mLightManager(lightManager)
|
||||||
|
, mDummyProgram(new osg::Program)
|
||||||
|
{
|
||||||
|
static const std::string dummyVertSource = generateDummyShader(mLightManager->getMaxLightsInScene());
|
||||||
|
|
||||||
|
mDummyProgram->addShader(new osg::Shader(osg::Shader::VERTEX, dummyVertSource));
|
||||||
|
mDummyProgram->addBindUniformBlock("LightBufferBinding", static_cast<int>(Shader::UBOBinding::LightBuffer));
|
||||||
|
// Needed to query the layout of the buffer object. The layout specifier needed to use the std140 layout is not reliably
|
||||||
|
// available, regardless of extensions, until GLSL 140.
|
||||||
|
mLightManager->getOrCreateStateSet()->setAttributeAndModes(mDummyProgram, osg::StateAttribute::ON|osg::StateAttribute::PROTECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
LightManagerStateAttribute(const LightManagerStateAttribute& copy, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
|
||||||
|
: osg::StateAttribute(copy,copyop), mLightManager(copy.mLightManager) {}
|
||||||
|
|
||||||
int compare(const StateAttribute &sa) const override
|
int compare(const StateAttribute &sa) const override
|
||||||
{
|
{
|
||||||
|
@ -585,12 +656,76 @@ namespace SceneUtil
|
||||||
|
|
||||||
META_StateAttribute(NifOsg, LightManagerStateAttribute, osg::StateAttribute::LIGHT)
|
META_StateAttribute(NifOsg, LightManagerStateAttribute, osg::StateAttribute::LIGHT)
|
||||||
|
|
||||||
|
void initSharedLayout(osg::GLExtensions* ext, int handle) const
|
||||||
|
{
|
||||||
|
std::vector<unsigned int> index = { static_cast<int>(Shader::UBOBinding::LightBuffer) };
|
||||||
|
int totalBlockSize = -1;
|
||||||
|
int stride = -1;
|
||||||
|
|
||||||
|
ext->glGetActiveUniformBlockiv(handle, 0, GL_UNIFORM_BLOCK_DATA_SIZE, &totalBlockSize);
|
||||||
|
ext->glGetActiveUniformsiv(handle, index.size(), index.data(), GL_UNIFORM_ARRAY_STRIDE, &stride);
|
||||||
|
|
||||||
|
std::vector<const char*> names = {
|
||||||
|
"LightBuffer[0].packedColors"
|
||||||
|
,"LightBuffer[0].position"
|
||||||
|
,"LightBuffer[0].attenuation"
|
||||||
|
};
|
||||||
|
std::vector<unsigned int> indices(names.size());
|
||||||
|
std::vector<int> offsets(names.size());
|
||||||
|
|
||||||
|
ext->glGetUniformIndices(handle, names.size(), names.data(), indices.data());
|
||||||
|
ext->glGetActiveUniformsiv(handle, indices.size(), indices.data(), GL_UNIFORM_OFFSET, offsets.data());
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
auto& buf = mLightManager->getLightBuffer(i);
|
||||||
|
buf->configureLayout(offsets[0], offsets[1], offsets[2], totalBlockSize, stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void apply(osg::State& state) const override
|
void apply(osg::State& state) const override
|
||||||
|
{
|
||||||
|
static bool init = false;
|
||||||
|
if (!init)
|
||||||
|
{
|
||||||
|
auto handle = mDummyProgram->getPCP(state)->getHandle();
|
||||||
|
auto* ext = state.get<osg::GLExtensions>();
|
||||||
|
|
||||||
|
int activeUniformBlocks = 0;
|
||||||
|
ext->glGetProgramiv(handle, GL_ACTIVE_UNIFORM_BLOCKS, &activeUniformBlocks);
|
||||||
|
|
||||||
|
// wait until the UBO binding is created
|
||||||
|
if (activeUniformBlocks > 0)
|
||||||
|
{
|
||||||
|
initSharedLayout(ext, handle);
|
||||||
|
init = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
mLightManager->getLightBuffer(state.getFrameStamp()->getFrameNumber())->dirty();
|
mLightManager->getLightBuffer(state.getFrameStamp()->getFrameNumber())->dirty();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::string generateDummyShader(int maxLightsInScene)
|
||||||
|
{
|
||||||
|
return "#version 120\n"
|
||||||
|
"#extension GL_ARB_uniform_buffer_object : require\n"
|
||||||
|
"struct LightData {\n"
|
||||||
|
" ivec4 packedColors;\n"
|
||||||
|
" vec4 position;\n"
|
||||||
|
" vec4 attenuation;\n"
|
||||||
|
"};\n"
|
||||||
|
"uniform LightBufferBinding {\n"
|
||||||
|
" LightData LightBuffer[" + std::to_string(mLightManager->getMaxLightsInScene()) + "];\n"
|
||||||
|
"};\n"
|
||||||
|
"void main() { gl_Position = vec4(0.0); }\n";
|
||||||
|
}
|
||||||
|
|
||||||
LightManager* mLightManager;
|
LightManager* mLightManager;
|
||||||
|
osg::ref_ptr<osg::Program> mDummyProgram;
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::unordered_map<std::string, LightingMethod> LightManager::mLightingMethodSettingMap = {
|
const std::unordered_map<std::string, LightingMethod> LightManager::mLightingMethodSettingMap = {
|
||||||
|
@ -654,12 +789,15 @@ namespace SceneUtil
|
||||||
bool supportsUBO = exts && exts->isUniformBufferObjectSupported;
|
bool supportsUBO = exts && exts->isUniformBufferObjectSupported;
|
||||||
bool supportsGPU4 = exts && exts->isGpuShader4Supported;
|
bool supportsGPU4 = exts && exts->isGpuShader4Supported;
|
||||||
|
|
||||||
if (getLightingMethod() == LightingMethod::SingleUBO)
|
static bool hasLoggedWarnings = false;
|
||||||
|
|
||||||
|
if (getLightingMethod() == LightingMethod::SingleUBO && !hasLoggedWarnings)
|
||||||
{
|
{
|
||||||
if (!supportsUBO)
|
if (!supportsUBO)
|
||||||
Log(Debug::Info) << "GL_ARB_uniform_buffer_object not supported: using fallback uniforms";
|
Log(Debug::Warning) << "GL_ARB_uniform_buffer_object not supported: switching to shader compatibility lighting mode";
|
||||||
else if (!supportsGPU4)
|
if (!supportsGPU4)
|
||||||
Log(Debug::Info) << "GL_EXT_gpu_shader4 not supported: using fallback uniforms";
|
Log(Debug::Warning) << "GL_EXT_gpu_shader4 not supported: switching to shader compatibility lighting mode";
|
||||||
|
hasLoggedWarnings = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int targetLights = Settings::Manager::getInt("max lights", "Shaders");
|
int targetLights = Settings::Manager::getInt("max lights", "Shaders");
|
||||||
|
@ -978,8 +1116,7 @@ namespace SceneUtil
|
||||||
auto& buf = getLightBuffer(frameNum);
|
auto& buf = getLightBuffer(frameNum);
|
||||||
buf->setDiffuse(index, light->getDiffuse());
|
buf->setDiffuse(index, light->getDiffuse());
|
||||||
buf->setAmbient(index, light->getAmbient());
|
buf->setAmbient(index, light->getAmbient());
|
||||||
buf->setAttenuation(index, light->getConstantAttenuation(), light->getLinearAttenuation(), light->getQuadraticAttenuation());
|
buf->setAttenuationRadius(index, osg::Vec4(light->getConstantAttenuation(), light->getLinearAttenuation(), light->getQuadraticAttenuation(), lightSource->getRadius()));
|
||||||
buf->setRadius(index, lightSource->getRadius());
|
|
||||||
buf->setPosition(index, light->getPosition() * (*viewMatrix));
|
buf->setPosition(index, light->getPosition() * (*viewMatrix));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -453,7 +453,7 @@ lighting method = shaders compatibility
|
||||||
|
|
||||||
# Sets the bounding sphere multiplier of light sources, which are used to
|
# Sets the bounding sphere multiplier of light sources, which are used to
|
||||||
# determine if an object should receive lighting. Higher values will allow for
|
# determine if an object should receive lighting. Higher values will allow for
|
||||||
# smoother transitions of light sources, but may have a performance cost and
|
# smoother transitions of light sources, but may carry a performance cost and
|
||||||
# requires a higher number of 'max lights' set. It is recommended to keep this
|
# requires a higher number of 'max lights' set. It is recommended to keep this
|
||||||
# at 1.0 with 'legacy' lighting enabled.
|
# at 1.0 with 'legacy' lighting enabled.
|
||||||
light bounds multiplier = 1.75
|
light bounds multiplier = 1.75
|
||||||
|
|
|
@ -43,7 +43,8 @@ struct LightData
|
||||||
uniform int PointLightIndex[@maxLights];
|
uniform int PointLightIndex[@maxLights];
|
||||||
uniform int PointLightCount;
|
uniform int PointLightCount;
|
||||||
|
|
||||||
layout(std140) uniform LightBufferBinding
|
// Defaults to shared layout. If we ever move to GLSL 140, std140 layout should be considered
|
||||||
|
uniform LightBufferBinding
|
||||||
{
|
{
|
||||||
LightData LightBuffer[@maxLightsInScene];
|
LightData LightBuffer[@maxLightsInScene];
|
||||||
};
|
};
|
||||||
|
@ -92,7 +93,6 @@ void perLightSun(out vec3 ambientOut, out vec3 diffuseOut, vec3 viewPos, vec3 vi
|
||||||
}
|
}
|
||||||
lambert *= clamp(-8.0 * (1.0 - 0.3) * eyeCosine + 1.0, 0.3, 1.0);
|
lambert *= clamp(-8.0 * (1.0 - 0.3) * eyeCosine + 1.0, 0.3, 1.0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
diffuseOut = sunDiffuse * lambert;
|
diffuseOut = sunDiffuse * lambert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue