Browse Source

Shaders: introduce Phong::Flag::LightCulling.

After several failed attempts to make UBO performance not suck on Intel
Mesa and Windows drivers, I ended up hiding the dynamic aspect under a
flag. That way it's still possible to get the proper perf in UBO
workflows that don't do light culling, and for workflows where light
culling matters the 2x slowdown might be still better than looping
through several extra lights that don't contribute anything.
pull/518/head
Vladimír Vondruš 5 years ago
parent
commit
afd8d7c8f9
  1. 26
      src/Magnum/Shaders/Phong.frag
  2. 30
      src/Magnum/Shaders/Phong.h
  3. 9
      src/Magnum/Shaders/PhongGL.cpp
  4. 28
      src/Magnum/Shaders/PhongGL.h
  5. 95
      src/Magnum/Shaders/Test/PhongGLTest.cpp
  6. 3
      src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp

26
src/Magnum/Shaders/Phong.frag

@ -422,7 +422,7 @@ void main() {
highp const vec3 cameraDirection = normalize(-transformedPosition);
/* Add diffuse color for each light */
#ifndef UNIFORM_BUFFERS
#ifndef LIGHT_CULLING
for(int i = 0; i < LIGHT_COUNT; ++i)
#else
for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawId].draw_lightCount); i < actualLightCount; ++i)
@ -432,21 +432,33 @@ void main() {
#ifndef UNIFORM_BUFFERS
lightColors[i]
#else
lights[lightOffset + i].light_color
lights[
#ifdef LIGHT_CULLING
lightOffset +
#endif
i].light_color
#endif
;
lowp const vec3 lightSpecularColor =
#ifndef UNIFORM_BUFFERS
lightSpecularColors[i]
#else
lights[lightOffset + i].light_specularColor
lights[
#ifdef LIGHT_CULLING
lightOffset +
#endif
i].light_specularColor
#endif
;
lowp const float lightRange =
#ifndef UNIFORM_BUFFERS
lightRanges[i]
#else
lights[lightOffset + i].light_range
lights[
#ifdef LIGHT_CULLING
lightOffset +
#endif
i].light_range
#endif
;
@ -454,7 +466,11 @@ void main() {
#ifndef UNIFORM_BUFFERS
lightPositions[i]
#else
lights[lightOffset + i].position
lights[
#ifdef LIGHT_CULLING
lightOffset +
#endif
i].position
#endif
;
highp const vec4 lightDirection = vec4(lightPosition.xyz - transformedPosition*lightPosition.w, lightPosition.w);

30
src/Magnum/Shaders/Phong.h

@ -188,6 +188,9 @@ struct PhongDrawUniform {
* References the first light in the @ref PhongLightUniform array. Should
* be less than the light count passed to @ref PhongGL constructor. Default
* value is @cpp 0 @ce.
*
* Used only if @ref PhongGL::Flag::LightCulling is enabled, otherwise
* light offset is implicitly @cpp 0 @ce.
*/
UnsignedInt lightOffset;
@ -198,6 +201,9 @@ struct PhongDrawUniform {
* @ref PhongLightUniform array. Gets clamped by the shader so it's
* together with @ref lightOffset not larger than the light count passed to
* @ref PhongGL constructor. Default value is @cpp 0xffffffffu @ce.
*
* Used only if @ref PhongGL::Flag::LightCulling is enabled, otherwise
* light count is implicitly @ref PhongGL::lightCount().
*/
UnsignedInt lightCount;
};
@ -307,9 +313,9 @@ struct PhongMaterialUniform {
*
* Default value is @cpp 0xffffffff_rgbaf @ce.
*
* Used only if @ref PhongDrawUniform::lightCount is not zero, ignored
* otherwise. If @ref PhongGL::Flag::VertexColor is enabled, the color is
* multiplied with a color coming from the @ref PhongGL::Color3 /
* Used only if the effective light count for given draw is not zero,
* ignored otherwise. If @ref PhongGL::Flag::VertexColor is enabled, the
* color is multiplied with a color coming from the @ref PhongGL::Color3 /
* @ref PhongGL::Color4 attribute.
* @see @ref PhongGL::setDiffuseColor()
*/
@ -320,8 +326,8 @@ struct PhongMaterialUniform {
*
* Default value is @cpp 0xffffff00_rgbaf @ce.
*
* Used only if @ref PhongDrawUniform::lightCount is not zero, ignored
* otherwise.
* Used only if the effective light count for given draw is not zero,
* ignored otherwise.
* @see @ref PhongGL::setSpecularColor()
*/
Color4 specularColor;
@ -333,8 +339,8 @@ struct PhongMaterialUniform {
* meaning the normal texture is not changed in any way; a value of
* @cpp 0.0f @ce disables the normal texture effect altogether.
*
* Used only if @ref PhongGL::Flag::NormalTexture is enabled and
* @ref PhongDrawUniform::lightCount is not zero, ignored otherwise.
* Used only if @ref PhongGL::Flag::NormalTexture is enabled and the
* effective light count for given draw is not zero, ignored otherwise.
* @see @ref PhongGL::setNormalTextureScale()
*/
Float normalTextureScale;
@ -345,8 +351,8 @@ struct PhongMaterialUniform {
* The larger value, the harder surface (smaller specular highlight).
* Default value is @cpp 80.0f @ce.
*
* Used only if @ref PhongDrawUniform::lightCount is not zero, ignored
* otherwise.
* Used only if the effective light count for given draw is not zero,
* ignored otherwise.
* @see @ref PhongGL::setShininess()
*/
Float shininess;
@ -378,8 +384,10 @@ struct PhongMaterialUniform {
@brief Light parameters for Phong shaders
@m_since_latest
Describes light properties referenced by the @ref PhongDrawUniform::lightOffset
and @ref PhongDrawUniform::lightCount range.
Describes light properties for each light used by the shader, either all
@ref PhongGL::lightCount() or the subrange referenced by the
@ref PhongDrawUniform::lightOffset and @ref PhongDrawUniform::lightCount range
if @ref PhongGL::Flag::LightCulling is enabled.
@see @ref PhongGL::bindLightBuffer()
*/
struct PhongLightUniform {

9
src/Magnum/Shaders/PhongGL.cpp

@ -107,6 +107,8 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
"Shaders::PhongGL: texture arrays enabled but the shader is not textured", );
CORRADE_ASSERT(!(flags & Flag::UniformBuffers) || !(flags & Flag::TextureArrays) || flags >= (Flag::TextureArrays|Flag::TextureTransformation),
"Shaders::PhongGL: texture arrays require texture transformation enabled as well if uniform buffers are used", );
CORRADE_ASSERT(!(flags & Flag::LightCulling) || (flags & Flag::UniformBuffers),
"Shaders::PhongGL: light culling requires uniform buffers to be enabled", );
#endif
#ifndef MAGNUM_TARGET_GLES
@ -252,7 +254,8 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
drawCount,
materialCount,
lightCount));
frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : "");
frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : "")
.addSource(flags >= Flag::LightCulling ? "#define LIGHT_CULLING\n" : "");
} else
#endif
{
@ -915,6 +918,7 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) {
_c(UniformBuffers)
_c(MultiDraw)
_c(TextureArrays)
_c(LightCulling)
#endif
#undef _c
/* LCOV_EXCL_STOP */
@ -942,7 +946,8 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) {
#ifndef MAGNUM_TARGET_GLES2
PhongGL::Flag::MultiDraw, /* Superset of UniformBuffers */
PhongGL::Flag::UniformBuffers,
PhongGL::Flag::TextureArrays
PhongGL::Flag::TextureArrays,
PhongGL::Flag::LightCulling
#endif
});
}

28
src/Magnum/Shaders/PhongGL.h

@ -266,11 +266,12 @@ with one default light, would look like this:
For a multidraw workflow enable @ref Flag::MultiDraw (and possibly
@ref Flag::TextureArrays) and supply desired light, material and draw count in
the @ref PhongGL(Flags, UnsignedInt, UnsignedInt, UnsignedInt) constructor. For
every draw then specify material references and texture offsets/layers, it's
also possible to perform per-draw light culling by supplying a subrange into
the @ref PhongLightUniform array using @ref PhongDrawUniform::lightOffset and
@relativeref{PhongDrawUniform,lightCount}. Besides that, the usage is similar
for all shaders, see @ref shaders-usage-multidraw for an example.
every draw then specify material references and texture offsets/layers. With
@ref Flag::LightCulling it's also possible to perform per-draw light culling by
supplying a subrange into the @ref PhongLightUniform array using
@ref PhongDrawUniform::lightOffset and @relativeref{PhongDrawUniform,lightCount}.
Besides that, the usage is similar for all shaders, see
@ref shaders-usage-multidraw for an example.
@requires_gl30 Extension @gl_extension{EXT,texture_array} for texture arrays.
@requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform
@ -689,7 +690,22 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
* @requires_webgl20 Texture arrays are not available in WebGL 1.0.
* @m_since_latest
*/
TextureArrays = 1 << 14
TextureArrays = 1 << 14,
/**
* Enable light culling in uniform buffer workflows using the
* @ref PhongDrawUniform::lightOffset and
* @ref PhongDrawUniform::lightCount fields. If not enabled, all
* @ref lightCount() lights are used for every draw. Expects that
* @ref Flag::UniformBuffers is enabled as well.
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
* @requires_gles30 Uniform buffers are not available in OpenGL ES
* 2.0.
* @requires_webgl20 Uniform buffers are not available in WebGL
* 1.0.
* @m_since_latest
*/
LightCulling = 1 << 15
#endif
};

95
src/Magnum/Shaders/Test/PhongGLTest.cpp

@ -147,6 +147,9 @@ struct PhongGLTest: GL::OpenGLTester {
void renderLightsSetOneByOne();
/* This tests just the algorithm, not affected by UBOs */
void renderLowLightAngle();
#ifndef MAGNUM_TARGET_GLES2
void renderLightCulling();
#endif
template<PhongGL::Flag flag = PhongGL::Flag{}> void renderZeroLights();
@ -253,6 +256,7 @@ constexpr struct {
/* SwiftShader has 256 uniform vectors at most, per-3D-draw is 4+4,
per-material 4, per-light 4 plus 4 for projection */
{"multiple lights, materials, draws", PhongGL::Flag::UniformBuffers, 8, 8, 24},
{"multiple lights, materials, draws + light culling", PhongGL::Flag::UniformBuffers|PhongGL::Flag::LightCulling, 8, 8, 24},
{"zero lights", PhongGL::Flag::UniformBuffers, 0, 16, 24},
{"ambient + diffuse + specular texture", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, 1, 1, 1},
{"ambient + diffuse + specular texture + texture transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1, 1, 1},
@ -261,7 +265,7 @@ constexpr struct {
{"normal texture + separate bitangents", PhongGL::Flag::UniformBuffers|PhongGL::Flag::NormalTexture|PhongGL::Flag::Bitangent, 1, 1, 1},
{"alpha mask", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AlphaMask, 1, 1, 1},
{"object ID", PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId, 1, 1, 1},
{"multidraw with all the things", PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::AmbientTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::AlphaMask|PhongGL::Flag::ObjectId|PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId, 8, 16, 24}
{"multidraw with all the things", PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::AmbientTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::AlphaMask|PhongGL::Flag::ObjectId|PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId|PhongGL::Flag::LightCulling, 8, 16, 24}
};
#endif
@ -294,7 +298,9 @@ constexpr struct {
{"zero materials", PhongGL::Flag::UniformBuffers, 1, 0, 1,
"material count can't be zero"},
{"texture arrays but no transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::TextureArrays, 1, 1, 1,
"texture arrays require texture transformation enabled as well if uniform buffers are used"}
"texture arrays require texture transformation enabled as well if uniform buffers are used"},
{"light culling but no UBOs", PhongGL::Flag::LightCulling, 1, 1, 1,
"light culling requires uniform buffers to be enabled"}
};
#endif
@ -899,8 +905,13 @@ PhongGLTest::PhongGLTest() {
&PhongGLTest::renderSetup,
&PhongGLTest::renderTeardown);
addTests({&PhongGLTest::renderLightsSetOneByOne,
&PhongGLTest::renderLowLightAngle},
addTests({
&PhongGLTest::renderLightsSetOneByOne,
&PhongGLTest::renderLowLightAngle,
#ifndef MAGNUM_TARGET_GLES2
&PhongGLTest::renderLightCulling
#endif
},
&PhongGLTest::renderSetup,
&PhongGLTest::renderTeardown);
@ -3044,6 +3055,80 @@ void PhongGLTest::renderLowLightAngle() {
(DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold}));
}
#ifndef MAGNUM_TARGET_GLES2
void PhongGLTest::renderLightCulling() {
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
#endif
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader)
CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test.");
#endif
GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32));
GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, {
ProjectionUniform3D{}
.setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f))
}};
GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, {
TransformationUniform3D{}
.setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f)))
}};
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
PhongDrawUniform{}
.setLightOffsetCount(57, 2)
}};
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, {
PhongMaterialUniform{}
.setAmbientColor(0x330033_rgbf)
.setDiffuseColor(0xccffcc_rgbf)
.setSpecularColor(0x6666ff_rgbf)
}};
/* Put one light into the first 32-bit component, one into the second to
test that both halves are checked correctly */
PhongLightUniform lights[64];
lights[57] = PhongLightUniform{}
.setPosition({-3.0f, -3.0f, 2.0f, 0.0f})
.setColor(0x993366_rgbf);
lights[58] = PhongLightUniform{}
.setPosition({3.0f, -3.0f, 2.0f, 0.0f})
.setColor(0x669933_rgbf);
GL::Buffer lightUniform{lights};
PhongGL shader{PhongGL::Flag::UniformBuffers|PhongGL::Flag::LightCulling, 64};
shader
.bindProjectionBuffer(projectionUniform)
.bindTransformationBuffer(transformationUniform)
.bindDrawBuffer(drawUniform)
.bindMaterialBuffer(materialUniform)
.bindLightBuffer(lightUniform)
.draw(sphere);
MAGNUM_VERIFY_NO_GL_ERROR();
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
#if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL))
/* SwiftShader has some minor rounding differences (max = 1). ARM Mali G71
and Apple A8 has bigger rounding differences. */
const Float maxThreshold = 8.34f, meanThreshold = 0.100f;
#else
/* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */
const Float maxThreshold = 15.34f, meanThreshold = 3.33f;
#endif
CORRADE_COMPARE_WITH(
/* Dropping the alpha channel, as it's always 1.0 */
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
Utility::Directory::join(_testDir, "PhongTestFiles/colored.tga"),
(DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold}));
}
#endif
template<PhongGL::Flag flag> void PhongGLTest::renderZeroLights() {
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
@ -3613,7 +3698,7 @@ void PhongGLTest::renderMulti() {
CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test.");
#endif
PhongGL shader{PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId|data.flags, data.lightCount, data.materialCount, data.drawCount};
PhongGL shader{PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId|PhongGL::Flag::LightCulling|data.flags, data.lightCount, data.materialCount, data.drawCount};
GL::Texture2D diffuse{NoCreate};
GL::Texture2DArray diffuseArray{NoCreate};

3
src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp

@ -197,7 +197,8 @@ const struct {
{"UBO single, ADS texture arrays + transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::TextureTransformation, 1, 1, 1},
{"UBO multi, one light", PhongGL::Flag::UniformBuffers, 1, 32, 128},
{"multidraw, one light", PhongGL::Flag::MultiDraw, 1, 32, 128},
{"multidraw, 64 lights, five used", PhongGL::Flag::MultiDraw, 64, 32, 128},
{"multidraw, one light, light culling enabled", PhongGL::Flag::MultiDraw|PhongGL::Flag::LightCulling, 1, 32, 128},
{"multidraw, 64 lights, light culling enabled, five used", PhongGL::Flag::MultiDraw|PhongGL::Flag::LightCulling, 64, 32, 128},
#endif
};

Loading…
Cancel
Save