Browse Source

[wip] Shaders: line shader.

TODO: rework this against MeshTools first
TODO: tests for edge cases (zero-size lines, angles causing infinite
  corners, ...), OTOH the defaults test doesn't have to be so complex
TODO: docs
TODO: UBOs, multidraw, instancing...
TODO: line caps...
TODO: ugh the line joints only work if the two segments are ~same
length, wtf
pull/610/head
Vladimír Vondruš 4 years ago
parent
commit
08b396b0d2
  1. 2
      src/Magnum/Shaders/CMakeLists.txt
  2. 202
      src/Magnum/Shaders/Line.frag
  3. 95
      src/Magnum/Shaders/Line.h
  4. 402
      src/Magnum/Shaders/Line.vert
  5. 417
      src/Magnum/Shaders/LineGL.cpp
  6. 281
      src/Magnum/Shaders/LineGL.h
  7. 4
      src/Magnum/Shaders/Shaders.h
  8. 31
      src/Magnum/Shaders/Test/CMakeLists.txt
  9. 754
      src/Magnum/Shaders/Test/LineGLTest.cpp
  10. 152
      src/Magnum/Shaders/Test/LineGL_Test.cpp
  11. 196
      src/Magnum/Shaders/Test/LineTest.cpp
  12. 6
      src/Magnum/Shaders/generic.glsl
  13. 6
      src/Magnum/Shaders/resources-gl.conf

2
src/Magnum/Shaders/CMakeLists.txt

@ -39,6 +39,7 @@ set(MagnumShaders_SRCS
set(MagnumShaders_GracefulAssert_SRCS set(MagnumShaders_GracefulAssert_SRCS
DistanceFieldVectorGL.cpp DistanceFieldVectorGL.cpp
FlatGL.cpp FlatGL.cpp
LineGL.cpp
MeshVisualizerGL.cpp MeshVisualizerGL.cpp
PhongGL.cpp PhongGL.cpp
VectorGL.cpp VectorGL.cpp
@ -53,6 +54,7 @@ set(MagnumShaders_HEADERS
FlatGL.h FlatGL.h
Generic.h Generic.h
GenericGL.h GenericGL.h
LineGL.h
MeshVisualizer.h MeshVisualizer.h
MeshVisualizerGL.h MeshVisualizerGL.h
Phong.h Phong.h

202
src/Magnum/Shaders/Line.frag

@ -0,0 +1,202 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#if defined(OBJECT_ID) && !defined(GL_ES) && !defined(NEW_GLSL)
#extension GL_EXT_gpu_shader4: require
#endif
#ifndef NEW_GLSL
#define fragmentColor gl_FragColor
#define in varying
#endif
#ifndef RUNTIME_CONST
#define const
#endif
/* Uniforms */
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 2)
#endif
uniform mediump float width
#ifndef GL_ES
= 1.0
#endif
;
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 3)
#endif
uniform mediump float smoothness
#ifndef GL_ES
= 0.0
#endif
;
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 4)
#endif
uniform lowp vec4 backgroundColor
#ifndef GL_ES
= vec4(0.0)
#endif
;
#ifndef UNIFORM_BUFFERS
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 5)
#endif
uniform lowp vec4 color
#ifndef GL_ES
= vec4(1.0)
#endif
;
#ifdef OBJECT_ID
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 6)
#endif
/* mediump is just 2^10, which might not be enough, this is 2^16 */
uniform highp uint objectId; /* defaults to zero */
#endif
/* Uniform buffers */
#else
#ifndef MULTI_DRAW
#if DRAW_COUNT > 1
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 0)
#endif
uniform highp uint drawOffset
#ifndef GL_ES
= 0u
#endif
;
#else
#define drawOffset 0u
#endif
#define drawId drawOffset
#endif
struct DrawUniform {
highp uvec4 materialIdReservedObjectIdReservedReserved;
#define draw_materialIdReserved materialIdReservedObjectIdReservedReserved.x
#define draw_objectId materialIdReservedObjectIdReservedReserved.y
};
layout(std140
#ifdef EXPLICIT_BINDING
, binding = 2
#endif
) uniform Draw {
DrawUniform draws[DRAW_COUNT];
};
struct MaterialUniform {
lowp vec4 color;
};
layout(std140
#ifdef EXPLICIT_BINDING
, binding = 4
#endif
) uniform Material {
MaterialUniform materials[MATERIAL_COUNT];
};
#endif
/* Inputs */
in highp vec2 centerDistanceSigned;
in highp float halfSegmentLength;
#ifdef VERTEX_COLOR
in lowp vec4 interpolatedVertexColor;
#endif
#ifdef INSTANCED_OBJECT_ID
flat in highp uint interpolatedInstanceObjectId;
#endif
/* Outputs */
#ifdef NEW_GLSL
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = COLOR_OUTPUT_ATTRIBUTE_LOCATION)
#endif
out lowp vec4 fragmentColor;
#endif
#ifdef OBJECT_ID
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION)
#endif
/* mediump is just 2^10, which might not be enough, this is 2^16 */
out highp uint fragmentObjectId;
#endif
#ifdef MULTI_DRAW
flat in highp uint drawId;
#endif
void main() {
#ifdef UNIFORM_BUFFERS
#ifdef OBJECT_ID
highp const uint objectId = draws[drawId].draw_objectId;
#endif
#if MATERIAL_COUNT > 1
mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu;
#else
#define materialId 0u
#endif
lowp const vec4 color = materials[materialId].color;
#endif
/* Pixels with `abs(centerDistanceSigned) <= [d+w,w]` are foreground,
pixels with `abs(centerDistanceSigned) > [d+w+s,w+s]` are background,
smoothstep in between */
highp const vec2 edge = vec2(halfSegmentLength+0.5*width, width*0.5);
lowp const vec2 factor = smoothstep( // TODO CSE
edge - vec2(smoothness),
edge + vec2(smoothness), abs(centerDistanceSigned));
// lowp const vec2 factor = step(edge, abs(centerDistanceSigned));
// fragmentColor.ba = vec2(0.0);
fragmentColor = mix(
#ifdef VERTEX_COLOR
interpolatedVertexColor*
#endif
color, backgroundColor, max(factor.x, factor.y)); // TODO huh*/
#ifdef OBJECT_ID
// TODO how to handle smoothness here?
fragmentObjectId = all(lessThan(abs(centerDistanceSigned), edge)) ?
#ifdef INSTANCED_OBJECT_ID
interpolatedInstanceObjectId +
#endif
objectId : 0u;
#endif
}

95
src/Magnum/Shaders/Line.h

@ -0,0 +1,95 @@
#ifndef Magnum_Shaders_Line_h
#define Magnum_Shaders_Line_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/** @file
* @brief Struct @ref Magnum::Shaders::LineDrawUniform, @ref Magnum::Shaders::LineMaterialUniform
*/
#include "Magnum/Magnum.h"
#include "Magnum/Math/Color.h"
namespace Magnum { namespace Shaders {
struct LineDrawUniform {
/** @brief Construct with default parameters */
constexpr explicit LineDrawUniform(DefaultInitT = DefaultInit) noexcept: materialId{0}, objectId{0} {}
/** @brief Construct without initializing the contents */
explicit LineDrawUniform(NoInitT) noexcept {}
LineDrawUniform& setMaterialId(UnsignedInt id) {
materialId = id;
return *this;
}
LineDrawUniform& setObjectId(UnsignedInt id) {
objectId = id;
return *this;
}
/* This field is an UnsignedInt in the shader and materialId is extracted
as (value & 0xffff), so the order has to be different on BE */
#ifndef CORRADE_TARGET_BIG_ENDIAN
alignas(4) UnsignedShort materialId;
/* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK
I MADE THOSE UNNAMED, YOU DUMB FOOL */
#ifndef DOXYGEN_GENERATING_OUTPUT
UnsignedShort:16; /* reserved for skinOffset */
#endif
#else
alignas(4) UnsignedShort:16; /* reserved for skinOffset */
UnsignedShort materialId;
#endif
UnsignedInt objectId;
/* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK
I MADE THOSE UNNAMED, YOU DUMB FOOL */
#ifndef DOXYGEN_GENERATING_OUTPUT
Int:32;
Int:32;
#endif
};
struct LineMaterialUniform {
/** @brief Construct with default parameters */
constexpr explicit LineMaterialUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f} {}
/** @brief Construct without initializing the contents */
explicit LineMaterialUniform(NoInitT) noexcept: color{NoInit} {}
LineMaterialUniform& setColor(const Color4& color) {
this->color = color;
return *this;
}
Color4 color;
};
}}
#endif

402
src/Magnum/Shaders/Line.vert

@ -0,0 +1,402 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#if defined(INSTANCED_OBJECT_ID) && !defined(GL_ES) && !defined(NEW_GLSL)
#extension GL_EXT_gpu_shader4: require
#endif
#if defined(UNIFORM_BUFFERS) && defined(TEXTURE_ARRAYS) && !defined(GL_ES)
#extension GL_ARB_shader_bit_encoding: require
#endif
#ifdef MULTI_DRAW
#ifndef GL_ES
#extension GL_ARB_shader_draw_parameters: require
#else /* covers WebGL as well */
#extension GL_ANGLE_multi_draw: require
#endif
#endif
#ifndef NEW_GLSL
#define in attribute
#define out varying
#endif
#ifndef RUNTIME_CONST
#define const
#endif
/* Uniforms */
/* This one is for both classic and UBOs, as it's usually set globally instead
of changing per-draw */
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 0)
#endif
uniform lowp vec2 viewportSize; /* defaults to zero */
#ifndef UNIFORM_BUFFERS
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 1)
#endif
#ifdef TWO_DIMENSIONS
uniform highp mat3 transformationProjectionMatrix
#ifndef GL_ES
= mat3(1.0)
#endif
;
#elif defined(THREE_DIMENSIONS)
uniform highp mat4 transformationProjectionMatrix
#ifndef GL_ES
= mat4(1.0)
#endif
;
#else
#error
#endif
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 2)
#endif
uniform mediump float width
#ifndef GL_ES
= 1.0
#endif
;
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 3)
#endif
uniform mediump float smoothness
#ifndef GL_ES
= 0.0
#endif
;
/* Uniform buffers */
#else
#if DRAW_COUNT > 1
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 1)
#endif
uniform highp uint drawOffset
#ifndef GL_ES
= 0u
#endif
;
#else
#define drawOffset 0u
#endif
layout(std140
#ifdef EXPLICIT_BINDING
, binding = 1
#endif
) uniform TransformationProjection {
highp
#ifdef TWO_DIMENSIONS
/* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for
details */
mat3x4
#elif defined(THREE_DIMENSIONS)
mat4
#else
#error
#endif
transformationProjectionMatrices[DRAW_COUNT];
};
#endif
/* Inputs */
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = POSITION_ATTRIBUTE_LOCATION)
#endif
#ifdef TWO_DIMENSIONS
in highp vec2 position;
#elif defined(THREE_DIMENSIONS)
in highp vec4 position;
#else
#error
#endif
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = LINE_DIRECTION_ATTRIBUTE_LOCATION)
#endif
#ifdef TWO_DIMENSIONS
in highp vec2 direction_;
#elif defined(THREE_DIMENSIONS)
in highp vec3 direction_;
#else
#error
#endif
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = LINE_NEIGHBOR_DIRECTION_ATTRIBUTE_LOCATION)
#endif
#ifdef TWO_DIMENSIONS
in highp vec2 neighborDirection_;
#elif defined(THREE_DIMENSIONS)
in highp vec3 neighborDirection_;
#else
#error
#endif
#ifdef VERTEX_COLOR
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = COLOR_ATTRIBUTE_LOCATION)
#endif
in lowp vec4 vertexColor;
#endif
#ifdef INSTANCED_OBJECT_ID
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = OBJECT_ID_ATTRIBUTE_LOCATION)
#endif
in highp uint instanceObjectId;
#endif
#ifdef INSTANCED_TRANSFORMATION
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = TRANSFORMATION_MATRIX_ATTRIBUTE_LOCATION)
#endif
#ifdef TWO_DIMENSIONS
in highp mat3 instancedTransformationMatrix;
#elif defined(THREE_DIMENSIONS)
in highp mat4 instancedTransformationMatrix;
#else
#error
#endif
#endif
/* Outputs */
out highp vec2 centerDistanceSigned;
out highp float halfSegmentLength;
#ifdef VERTEX_COLOR
out lowp vec4 interpolatedVertexColor;
#endif
#ifdef INSTANCED_OBJECT_ID
flat out highp uint interpolatedInstanceObjectId;
#endif
#ifdef MULTI_DRAW
flat out highp uint drawId;
#endif
/* Same as Math::Vector2::perpendicular() */
vec2 perpendicular(vec2 a) {
return vec2(-a.y, a.x);
}
void main() {
#ifdef UNIFORM_BUFFERS
#ifdef MULTI_DRAW
drawId = drawOffset + uint(
#ifndef GL_ES
gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */
#else
gl_DrawID
#endif
);
#else
#define drawId drawOffset
#endif
#ifdef TWO_DIMENSIONS
highp const mat3 transformationProjectionMatrix = mat3(transformationProjectionMatrices[drawId]);
#elif defined(THREE_DIMENSIONS)
highp const mat4 transformationProjectionMatrix = transformationProjectionMatrices[drawId];
#else
#error
#endif
#endif
#ifdef TWO_DIMENSIONS
const vec2 lineCenterPosition = (transformationProjectionMatrix*
#ifdef INSTANCED_TRANSFORMATION
instancedTransformationMatrix*
#endif
vec3(position, 1.0)).xy;
// TODO take prev/next positions, this makes no sense
const vec2 direction = (transformationProjectionMatrix*
#ifdef INSTANCED_TRANSFORMATION
instancedTransformationMatrix*
#endif
vec3(position + direction_, 1.0)).xy - lineCenterPosition;
const vec2 neighborDirection = (transformationProjectionMatrix*
#ifdef INSTANCED_TRANSFORMATION
instancedTransformationMatrix*
#endif
vec3(position + neighborDirection_, 1.0)).xy - lineCenterPosition;
const float directionLength = length(direction);
// TODO since this is used just for AA, set to a special value in case of
// a line joint that shouldn't be AAd?
halfSegmentLength = length(direction*0.5*viewportSize/2.0);
// TODO zero-sized lines better?
const vec2 directionNormalized = directionLength == 0.0 ? vec2(1.0, 0.0) : direction/directionLength;
/* Line width includes also twice the smoothness radius, some extra padding
on top, and is rounded to whole pixels. So for the edge distance we need
half of it. */
// TODO ref the paper here; actually just drop all that, smoothstep FTW
const float edgeDistance = ceil(width + 2.0*smoothness)*0.5;
/* Copied from MeshTools::generateLines() internals for completenes. The
position is always either `A` or `B` for all four quad corners, the `d`
comes in the direction attribute, `pd`/`nd` in neighborDirection and
the vertex order, which is (4n +) 0/1/2/3, in gl_VertexID.
0-d->-------2-d->
| / \
A---------B nd
| / \ v
1-d->---3-d-> \
\ \ .
nd . .
v C . */
// TODO redo all this here
/* The perpendicular direction is rotating 90° counterclockwise. Which
means for points 1 and 3 (i.e., gl_VertexID not divisible by 2) we need
to negate it to point the other way. */
// TODO zero-sized lines do what?
const float edgeSign = (gl_VertexID & 1) == 0 ? 1.0 : -1.0;
const float capSign = (gl_VertexID & 2) == 0 ? -1.0 : 1.0;
const float edgeDistanceSigned = edgeDistance*edgeSign;
// vec2 edgeDirection = perpendicular(directionNormalized)*edgeDistanceSigned*2.0/viewportSize;
// float centerDirection
vec2 edgeDirection;
/* Distance to center, passed to the fragment shader. It's chosen in a way
that interpolates to a zero vector in the quad center, and the area
where `abs(centerDirection) <= [d+w,w]` is inside the line. Inside the
line strip (where there's no line caps, shown on the left) the X value
is always 0, resulting in no antialiasing done on the beginning/end edge
in order to join tightly with the neigboring segments.
0------------------2 0-----------------------------2
[0,+w]-------------[0,+w] [-d-w,+w]------------------[+d+w,+w]
| | | | | |
[0,0] [0,0] [0,0] [-d-w,0] [0,0] [+d+w,0]
| | | | | |
[0,-w]-------------[0,-w] [-d-w,-w]------------------[+d+w,-w]
1------------------3 1-----------------------------3 */
// TODO handle caps
centerDistanceSigned.y = edgeDistanceSigned;
/* If the neighbor direction is a NaN, it means we're at the line cap --
the quad gets extended beyond A or B in the direction of the segment,
as shown with the points 0 and 1:
0--+---
| |
| A--d->--
| |
1--+---
For points 0 and 1 it'll be in the negative direction `d`, for points 2
and 3 in positive `d`. */
if(all(isnan(neighborDirection))) {
edgeDirection =
(directionNormalized*capSign +
perpendicular(directionNormalized)*edgeSign)*edgeDistance*2.0/viewportSize
;
centerDistanceSigned.x = (edgeDistance + halfSegmentLength)*capSign;
/* Otherwise we need to create a tight joint with the neighboring line
segment, as shown with the points 2 and 3. Given normalized direction
`d` and neighbor direction `nd`, `normalized(d + nd)` is the "average"
direction of the two and `perpendicular(normalized(d + nd))` gives us
the direction from B to 2:
--------+----2
| / alpha/2
w | / j
|/
--d->-B
alpha/2 / \
/ nd
/ v
----3
With `alpha` being the angle between `d` and `nd`, `alpha/2` appears in
two right triangles and the following holds, `w` being the edge distance
from above, and `j` being the length that's needed to scale `perpendicular(normalized(d + nd))` to get point 2:
|d + nd| w 2 w
sin(alpha/2) = -------- = --- --> j = --------
2 |d| j |d + nd|
Point 3 is then just in the opposite direction; for the other side it's
done equivalently. */
} else {
// edgeDirection = perpendicular(directionNormalized)*edgeSign*edgeDistance*2.0/viewportSize;
const vec2 averageDirection = capSign*directionNormalized + normalize(neighborDirection);
const float averageDirectionLength = length(averageDirection);
edgeDirection = perpendicular(averageDirection)*(capSign*edgeSign*edgeDistance*4.0/averageDirectionLength)/viewportSize;
// const float ex = sqrt((4.0*edgeDistance*edgeDistance/(averageDirectionLength*averageDirectionLength)) - edgeDistance*edgeDistance);
const float ex = sqrt(dot(edgeDirection, edgeDirection) - edgeDistance*edgeDistance);
// edgeDirection = -perpendicular(averageDirection)*(1.0*edgeDistance*edgeSign*capSign/averageDirectionLength)
// *2.0/viewportSize;
// TODO it can't be 0!! it also has to include the skew in here but
// then somehow be marked to not smooth
centerDistanceSigned.x = halfSegmentLength*capSign;// + ex*sign(dot(direction*capSign, edgeDirection));
}
// TODO cap ends / joints, depending on neighborDirection size
gl_Position.xyzw = vec4(lineCenterPosition + edgeDirection, 0.0, 1.0);
#elif defined(THREE_DIMENSIONS)
// TODO 3D, how to handle perspective? multiply edgeDistance with w?
gl_Position = transformationProjectionMatrix*
#ifdef INSTANCED_TRANSFORMATION
instancedTransformationMatrix*
#endif
position;
#else
#error
#endif
#ifdef VERTEX_COLOR
/* Vertex colors, if enabled */
interpolatedVertexColor = vertexColor;
#endif
#ifdef INSTANCED_OBJECT_ID
/* Instanced object ID, if enabled */
interpolatedInstanceObjectId = instanceObjectId;
#endif
}

417
src/Magnum/Shaders/LineGL.cpp

@ -0,0 +1,417 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "LineGL.h"
#include <Corrade/Containers/EnumSet.hpp>
#include <Corrade/Utility/Resource.h>
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Extensions.h"
#include "Magnum/GL/Shader.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Math/Matrix3.h"
#include "Magnum/Math/Matrix4.h"
#include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h"
#ifndef MAGNUM_TARGET_GLES2
#include <Corrade/Utility/FormatStl.h>
#include "Magnum/GL/Buffer.h"
#endif
namespace Magnum { namespace Shaders {
namespace {
enum: Int {
/* 0/1/2/3 taken by Phong (A/D/S/N), 4 by MeshVisualizer colormap, 5 by
object ID textures, 6 by Vector */
TextureUnit = 7
};
#ifndef MAGNUM_TARGET_GLES2
enum: Int {
/* Not using the zero binding to avoid conflicts with
ProjectionBufferBinding from other shaders which can likely stay
bound to the same buffer for the whole time */
TransformationProjectionBufferBinding = 1,
DrawBufferBinding = 2,
MaterialBufferBinding = 3
};
#endif
}
template<UnsignedInt dimensions> typename LineGL<dimensions>::CompileState LineGL<dimensions>::compile(const Configuration& configuration) {
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(),
"Shaders::LineGL: material count can't be zero", CompileState{NoCreate});
CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(),
"Shaders::LineGL: draw count can't be zero", CompileState{NoCreate});
#endif
#ifndef MAGNUM_TARGET_GLES
if(configuration.flags() >= Flag::UniformBuffers)
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object);
#endif
#ifndef MAGNUM_TARGET_GLES2
if(configuration.flags() >= Flag::MultiDraw) {
#ifndef MAGNUM_TARGET_GLES
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters);
#elif !defined(MAGNUM_TARGET_WEBGL)
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw);
#else
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw);
#endif
}
#endif
#ifdef MAGNUM_BUILD_STATIC
/* Import resources on static build, if not already */
if(!Utility::Resource::hasGroup("MagnumShadersGL"))
importShaderResources();
#endif
Utility::Resource rs("MagnumShadersGL");
const GL::Context& context = GL::Context::current();
const GL::Version version = context.supportedVersion({
#ifndef MAGNUM_TARGET_GLES
GL::Version::GL320, GL::Version::GL310, GL::Version::GL300, GL::Version::GL210
#else
GL::Version::GLES300, GL::Version::GLES200
#endif
});
GL::Shader vert = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Vertex);
GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment);
vert.addSource(configuration.flags() & Flag::VertexColor ? "#define VERTEX_COLOR\n" : "")
.addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n")
#ifndef MAGNUM_TARGET_GLES2
.addSource(configuration.flags() >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "")
#endif
.addSource(configuration.flags() & Flag::InstancedTransformation ? "#define INSTANCED_TRANSFORMATION\n" : "");
#ifndef MAGNUM_TARGET_GLES2
if(configuration.flags() >= Flag::UniformBuffers) {
vert.addSource(Utility::formatString(
"#define UNIFORM_BUFFERS\n"
"#define DRAW_COUNT {}\n",
configuration.drawCount()));
vert.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : "");
}
#endif
vert.addSource(rs.getString("generic.glsl"))
.addSource(rs.getString("Line.vert"));
frag.addSource(configuration.flags() & Flag::VertexColor ? "#define VERTEX_COLOR\n" : "")
#ifndef MAGNUM_TARGET_GLES2
.addSource(configuration.flags() & Flag::ObjectId ? "#define OBJECT_ID\n" : "")
.addSource(configuration.flags() >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "")
#endif
;
#ifndef MAGNUM_TARGET_GLES2
if(configuration.flags() >= Flag::UniformBuffers) {
frag.addSource(Utility::formatString(
"#define UNIFORM_BUFFERS\n"
"#define DRAW_COUNT {}\n"
"#define MATERIAL_COUNT {}\n",
configuration.drawCount(),
configuration.materialCount()));
frag.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : "");
}
#endif
frag.addSource(rs.getString("generic.glsl"))
.addSource(rs.getString("Line.frag"));
vert.submitCompile();
frag.submitCompile();
LineGL<dimensions> out{NoInit};
out._flags = configuration.flags();
#ifndef MAGNUM_TARGET_GLES2
out._materialCount = configuration.materialCount();
out._drawCount = configuration.drawCount();
#endif
out.attachShaders({vert, frag});
/* ES3 has this done in the shader directly and doesn't even provide
bindFragmentDataLocation() */
#if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2)
#ifndef MAGNUM_TARGET_GLES
if(!context.isExtensionSupported<GL::Extensions::ARB::explicit_attrib_location>(version))
#endif
{
out.bindAttributeLocation(Position::Location, "position");
out.bindAttributeLocation(LineDirection::Location, "direction");
out.bindAttributeLocation(LineNeighborDirection::Location, "neighborDirection");
if(configuration.flags() & Flag::VertexColor)
out.bindAttributeLocation(Color3::Location, "vertexColor"); /* Color4 is the same */
#ifndef MAGNUM_TARGET_GLES2
if(configuration.flags() & Flag::ObjectId) {
out.bindFragmentDataLocation(ColorOutput, "color");
out.bindFragmentDataLocation(ObjectIdOutput, "objectId");
}
if(configuration.flags() >= Flag::InstancedObjectId)
out.bindAttributeLocation(ObjectId::Location, "instanceObjectId");
#endif
if(configuration.flags() & Flag::InstancedTransformation)
out.bindAttributeLocation(TransformationMatrix::Location, "instancedTransformationMatrix");
}
#endif
out.submitLink();
return CompileState{std::move(out), std::move(vert), std::move(frag), version};
}
template<UnsignedInt dimensions> LineGL<dimensions>::LineGL(CompileState&& state): LineGL{static_cast<LineGL&&>(std::move(state))} {
#ifdef CORRADE_GRACEFUL_ASSERT
/* When graceful assertions fire from within compile(), we get a NoCreate'd
CompileState. Exiting makes it possible to test the assert. */
if(!id()) return;
#endif
CORRADE_INTERNAL_ASSERT_OUTPUT(checkLink({GL::Shader(state._vert), GL::Shader(state._frag)}));
const GL::Context& context = GL::Context::current();
const GL::Version version = state._version;
#ifndef MAGNUM_TARGET_GLES
if(!context.isExtensionSupported<GL::Extensions::ARB::explicit_uniform_location>(version))
#endif
{
_viewportSizeUniform = uniformLocation("viewportSize");
#ifndef MAGNUM_TARGET_GLES2
if(_flags >= Flag::UniformBuffers) {
if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset");
} else
#endif
{
_transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix");
_widthUniform = uniformLocation("width");
_smoothnessUniform = uniformLocation("smoothness");
_backgroundColorUniform = uniformLocation("backgroundColor");
_colorUniform = uniformLocation("color");
#ifndef MAGNUM_TARGET_GLES2
if(_flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId");
#endif
}
}
#ifndef MAGNUM_TARGET_GLES
if(!context.isExtensionSupported<GL::Extensions::ARB::shading_language_420pack>(version))
#endif
{
#ifndef MAGNUM_TARGET_GLES2
if(_flags >= Flag::UniformBuffers) {
setUniformBlockBinding(uniformBlockIndex("TransformationProjection"), TransformationProjectionBufferBinding);
setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding);
setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding);
}
#endif
}
/* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */
#ifdef MAGNUM_TARGET_GLES
#ifndef MAGNUM_TARGET_GLES2
if(_flags >= Flag::UniformBuffers) {
/* Draw offset is zero by default */
} else
#endif
{
setTransformationProjectionMatrix(MatrixTypeFor<dimensions, Float>{Math::IdentityInit});
setColor(Magnum::Color4{1.0f});
/* Object ID is zero by default */
}
#endif
static_cast<void>(version);
static_cast<void>(context);
}
template<UnsignedInt dimensions> LineGL<dimensions>::LineGL(const Configuration& configuration): LineGL{compile(configuration)} {}
template<UnsignedInt dimensions> LineGL<dimensions>::LineGL(NoInitT) {}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setTransformationProjectionMatrix(const MatrixTypeFor<dimensions, Float>& matrix) {
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::LineGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled", *this);
#endif
setUniform(_transformationProjectionMatrixUniform, matrix);
return *this;
}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setViewportSize(const Vector2& size) {
setUniform(_viewportSizeUniform, size);
return *this;
}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setWidth(const Float width) {
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled", *this);
#endif
setUniform(_widthUniform, width);
return *this;
}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setSmoothness(const Float smoothness) {
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled", *this);
#endif
setUniform(_smoothnessUniform, smoothness);
return *this;
}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setBackgroundColor(const Magnum::Color4& color) {
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled", *this);
#endif
setUniform(_backgroundColorUniform, color);
return *this;
}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setColor(const Magnum::Color4& color) {
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled", *this);
#endif
setUniform(_colorUniform, color);
return *this;
}
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setObjectId(UnsignedInt id) {
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::LineGL::setObjectId(): the shader was created with uniform buffers enabled", *this);
CORRADE_ASSERT(_flags & Flag::ObjectId,
"Shaders::LineGL::setObjectId(): the shader was not created with object ID enabled", *this);
setUniform(_objectIdUniform, id);
return *this;
}
#endif
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setDrawOffset(const UnsignedInt offset) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
"Shaders::LineGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this);
CORRADE_ASSERT(offset < _drawCount,
"Shaders::LineGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this);
if(_drawCount > 1) setUniform(_drawOffsetUniform, offset);
return *this;
}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindTransformationProjectionBuffer(GL::Buffer& buffer) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
"Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding);
return *this;
}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
"Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size);
return *this;
}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindDrawBuffer(GL::Buffer& buffer) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
"Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding);
return *this;
}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
"Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size);
return *this;
}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindMaterialBuffer(GL::Buffer& buffer) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
"Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding);
return *this;
}
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
"Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size);
return *this;
}
#endif
template class MAGNUM_SHADERS_EXPORT LineGL<2>;
template class MAGNUM_SHADERS_EXPORT LineGL<3>;
namespace Implementation {
Debug& operator<<(Debug& debug, const LineGLFlag value) {
debug << "Shaders::LineGL::Flag" << Debug::nospace;
switch(value) {
/* LCOV_EXCL_START */
#define _c(v) case LineGLFlag::v: return debug << "::" #v;
_c(VertexColor)
#ifndef MAGNUM_TARGET_GLES2
_c(ObjectId)
_c(InstancedObjectId)
#endif
_c(InstancedTransformation)
#ifndef MAGNUM_TARGET_GLES2
_c(UniformBuffers)
_c(MultiDraw)
#endif
#undef _c
/* LCOV_EXCL_STOP */
}
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedShort(value)) << Debug::nospace << ")";
}
Debug& operator<<(Debug& debug, const LineGLFlags value) {
return Containers::enumSetDebugOutput(debug, value, "Shaders::LineGL::Flags{}", {
LineGLFlag::VertexColor,
#ifndef MAGNUM_TARGET_GLES2
LineGLFlag::InstancedObjectId, /* Superset of ObjectId */
LineGLFlag::ObjectId,
#endif
LineGLFlag::InstancedTransformation,
#ifndef MAGNUM_TARGET_GLES2
LineGLFlag::MultiDraw, /* Superset of UniformBuffers */
LineGLFlag::UniformBuffers
#endif
});
}
}
}}

281
src/Magnum/Shaders/LineGL.h

@ -0,0 +1,281 @@
#ifndef Magnum_Shaders_LineGL_h
#define Magnum_Shaders_LineGL_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "Magnum/DimensionTraits.h"
#include "Magnum/GL/AbstractShaderProgram.h"
#include "Magnum/Shaders/GenericGL.h"
#include "Magnum/Shaders/glShaderWrapper.h"
#include "Magnum/Shaders/visibility.h"
namespace Magnum { namespace Shaders {
namespace Implementation {
enum class LineGLFlag: UnsignedShort {
VertexColor = 1 << 0,
#ifndef MAGNUM_TARGET_GLES2
ObjectId = 1 << 1,
InstancedObjectId = (1 << 2)|ObjectId,
#endif
InstancedTransformation = 1 << 3,
#ifndef MAGNUM_TARGET_GLES2
UniformBuffers = 1 << 4,
MultiDraw = UniformBuffers|(1 << 5)
#endif
};
typedef Containers::EnumSet<LineGLFlag> LineGLFlags;
}
template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT LineGL: public GL::AbstractShaderProgram {
public:
class Configuration;
class CompileState;
typedef typename GenericGL<dimensions>::Position Position;
// TODO move to Generic
typedef GL::Attribute<3, VectorTypeFor<dimensions, Float>> LineDirection;
typedef GL::Attribute<5, VectorTypeFor<dimensions, Float>> LineNeighborDirection;
typedef typename GenericGL<dimensions>::Color3 Color3;
typedef typename GenericGL<dimensions>::Color4 Color4;
#ifndef MAGNUM_TARGET_GLES2
typedef typename GenericGL<dimensions>::ObjectId ObjectId;
#endif
typedef typename GenericGL<dimensions>::TransformationMatrix TransformationMatrix;
enum: UnsignedInt {
ColorOutput = GenericGL<dimensions>::ColorOutput,
#ifndef MAGNUM_TARGET_GLES2
ObjectIdOutput = GenericGL<dimensions>::ObjectIdOutput
#endif
};
#ifdef DOXYGEN_GENERATING_OUTPUT
/**
* @brief Flag
*
* @see @ref Flags, @ref flags()
*/
enum class Flag: UnsignedShort {
VertexColor = 1 << 0,
#ifndef MAGNUM_TARGET_GLES2
ObjectId = 1 << 1,
InstancedObjectId = (1 << 2)|ObjectId,
#endif
InstancedTransformation = 1 << 3,
#ifndef MAGNUM_TARGET_GLES2
UniformBuffers = 1 << 4,
MultiDraw = UniformBuffers|(1 << 5)
#endif
};
/**
* @brief Flags
*
* @see @ref flags()
*/
typedef Containers::EnumSet<Flag> Flags;
#else
/* Done this way to be prepared for possible future diversion of 2D
and 3D flags (e.g. introducing 3D-specific features) */
typedef Implementation::LineGLFlag Flag;
typedef Implementation::LineGLFlags Flags;
#endif
static CompileState compile(const Configuration& configuration = Configuration{});
explicit LineGL(const Configuration& configuration = Configuration{});
explicit LineGL(CompileState&& state);
explicit LineGL(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {}
/** @brief Copying is not allowed */
LineGL(const LineGL<dimensions>&) = delete;
/** @brief Move constructor */
LineGL(LineGL<dimensions>&&) noexcept = default;
/** @brief Copying is not allowed */
LineGL<dimensions>& operator=(const LineGL<dimensions>&) = delete;
/** @brief Move assignment */
LineGL<dimensions>& operator=(LineGL<dimensions>&&) noexcept = default;
/** @brief Flags */
Flags flags() const { return _flags; }
#ifndef MAGNUM_TARGET_GLES2
UnsignedInt materialCount() const { return _materialCount; }
UnsignedInt drawCount() const { return _drawCount; }
#endif
// TODO split projection for 3D?
LineGL<dimensions>& setTransformationProjectionMatrix(const MatrixTypeFor<dimensions, Float>& matrix);
LineGL<dimensions>& setViewportSize(const Vector2& size);
LineGL<dimensions>& setWidth(Float width);
LineGL<dimensions>& setSmoothness(Float smoothness);
LineGL<dimensions>& setBackgroundColor(const Magnum::Color4& color);
LineGL<dimensions>& setColor(const Magnum::Color4& color);
#ifndef MAGNUM_TARGET_GLES2
LineGL<dimensions>& setObjectId(UnsignedInt id);
#endif
#ifndef MAGNUM_TARGET_GLES2
LineGL<dimensions>& setDrawOffset(UnsignedInt offset);
LineGL<dimensions>& bindTransformationProjectionBuffer(GL::Buffer& buffer);
LineGL<dimensions>& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size);
LineGL<dimensions>& bindDrawBuffer(GL::Buffer& buffer);
LineGL<dimensions>& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size);
LineGL<dimensions>& bindMaterialBuffer(GL::Buffer& buffer);
LineGL<dimensions>& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size);
#endif
// TODO draw() overloads, as a macro in AbstractShaderProgram
private:
/* Creates the GL shader program object but does nothing else.
Internal, used by compile(). */
explicit LineGL(NoInitT);
Flags _flags;
#ifndef MAGNUM_TARGET_GLES2
UnsignedInt _materialCount{}, _drawCount{};
#endif
Int _viewportSizeUniform{0},
_transformationProjectionMatrixUniform{1},
_widthUniform{2},
_smoothnessUniform{3},
_backgroundColorUniform{4},
_colorUniform{5};
#ifndef MAGNUM_TARGET_GLES2
Int _objectIdUniform{6};
/* Used instead of all other uniforms except viewportSize when
Flag::UniformBuffers is set, so it can alias them */
Int _drawOffsetUniform{1};
#endif
};
/**
@brief Configuration
*/
template<UnsignedInt dimensions> class LineGL<dimensions>::Configuration {
public:
explicit Configuration() = default;
Configuration& setFlags(Flags flags) {
_flags = flags;
return *this;
}
Flags flags() const { return _flags; }
#ifndef MAGNUM_TARGET_GLES2
Configuration& setMaterialCount(UnsignedInt count) {
_materialCount = count;
return *this;
}
UnsignedInt materialCount() const { return _materialCount; }
Configuration& setDrawCount(UnsignedInt count) {
_drawCount = count;
return *this;
}
UnsignedInt drawCount() const { return _drawCount; }
#endif
private:
Flags _flags;
#ifndef MAGNUM_TARGET_GLES2
UnsignedInt _materialCount{1};
UnsignedInt _drawCount{1};
#endif
};
/**
@brief Asynchronous compilation state
Returned by @ref compile(). See @ref shaders-async for more information.
*/
template<UnsignedInt dimensions> class LineGL<dimensions>::CompileState: public LineGL<dimensions> {
/* Everything deliberately private except for the inheritance */
friend class LineGL;
explicit CompileState(NoCreateT): LineGL{NoCreate}, _vert{NoCreate}, _frag{NoCreate} {}
explicit CompileState(LineGL<dimensions>&& shader, GL::Shader&& vert, GL::Shader&& frag, GL::Version version): LineGL<dimensions>{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)}, _version{version} {}
Implementation::GLShaderWrapper _vert, _frag;
GL::Version _version;
};
/**
@brief 2D line OpenGL shader
@m_since_latest
*/
typedef LineGL<2> LineGL2D;
/**
@brief 3D LineGL OpenGL shader
@m_since_latest
*/
typedef LineGL<3> LineGL3D;
#ifdef DOXYGEN_GENERATING_OUTPUT
/** @debugoperatorclassenum{LineGL,LineGL::Flag} */
template<UnsignedInt dimensions> Debug& operator<<(Debug& debug, LineGL<dimensions>::Flag value);
/** @debugoperatorclassenum{LineGL,LineGL::Flags} */
template<UnsignedInt dimensions> Debug& operator<<(Debug& debug, LineGL<dimensions>::Flags value);
#else
namespace Implementation {
MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, LineGLFlag value);
MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, LineGLFlags value);
CORRADE_ENUMSET_OPERATORS(LineGLFlags)
}
#endif
}}
#endif

4
src/Magnum/Shaders/Shaders.h

@ -58,6 +58,10 @@ typedef CORRADE_DEPRECATED("use FlatGL3D instead") FlatGL3D Flat3D;
/* Generic is used only statically */ /* Generic is used only statically */
template<UnsignedInt> class LineGL;
typedef LineGL<2> LineGL2D;
typedef LineGL<3> LineGL3D;
class MeshVisualizerGL2D; class MeshVisualizerGL2D;
class MeshVisualizerGL3D; class MeshVisualizerGL3D;
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED

31
src/Magnum/Shaders/Test/CMakeLists.txt

@ -30,6 +30,7 @@ set(CMAKE_FOLDER "Magnum/Shaders/Test")
corrade_add_test(ShadersDistanceFieldVectorTest DistanceFieldVectorTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersDistanceFieldVectorTest DistanceFieldVectorTest.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersFlatTest FlatTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersFlatTest FlatTest.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersGenericTest GenericTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersGenericTest GenericTest.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersLineTest LineTest.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersGLShaderWrapperTest GLShaderWrapperTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersGLShaderWrapperTest GLShaderWrapperTest.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersMeshVisualizerTest MeshVisualizerTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersMeshVisualizerTest MeshVisualizerTest.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersPhongTest PhongTest.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersPhongTest PhongTest.cpp LIBRARIES MagnumShaders)
@ -40,6 +41,7 @@ corrade_add_test(ShadersVectorTest VectorTest.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersDistanceFieldVectorGL_Test DistanceFieldVectorGL_Test.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersDistanceFieldVectorGL_Test DistanceFieldVectorGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersFlatGL_Test FlatGL_Test.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersFlatGL_Test FlatGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersGenericGL_Test GenericGL_Test.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersGenericGL_Test GenericGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersLineGL_Test LineGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersMeshVisualizerGL_Test MeshVisualizerGL_Test.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersMeshVisualizerGL_Test MeshVisualizerGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersPhongGL_Test PhongGL_Test.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersPhongGL_Test PhongGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersVectorGL_Test VectorGL_Test.cpp LIBRARIES MagnumShaders) corrade_add_test(ShadersVectorGL_Test VectorGL_Test.cpp LIBRARIES MagnumShaders)
@ -165,6 +167,35 @@ if(MAGNUM_BUILD_GL_TESTS)
endif() endif()
endif() endif()
set(ShadersLineGLTest_SRCS LineGLTest.cpp)
if(CORRADE_TARGET_IOS)
list(APPEND ShadersLineGLTest_SRCS TestFiles LineTestFiles)
endif()
corrade_add_test(ShadersLineGLTest ${ShadersLineGLTest_SRCS}
LIBRARIES
MagnumDebugTools
MagnumMeshTools
MagnumPrimitives
MagnumShadersTestLib
MagnumOpenGLTester)
target_include_directories(ShadersLineGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)
if(MAGNUM_BUILD_PLUGINS_STATIC)
if(MAGNUM_WITH_ANYIMAGEIMPORTER)
target_link_libraries(ShadersLineGLTest PRIVATE AnyImageImporter)
endif()
if(MAGNUM_WITH_TGAIMPORTER)
target_link_libraries(ShadersLineGLTest PRIVATE TgaImporter)
endif()
else()
# So the plugins get properly built when building the test
if(MAGNUM_WITH_ANYIMAGEIMPORTER)
add_dependencies(ShadersLineGLTest AnyImageImporter)
endif()
if(MAGNUM_WITH_TGAIMPORTER)
add_dependencies(ShadersLineGLTest TgaImporter)
endif()
endif()
corrade_add_test(ShadersGLShaderWrapperGLTest GLShaderWrapperGLTest.cpp LIBRARIES MagnumShaders MagnumOpenGLTester) corrade_add_test(ShadersGLShaderWrapperGLTest GLShaderWrapperGLTest.cpp LIBRARIES MagnumShaders MagnumOpenGLTester)
set(ShadersMeshVisualizerGLTest_SRCS MeshVisualizerGLTest.cpp) set(ShadersMeshVisualizerGLTest_SRCS MeshVisualizerGLTest.cpp)

754
src/Magnum/Shaders/Test/LineGLTest.cpp

@ -0,0 +1,754 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/PluginManager/Manager.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/FormatStl.h>
#include <Corrade/Utility/Path.h>
#include <Corrade/Utility/System.h>
#include "Magnum/Image.h"
#include "Magnum/PixelFormat.h"
#include "Magnum/DebugTools/CompareImage.h"
#include "Magnum/GL/Buffer.h"
#include "Magnum/GL/Extensions.h"
#include "Magnum/GL/Framebuffer.h"
#include "Magnum/GL/Mesh.h"
#include "Magnum/GL/OpenGLTester.h"
#include "Magnum/GL/Renderbuffer.h"
#include "Magnum/GL/RenderbufferFormat.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Math/Matrix3.h"
#include "Magnum/Math/Matrix4.h"
#include "Magnum/Shaders/Generic.h"
#include "Magnum/Shaders/Line.h"
#include "Magnum/Shaders/LineGL.h"
#include "Magnum/Trade/AbstractImporter.h"
#include "configure.h"
namespace Magnum { namespace Shaders { namespace Test { namespace {
struct LineGLTest: GL::OpenGLTester {
explicit LineGLTest();
template<UnsignedInt dimensions> void construct();
template<UnsignedInt dimensions> void constructAsync();
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void constructUniformBuffers();
template<UnsignedInt dimensions> void constructUniformBuffersAsync();
#endif
template<UnsignedInt dimensions> void constructMove();
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void constructMoveUniformBuffers();
#endif
// template<UnsignedInt dimensions> void constructInvalid();
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void constructUniformBuffersInvalid();
#endif
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void setUniformUniformBuffersEnabled();
template<UnsignedInt dimensions> void bindBufferUniformBuffersNotEnabled();
#endif
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void setObjectIdNotEnabled();
#endif
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void setWrongDrawOffset();
#endif
void renderSetup();
void renderTeardown();
template<LineGL2D::Flag flag = LineGL2D::Flag{}> void renderDefaults2D();
template<LineGL3D::Flag flag = LineGL3D::Flag{}> void renderDefaults3D();
template<LineGL2D::Flag flag = LineGL2D::Flag{}> void render2D();
template<LineGL3D::Flag flag = LineGL3D::Flag{}> void render3D();
template<class T, LineGL2D::Flag flag = LineGL2D::Flag{}> void renderVertexColor2D();
template<class T, LineGL3D::Flag flag = LineGL3D::Flag{}> void renderVertexColor3D();
#ifndef MAGNUM_TARGET_GLES2
void renderObjectIdSetup();
void renderObjectIdTeardown();
template<LineGL2D::Flag flag = LineGL2D::Flag{}> void renderObjectId2D();
template<LineGL3D::Flag flag = LineGL3D::Flag{}> void renderObjectId3D();
#endif
template<LineGL2D::Flag flag = LineGL2D::Flag{}> void renderInstanced2D();
template<LineGL3D::Flag flag = LineGL3D::Flag{}> void renderInstanced3D();
#ifndef MAGNUM_TARGET_GLES2
void renderMulti2D();
void renderMulti3D();
#endif
private:
PluginManager::Manager<Trade::AbstractImporter> _manager{"nonexistent"};
Containers::String _testDir;
GL::Renderbuffer _color{NoCreate};
#ifndef MAGNUM_TARGET_GLES2
GL::Renderbuffer _objectId{NoCreate};
#endif
GL::Framebuffer _framebuffer{NoCreate};
};
using namespace Math::Literals;
const struct {
const char* name;
LineGL2D::Flags flags;
} ConstructData[]{
{"", {}},
{"vertex colors", LineGL2D::Flag::VertexColor},
#ifndef MAGNUM_TARGET_GLES2
{"object ID", LineGL2D::Flag::ObjectId},
{"instanced object ID", LineGL2D::Flag::InstancedObjectId},
#endif
{"instanced transformation", LineGL2D::Flag::InstancedTransformation},
};
#ifndef MAGNUM_TARGET_GLES2
const struct {
const char* name;
LineGL2D::Flags flags;
UnsignedInt materialCount, drawCount;
} ConstructUniformBuffersData[]{
{"classic fallback", {}, 1, 1},
{"", LineGL2D::Flag::UniformBuffers, 1, 1},
/* SwiftShader has 256 uniform vectors at most, per-draw is 4+1 in 3D case
and 3+1 in 2D, per-material 1 */
{"multiple materials, draws", LineGL2D::Flag::UniformBuffers, 16, 48},
{"object ID", LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::ObjectId, 1, 1},
{"instanced object ID", LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::InstancedObjectId, 1, 1},
{"multidraw with all the things", LineGL2D::Flag::MultiDraw|LineGL2D::Flag::ObjectId|LineGL2D::Flag::InstancedTransformation|LineGL2D::Flag::InstancedObjectId, 16, 48}
};
#endif
// const struct {
// const char* name;
// LineGL2D::Flags flags;
// const char* message;
// } ConstructInvalidData[]{
// };
#ifndef MAGNUM_TARGET_GLES2
constexpr struct {
const char* name;
LineGL2D::Flags flags;
UnsignedInt materialCount, drawCount;
const char* message;
} ConstructUniformBuffersInvalidData[]{
{"zero draws", LineGL2D::Flag::UniformBuffers, 1, 0,
"draw count can't be zero"},
{"zero materials", LineGL2D::Flag::UniformBuffers, 0, 1,
"material count can't be zero"}
};
#endif
LineGLTest::LineGLTest() {
addInstancedTests<LineGLTest>({
&LineGLTest::construct<2>,
// &LineGLTest::construct<3>
},
Containers::arraySize(ConstructData));
addTests<LineGLTest>({
&LineGLTest::constructAsync<2>,
// &LineGLTest::constructAsync<3>
});
#ifndef MAGNUM_TARGET_GLES2
// addInstancedTests<LineGLTest>({
// &LineGLTest::constructUniformBuffers<2>,
// &LineGLTest::constructUniformBuffers<3>},
// Containers::arraySize(ConstructUniformBuffersData));
//
// addTests<LineGLTest>({
// &LineGLTest::constructUniformBuffersAsync<2>,
// &LineGLTest::constructUniformBuffersAsync<3>});
#endif
addTests<LineGLTest>({
&LineGLTest::constructMove<2>,
// &LineGLTest::constructMove<3>,
// #ifndef MAGNUM_TARGET_GLES2
// &LineGLTest::constructMoveUniformBuffers<2>,
// &LineGLTest::constructMoveUniformBuffers<3>,
// #endif
});
// addInstancedTests<LineGLTest>({
// &LineGLTest::constructInvalid<2>,
// &LineGLTest::constructInvalid<3>},
// Containers::arraySize(ConstructInvalidData));
#ifndef MAGNUM_TARGET_GLES2
addInstancedTests<LineGLTest>({
&LineGLTest::constructUniformBuffersInvalid<2>,
// &LineGLTest::constructUniformBuffersInvalid<3>
},
Containers::arraySize(ConstructUniformBuffersInvalidData));
#endif
#ifndef MAGNUM_TARGET_GLES2
addTests<LineGLTest>({
// &LineGLTest::setUniformUniformBuffersEnabled<2>,
// &LineGLTest::setUniformUniformBuffersEnabled<3>,
&LineGLTest::bindBufferUniformBuffersNotEnabled<2>,
// &LineGLTest::bindBufferUniformBuffersNotEnabled<3>,
&LineGLTest::setObjectIdNotEnabled<2>,
// &LineGLTest::setObjectIdNotEnabled<3>,
// &LineGLTest::setWrongDrawOffset<2>,
// &LineGLTest::setWrongDrawOffset<3>
});
#endif
/* MSVC needs explicit type due to default template args */
addTests<LineGLTest>({
&LineGLTest::renderDefaults2D,
#ifndef MAGNUM_TARGET_GLES2
// &LineGLTest::renderDefaults2D<LineGL2D::Flag::UniformBuffers>,
#endif
// &LineGLTest::renderDefaults3D,
// #ifndef MAGNUM_TARGET_GLES2
// &LineGLTest::renderDefaults3D<LineGL3D::Flag::UniformBuffers>,
// #endif
},
&LineGLTest::renderSetup,
&LineGLTest::renderTeardown);
/* Load the plugins directly from the build tree. Otherwise they're either
static and already loaded or not present in the build tree */
#ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME
CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded);
#endif
#ifdef TGAIMPORTER_PLUGIN_FILENAME
CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded);
#endif
}
template<UnsignedInt dimensions> void LineGLTest::construct() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
auto&& data = ConstructData[testCaseInstanceId()];
setTestCaseDescription(data.name);
#ifndef MAGNUM_TARGET_GLES
if((data.flags & LineGL2D::Flag::ObjectId) && !GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
#endif
LineGL<dimensions> shader{typename LineGL<dimensions>::Configuration{}
.setFlags(data.flags)};
CORRADE_COMPARE(shader.flags(), data.flags);
CORRADE_VERIFY(shader.id());
{
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
#endif
CORRADE_VERIFY(shader.validate().first);
}
MAGNUM_VERIFY_NO_GL_ERROR();
}
template<UnsignedInt dimensions> void LineGLTest::constructAsync() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
typename LineGL<dimensions>::CompileState state = LineGL<dimensions>::compile(typename LineGL<dimensions>::Configuration{}
.setFlags(LineGL2D::Flag::VertexColor));
CORRADE_COMPARE(state.flags(), LineGL2D::Flag::VertexColor);
while(!state.isLinkFinished())
Utility::System::sleep(100);
LineGL<dimensions> shader{std::move(state)};
CORRADE_COMPARE(shader.flags(), LineGL2D::Flag::VertexColor);
CORRADE_VERIFY(shader.id());
{
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
#endif
CORRADE_VERIFY(shader.validate().first);
}
MAGNUM_VERIFY_NO_GL_ERROR();
}
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void LineGLTest::constructUniformBuffers() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
auto&& data = ConstructUniformBuffersData[testCaseInstanceId()];
setTestCaseDescription(data.name);
#ifndef MAGNUM_TARGET_GLES
if((data.flags & LineGL2D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
if((data.flags & LineGL2D::Flag::ObjectId) && !GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
#endif
if(data.flags >= LineGL2D::Flag::MultiDraw) {
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::shader_draw_parameters>())
CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported.");
#elif !defined(MAGNUM_TARGET_WEBGL)
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ANGLE::multi_draw>())
CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported.");
#else
if(!GL::Context::current().isExtensionSupported<GL::Extensions::WEBGL::multi_draw>())
CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported.");
#endif
}
LineGL<dimensions> shader{typename LineGL<dimensions>::Configuration{}
.setFlags(data.flags)
.setMaterialCount(data.materialCount)
.setDrawCount(data.drawCount)};
CORRADE_COMPARE(shader.flags(), data.flags);
CORRADE_COMPARE(shader.materialCount(), data.materialCount);
CORRADE_COMPARE(shader.drawCount(), data.drawCount);
CORRADE_VERIFY(shader.id());
{
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
#endif
CORRADE_VERIFY(shader.validate().first);
}
MAGNUM_VERIFY_NO_GL_ERROR();
}
template<UnsignedInt dimensions> void LineGLTest::constructUniformBuffersAsync() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
#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
typename LineGL<dimensions>::CompileState state = LineGL<dimensions>::compile(typename LineGL<dimensions>::Configuration{}
.setFlags(LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::VertexColor)
.setMaterialCount(16)
.setDrawCount(48));
CORRADE_COMPARE(state.flags(), LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::VertexColor);
CORRADE_COMPARE(state.materialCount(), 16);
CORRADE_COMPARE(state.drawCount(), 48);
while(!state.isLinkFinished())
Utility::System::sleep(100);
LineGL<dimensions> shader{std::move(state)};
CORRADE_COMPARE(shader.flags(), LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::VertexColor);
CORRADE_COMPARE(shader.materialCount(), 16);
CORRADE_COMPARE(shader.drawCount(), 48);
CORRADE_VERIFY(shader.id());
{
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
#endif
CORRADE_VERIFY(shader.validate().first);
}
MAGNUM_VERIFY_NO_GL_ERROR();
}
#endif
template<UnsignedInt dimensions> void LineGLTest::constructMove() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
LineGL<dimensions> a{typename LineGL<dimensions>::Configuration{}
.setFlags(LineGL<dimensions>::Flag::VertexColor)};
const GLuint id = a.id();
CORRADE_VERIFY(id);
MAGNUM_VERIFY_NO_GL_ERROR();
LineGL<dimensions> b{std::move(a)};
CORRADE_COMPARE(b.id(), id);
CORRADE_COMPARE(b.flags(), LineGL<dimensions>::Flag::VertexColor);
CORRADE_VERIFY(!a.id());
LineGL<dimensions> c{NoCreate};
c = std::move(b);
CORRADE_COMPARE(c.id(), id);
CORRADE_COMPARE(c.flags(), LineGL<dimensions>::Flag::VertexColor);
CORRADE_VERIFY(!b.id());
}
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void LineGLTest::constructMoveUniformBuffers() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
#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
LineGL<dimensions> a{typename LineGL<dimensions>::Configuration{}
.setFlags(LineGL<dimensions>::Flag::UniformBuffers)
.setMaterialCount(2)
.setDrawCount(5)};
const GLuint id = a.id();
CORRADE_VERIFY(id);
MAGNUM_VERIFY_NO_GL_ERROR();
LineGL<dimensions> b{std::move(a)};
CORRADE_COMPARE(b.id(), id);
CORRADE_COMPARE(b.flags(), LineGL<dimensions>::Flag::UniformBuffers);
CORRADE_COMPARE(b.materialCount(), 2);
CORRADE_COMPARE(b.drawCount(), 5);
CORRADE_VERIFY(!a.id());
LineGL<dimensions> c{NoCreate};
c = std::move(b);
CORRADE_COMPARE(c.id(), id);
CORRADE_COMPARE(c.flags(), LineGL<dimensions>::Flag::UniformBuffers);
CORRADE_COMPARE(c.materialCount(), 2);
CORRADE_COMPARE(c.drawCount(), 5);
CORRADE_VERIFY(!b.id());
}
#endif
// template<UnsignedInt dimensions> void LineGLTest::constructInvalid() {
// auto&& data = ConstructInvalidData[testCaseInstanceId()];
// setTestCaseTemplateName(Utility::format("{}", dimensions));
// setTestCaseDescription(data.name);
//
// CORRADE_SKIP_IF_NO_ASSERT();
//
// std::ostringstream out;
// Error redirectError{&out};
// LineGL<dimensions>{data.flags};
// CORRADE_COMPARE(out.str(), Utility::formatString(
// "Shaders::LineGL: {}\n", data.message));
// }
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void LineGLTest::constructUniformBuffersInvalid() {
auto&& data = ConstructUniformBuffersInvalidData[testCaseInstanceId()];
setTestCaseTemplateName(Utility::format("{}", dimensions));
setTestCaseDescription(data.name);
CORRADE_SKIP_IF_NO_ASSERT();
#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
std::ostringstream out;
Error redirectError{&out};
LineGL<dimensions>{typename LineGL<dimensions>::Configuration{}
.setFlags(data.flags)
.setMaterialCount(data.materialCount)
.setDrawCount(data.drawCount)};
CORRADE_COMPARE(out.str(), Utility::formatString(
"Shaders::LineGL: {}\n", data.message));
}
#endif
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void LineGLTest::setUniformUniformBuffersEnabled() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
CORRADE_SKIP_IF_NO_ASSERT();
#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
LineGL<dimensions> shader{typename LineGL<dimensions>::Configuration{}
.setFlags(LineGL<dimensions>::Flag::UniformBuffers)};
/* This should work fine */
shader.setViewportSize({});
std::ostringstream out;
Error redirectError{&out};
shader.setTransformationProjectionMatrix({})
.setWidth({})
.setSmoothness({})
.setBackgroundColor({})
.setColor({})
.setObjectId({});
CORRADE_COMPARE(out.str(),
"Shaders::LineGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled\n"
"Shaders::LineGL::setWidth(): the shader was created with uniform buffers enabled\n"
"Shaders::LineGL::setSmoothness(): the shader was created with uniform buffers enabled\n"
"Shaders::LineGL::setBackgroundColor(): the shader was created with uniform buffers enabled\n"
"Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled\n"
"Shaders::LineGL::setObjectId(): the shader was created with uniform buffers enabled\n");
}
template<UnsignedInt dimensions> void LineGLTest::bindBufferUniformBuffersNotEnabled() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
CORRADE_SKIP_IF_NO_ASSERT();
std::ostringstream out;
Error redirectError{&out};
GL::Buffer buffer;
LineGL<dimensions> shader;
shader.bindTransformationProjectionBuffer(buffer)
.bindTransformationProjectionBuffer(buffer, 0, 16)
.bindDrawBuffer(buffer)
.bindDrawBuffer(buffer, 0, 16)
.bindMaterialBuffer(buffer)
.bindMaterialBuffer(buffer, 0, 16)
.setDrawOffset(0);
CORRADE_COMPARE(out.str(),
"Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n"
"Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n"
"Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n"
"Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n"
"Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n"
"Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n"
"Shaders::LineGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n");
}
#endif
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void LineGLTest::setObjectIdNotEnabled() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
CORRADE_SKIP_IF_NO_ASSERT();
LineGL<dimensions> shader;
std::ostringstream out;
Error redirectError{&out};
shader.setObjectId(33376);
CORRADE_COMPARE(out.str(),
"Shaders::LineGL::setObjectId(): the shader was not created with object ID enabled\n");
}
#endif
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void LineGLTest::setWrongDrawOffset() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
CORRADE_SKIP_IF_NO_ASSERT();
#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
LineGL<dimensions> shader{typename LineGL<dimensions>::Configuration{}
.setFlags(LineGL<dimensions>::Flag::UniformBuffers)
.setMaterialCount(2)
.setDrawCount(5)};
std::ostringstream out;
Error redirectError{&out};
shader.setDrawOffset(5);
CORRADE_COMPARE(out.str(),
"Shaders::LineGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n");
}
#endif
constexpr Vector2i RenderSize{80, 80};
void LineGLTest::renderSetup() {
/* Pick a color that's directly representable on RGBA4 as well to reduce
artifacts */
GL::Renderer::setClearColor(0x111111_rgbf);
GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
_color = GL::Renderbuffer{};
_color.setStorage(
#if !defined(MAGNUM_TARGET_GLES2) || !defined(MAGNUM_TARGET_WEBGL)
GL::RenderbufferFormat::RGBA8,
#else
GL::RenderbufferFormat::RGBA4,
#endif
RenderSize);
_framebuffer = GL::Framebuffer{{{}, RenderSize}};
_framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color)
.clear(GL::FramebufferClear::Color)
.bind();
}
void LineGLTest::renderTeardown() {
_framebuffer = GL::Framebuffer{NoCreate};
_color = GL::Renderbuffer{NoCreate};
}
template<LineGL2D::Flag flag> void LineGLTest::renderDefaults2D() {
#ifndef MAGNUM_TARGET_GLES2
if(flag == LineGL2D::Flag::UniformBuffers) {
setTestCaseTemplateName("Flag::UniformBuffers");
#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
}
#endif
// TODO drop this crap, use MeshTools once things settle down
struct Vertex {
Vector2 neighborDirection;
Vector2 position;
Vector2 direction;
} vertices[]{
/* Two connected line segments, down and then to the right */
{Vector2{Constants::nan()}, {-0.25f, 0.5f}, {}},
{Vector2{Constants::nan()}, {-0.25f, 0.5f}, {}},
{{}, {-0.5f, -0.5f}, {}},
{{}, {-0.5f, -0.5f}, {}},
{{}, {-0.5f, -0.5f}, {}},
{{}, {-0.5f, -0.5f}, {}},
{Vector2{Constants::nan()}, {0.5f, -0.25f}, {}},
{Vector2{Constants::nan()}, {0.5f, -0.25f}, {}},
/* Singular horizontal and vertical segment */
{Vector2{Constants::nan()}, {-0.75f, 0.25f}, {}},
{Vector2{Constants::nan()}, {-0.75f, 0.25f}, {}},
{Vector2{Constants::nan()}, {-0.75f, 0.75f}, {}},
{Vector2{Constants::nan()}, {-0.75f, 0.75f}, {}},
{Vector2{Constants::nan()}, {-0.25f, -0.75f}, {}},
{Vector2{Constants::nan()}, {-0.25f, -0.75f}, {}},
{Vector2{Constants::nan()}, {0.75f, -0.75f}, {}},
{Vector2{Constants::nan()}, {0.75f, -0.75f}, {}},
/* A single point */
{Vector2{Constants::nan()}, {0.5f, 0.5f}, {}},
{Vector2{Constants::nan()}, {0.5f, 0.5f}, {}},
{Vector2{Constants::nan()}, {0.5f, 0.5f}, {}},
{Vector2{Constants::nan()}, {0.5f, 0.5f}, {}},
};
/* Prev directions */
for(std::size_t i = 4; i < Containers::arraySize(vertices); i += 4) {
if(Math::isNan(vertices[i + 0].neighborDirection)) continue;
vertices[i + 0].neighborDirection = vertices[i + 1].neighborDirection =
vertices[i - 4].position - vertices[i + 0].position;
}
/* Segment directions */
for(std::size_t i = 0; i < Containers::arraySize(vertices); i += 4) {
vertices[i + 0].direction = vertices[i + 1].direction =
vertices[i + 2].direction = vertices[i + 3].direction =
vertices[i + 2].position - vertices[i + 0].position;
}
/* Next directions */
for(std::size_t i = 2; i < Containers::arraySize(vertices) - 4; i += 4) {
if(Math::isNan(vertices[i + 0].neighborDirection)) continue;
vertices[i + 0].neighborDirection = vertices[i + 1].neighborDirection =
vertices[i + 4].position - vertices[i + 0].position;
}
!Debug{} << Containers::stridedArrayView(vertices).slice(&Vertex::position).prefix(8);
!Debug{} << Containers::stridedArrayView(vertices).slice(&Vertex::direction).prefix(8);
!Debug{} << Containers::stridedArrayView(vertices).slice(&Vertex::neighborDirection).prefix(8);
const UnsignedInt indices[]{
0, 1, 2, 2, 1, 3,
4, 5, 6, 6, 5, 7,
8, 9, 10, 10, 9, 11,
12, 13, 14, 14, 13, 15,
16, 17, 18, 18, 17, 19
};
GL::Mesh lines;
lines.addVertexBuffer(GL::Buffer{vertices}, 0,
LineGL2D::LineNeighborDirection{},
LineGL2D::Position{},
LineGL2D::LineDirection{})
.setIndexBuffer(GL::Buffer{indices}, 0, GL::MeshIndexType::UnsignedInt)
.setCount(Containers::arraySize(indices));
LineGL2D shader{LineGL2D::Configuration{}
.setFlags(flag)};
shader.setViewportSize(Vector2{RenderSize});
shader
.setWidth(5.0f)
.setSmoothness(1.0f)
// .setBackgroundColor(0xff0000ff_rgbaf)
// .setColor(0x557766_rgbf)
;
if(flag == LineGL2D::Flag{}) {
shader.draw(lines);
}
#ifndef MAGNUM_TARGET_GLES2
else if(flag == LineGL2D::Flag::UniformBuffers) {
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, {
TransformationProjectionUniform2D{}
}};
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
LineDrawUniform{}
}};
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, {
LineMaterialUniform{}
}};
shader
.bindTransformationProjectionBuffer(transformationProjectionUniform)
.bindDrawBuffer(drawUniform)
.bindMaterialBuffer(materialUniform)
.draw(lines);
}
#endif
else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
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.");
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::Path::join(_testDir, "LineTestFiles/defaults2D.tga"),
/* SwiftShader has 8 different pixels on the edges */
(DebugTools::CompareImageToFile{_manager, 238.0f, 0.2975f}));
}
}}}}
CORRADE_TEST_MAIN(Magnum::Shaders::Test::LineGLTest)

152
src/Magnum/Shaders/Test/LineGL_Test.cpp

@ -0,0 +1,152 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/Containers/String.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/Format.h>
#include "Magnum/Shaders/LineGL.h"
namespace Magnum { namespace Shaders { namespace Test { namespace {
/* There's an underscore between GL and Test to disambiguate from GLTest, which
is a common suffix used to mark tests that need a GL context. Ugly, I know. */
struct LineGL_Test: TestSuite::Tester {
explicit LineGL_Test();
template<UnsignedInt dimensions> void constructConfigurationDefault();
template<UnsignedInt dimensions> void constructConfigurationSetters();
template<UnsignedInt dimensions> void constructNoCreate();
template<UnsignedInt dimensions> void constructCopy();
void debugFlag();
void debugFlags();
void debugFlagsSupersets();
};
LineGL_Test::LineGL_Test() {
addTests({&LineGL_Test::constructConfigurationDefault<2>,
&LineGL_Test::constructConfigurationDefault<3>,
&LineGL_Test::constructConfigurationSetters<2>,
&LineGL_Test::constructConfigurationSetters<3>,
&LineGL_Test::constructNoCreate<2>,
&LineGL_Test::constructNoCreate<3>,
&LineGL_Test::constructCopy<2>,
&LineGL_Test::constructCopy<3>,
&LineGL_Test::debugFlag,
&LineGL_Test::debugFlags,
&LineGL_Test::debugFlagsSupersets});
}
template<UnsignedInt dimensions> void LineGL_Test::constructConfigurationDefault() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
typename LineGL<dimensions>::Configuration configuration;
CORRADE_COMPARE(configuration.flags(), typename LineGL<dimensions>::Flags{});
#ifndef MAGNUM_TARGET_GLES2
CORRADE_COMPARE(configuration.materialCount(), 1);
CORRADE_COMPARE(configuration.drawCount(), 1);
#endif
}
template<UnsignedInt dimensions> void LineGL_Test::constructConfigurationSetters() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
typename LineGL<dimensions>::Configuration configuration = typename LineGL<dimensions>::Configuration{}
.setFlags(LineGL<dimensions>::Flag::VertexColor)
#ifndef MAGNUM_TARGET_GLES2
.setMaterialCount(17)
.setDrawCount(266)
#endif
;
CORRADE_COMPARE(configuration.flags(), LineGL<dimensions>::Flag::VertexColor);
#ifndef MAGNUM_TARGET_GLES2
CORRADE_COMPARE(configuration.materialCount(), 17);
CORRADE_COMPARE(configuration.drawCount(), 266);
#endif
}
template<UnsignedInt dimensions> void LineGL_Test::constructNoCreate() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
{
LineGL<dimensions> shader{NoCreate};
CORRADE_COMPARE(shader.id(), 0);
CORRADE_COMPARE(shader.flags(), typename LineGL<dimensions>::Flags{});
}
CORRADE_VERIFY(true);
}
template<UnsignedInt dimensions> void LineGL_Test::constructCopy() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
CORRADE_VERIFY(!std::is_copy_constructible<LineGL<dimensions>>{});
CORRADE_VERIFY(!std::is_copy_assignable<LineGL<dimensions>>{});
}
void LineGL_Test::debugFlag() {
std::ostringstream out;
Debug{&out} << LineGL3D::Flag::VertexColor << LineGL3D::Flag(0xf00d);
CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::VertexColor Shaders::LineGL::Flag(0xf00d)\n");
}
void LineGL_Test::debugFlags() {
std::ostringstream out;
Debug{&out} << (LineGL3D::Flag::VertexColor|LineGL3D::Flag::InstancedTransformation) << LineGL3D::Flags{};
CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::VertexColor|Shaders::LineGL::Flag::InstancedTransformation Shaders::LineGL::Flags{}\n");
}
void LineGL_Test::debugFlagsSupersets() {
#ifndef MAGNUM_TARGET_GLES2
/* InstancedObjectId is a superset of ObjectId so only one should be
printed */
{
std::ostringstream out;
Debug{&out} << (LineGL3D::Flag::ObjectId|LineGL3D::Flag::InstancedObjectId);
CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::InstancedObjectId\n");
}
#endif
#ifndef MAGNUM_TARGET_GLES2
/* MultiDraw is a superset of UniformBuffers so only one should be printed */
{
std::ostringstream out;
Debug{&out} << (LineGL3D::Flag::MultiDraw|LineGL3D::Flag::UniformBuffers);
CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::MultiDraw\n");
}
#endif
}
}}}}
CORRADE_TEST_MAIN(Magnum::Shaders::Test::LineGL_Test)

196
src/Magnum/Shaders/Test/LineTest.cpp

@ -0,0 +1,196 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <new>
#include <Corrade/TestSuite/Tester.h>
#include "Magnum/Shaders/Line.h"
namespace Magnum { namespace Shaders { namespace Test { namespace {
struct LineTest: TestSuite::Tester {
explicit LineTest();
template<class T> void uniformSizeAlignment();
void drawUniformConstructDefault();
void drawUniformConstructNoInit();
void drawUniformSetters();
void drawUniformMaterialIdPacking();
void materialUniformConstructDefault();
void materialUniformConstructNoInit();
void materialUniformSetters();
};
LineTest::LineTest() {
addTests({&LineTest::uniformSizeAlignment<LineDrawUniform>,
&LineTest::uniformSizeAlignment<LineMaterialUniform>,
&LineTest::drawUniformConstructDefault,
&LineTest::drawUniformConstructNoInit,
&LineTest::drawUniformSetters,
&LineTest::drawUniformMaterialIdPacking,
&LineTest::materialUniformConstructDefault,
&LineTest::materialUniformConstructNoInit,
&LineTest::materialUniformSetters});
}
using namespace Math::Literals;
template<class> struct UniformTraits;
template<> struct UniformTraits<LineDrawUniform> {
static const char* name() { return "LineDrawUniform"; }
};
template<> struct UniformTraits<LineMaterialUniform> {
static const char* name() { return "LineMaterialUniform"; }
};
template<class T> void LineTest::uniformSizeAlignment() {
setTestCaseTemplateName(UniformTraits<T>::name());
CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment.");
/* 48-byte structures are fine, we'll align them to 768 bytes and not
256, but warn about that */
CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment.");
if(256 % sizeof(T) != 0)
CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768.");
CORRADE_COMPARE(alignof(T), 4);
}
void LineTest::drawUniformConstructDefault() {
LineDrawUniform a;
LineDrawUniform b{DefaultInit};
CORRADE_COMPARE(a.materialId, 0);
CORRADE_COMPARE(b.materialId, 0);
CORRADE_COMPARE(a.objectId, 0);
CORRADE_COMPARE(b.objectId, 0);
constexpr LineDrawUniform ca;
constexpr LineDrawUniform cb{DefaultInit};
CORRADE_COMPARE(ca.materialId, 0);
CORRADE_COMPARE(cb.materialId, 0);
CORRADE_COMPARE(ca.objectId, 0);
CORRADE_COMPARE(cb.objectId, 0);
CORRADE_VERIFY(std::is_nothrow_default_constructible<LineDrawUniform>::value);
CORRADE_VERIFY(std::is_nothrow_constructible<LineDrawUniform, DefaultInitT>::value);
/* Implicit construction is not allowed */
CORRADE_VERIFY(!std::is_convertible<DefaultInitT, LineDrawUniform>::value);
}
void LineTest::drawUniformConstructNoInit() {
/* Testing only some fields, should be enough */
LineDrawUniform a;
a.materialId = 5;
a.objectId = 7;
new(&a) LineDrawUniform{NoInit};
{
/* Explicitly check we're not on Clang because certain Clang-based IDEs
inherit __GNUC__ if GCC is used instead of leaving it at 4 like
Clang itself does */
#if defined(CORRADE_TARGET_GCC) && !defined(CORRADE_TARGET_CLANG) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__
CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value.");
#endif
CORRADE_COMPARE(a.materialId, 5);
CORRADE_COMPARE(a.objectId, 7);
}
CORRADE_VERIFY(std::is_nothrow_constructible<LineDrawUniform, NoInitT>::value);
/* Implicit construction is not allowed */
CORRADE_VERIFY(!std::is_convertible<NoInitT, LineDrawUniform>::value);
}
void LineTest::drawUniformSetters() {
LineDrawUniform a;
a.setMaterialId(5)
.setObjectId(7);
CORRADE_COMPARE(a.materialId, 5);
CORRADE_COMPARE(a.objectId, 7);
}
void LineTest::drawUniformMaterialIdPacking() {
LineDrawUniform a;
a.setMaterialId(13765);
/* materialId should be right at the beginning, in the low 16 bits on both
LE and BE */
CORRADE_COMPARE(reinterpret_cast<UnsignedInt*>(&a)[0] & 0xffff, 13765);
}
void LineTest::materialUniformConstructDefault() {
LineMaterialUniform a;
LineMaterialUniform b{DefaultInit};
CORRADE_COMPARE(a.color, 0xffffffff_rgbaf);
CORRADE_COMPARE(b.color, 0xffffffff_rgbaf);
constexpr LineMaterialUniform ca;
constexpr LineMaterialUniform cb{DefaultInit};
CORRADE_COMPARE(ca.color, 0xffffffff_rgbaf);
CORRADE_COMPARE(cb.color, 0xffffffff_rgbaf);
CORRADE_VERIFY(std::is_nothrow_default_constructible<LineDrawUniform>::value);
CORRADE_VERIFY(std::is_nothrow_constructible<LineDrawUniform, DefaultInitT>::value);
/* Implicit construction is not allowed */
CORRADE_VERIFY(!std::is_convertible<DefaultInitT, LineDrawUniform>::value);
}
void LineTest::materialUniformConstructNoInit() {
/* Testing only some fields, should be enough */
LineMaterialUniform a;
a.color = 0x354565fc_rgbaf;
new(&a) LineMaterialUniform{NoInit};
{
/* Explicitly check we're not on Clang because certain Clang-based IDEs
inherit __GNUC__ if GCC is used instead of leaving it at 4 like
Clang itself does */
#if defined(CORRADE_TARGET_GCC) && !defined(CORRADE_TARGET_CLANG) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__
CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value.");
#endif
CORRADE_COMPARE(a.color, 0x354565fc_rgbaf);
}
CORRADE_VERIFY(std::is_nothrow_constructible<LineMaterialUniform, NoInitT>::value);
/* Implicit construction is not allowed */
CORRADE_VERIFY(!std::is_convertible<NoInitT, LineMaterialUniform>::value);
}
void LineTest::materialUniformSetters() {
LineMaterialUniform a;
a.setColor(0x354565fc_rgbaf);
CORRADE_COMPARE(a.color, 0x354565fc_rgbaf);
}
}}}}
CORRADE_TEST_MAIN(Magnum::Shaders::Test::LineTest)

6
src/Magnum/Shaders/generic.glsl

@ -28,10 +28,12 @@
#define POSITION_ATTRIBUTE_LOCATION 0 #define POSITION_ATTRIBUTE_LOCATION 0
#define TEXTURECOORDINATES_ATTRIBUTE_LOCATION 1 #define TEXTURECOORDINATES_ATTRIBUTE_LOCATION 1
#define COLOR_ATTRIBUTE_LOCATION 2 #define COLOR_ATTRIBUTE_LOCATION 2
#define TANGENT_ATTRIBUTE_LOCATION 3 #define TANGENT_ATTRIBUTE_LOCATION 3 /* also LineDirection */
#define LINE_DIRECTION_ATTRIBUTE_LOCATION 3 /* also Tangent */
#define BITANGENT_ATTRIBUTE_LOCATION 4 /* also ObjectId */ #define BITANGENT_ATTRIBUTE_LOCATION 4 /* also ObjectId */
#define OBJECT_ID_ATTRIBUTE_LOCATION 4 /* also Bitangent */ #define OBJECT_ID_ATTRIBUTE_LOCATION 4 /* also Bitangent */
#define NORMAL_ATTRIBUTE_LOCATION 5 #define NORMAL_ATTRIBUTE_LOCATION 5 /* also LineNeighborDirection */
#define LINE_NEIGHBOR_DIRECTION_ATTRIBUTE_LOCATION 5 /* also Normal */
#define TRANSFORMATION_MATRIX_ATTRIBUTE_LOCATION 8 #define TRANSFORMATION_MATRIX_ATTRIBUTE_LOCATION 8
#define NORMAL_MATRIX_ATTRIBUTE_LOCATION 12 #define NORMAL_MATRIX_ATTRIBUTE_LOCATION 12

6
src/Magnum/Shaders/resources-gl.conf

@ -12,6 +12,12 @@ filename=FullScreenTriangle.glsl
[file] [file]
filename=generic.glsl filename=generic.glsl
[file]
filename=Line.vert
[file]
filename=Line.frag
[file] [file]
filename=MeshVisualizer.vert filename=MeshVisualizer.vert

Loading…
Cancel
Save