diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index 8bcfc27b1..6b3f439a3 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -72,7 +72,35 @@ if(BUILD_GL_TESTS) ${CMAKE_CURRENT_BINARY_DIR}/configure.h) endif() - corrade_add_test(ShadersDistanceFieldVectorGLTest DistanceFieldVectorGLTest.cpp LIBRARIES MagnumShaders MagnumOpenGLTester) + corrade_add_test(ShadersDistanceFieldVectorGLTest DistanceFieldVectorGLTest.cpp + LIBRARIES + MagnumDebugTools + MagnumMeshTools + MagnumPrimitives + MagnumShadersTestLib + MagnumOpenGLTester + FILES + TestFiles/vector-distancefield.tga + + VectorTestFiles/defaults.tga + VectorTestFiles/defaults-distancefield.tga + VectorTestFiles/smooth0.1-2D.tga + VectorTestFiles/smooth0.1-3D.tga + VectorTestFiles/smooth0.2-2D.tga + VectorTestFiles/smooth0.2-3D.tga + VectorTestFiles/outline2D.tga + VectorTestFiles/outline3D.tga) + if(NOT BUILD_PLUGINS_STATIC) + target_include_directories(ShadersDistanceFieldVectorGLTest PRIVATE $) + else() + target_include_directories(ShadersDistanceFieldVectorGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + if(WITH_ANYIMAGEIMPORTER) + target_link_libraries(ShadersDistanceFieldVectorGLTest PRIVATE AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + target_link_libraries(ShadersDistanceFieldVectorGLTest PRIVATE TgaImporter) + endif() + endif() corrade_add_test(ShadersFlatGLTest FlatGLTest.cpp LIBRARIES @@ -109,7 +137,31 @@ if(BUILD_GL_TESTS) endif() endif() - corrade_add_test(ShadersMeshVisualizerGLTest MeshVisualizerGLTest.cpp LIBRARIES MagnumShaders MagnumOpenGLTester) + corrade_add_test(ShadersMeshVisualizerGLTest MeshVisualizerGLTest.cpp + LIBRARIES + MagnumDebugTools + MagnumMeshTools + MagnumPrimitives + MagnumShadersTestLib + MagnumOpenGLTester + FILES + FlatTestFiles/defaults.tga + FlatTestFiles/colored3D.tga + MeshVisualizerTestFiles/defaults-wireframe.tga + MeshVisualizerTestFiles/wireframe.tga + MeshVisualizerTestFiles/wireframe-nogeo.tga + MeshVisualizerTestFiles/wireframe-wide.tga) + if(NOT BUILD_PLUGINS_STATIC) + target_include_directories(ShadersMeshVisualizerGLTest PRIVATE $) + else() + target_include_directories(ShadersMeshVisualizerGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + if(WITH_ANYIMAGEIMPORTER) + target_link_libraries(ShadersMeshVisualizerGLTest PRIVATE AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + target_link_libraries(ShadersMeshVisualizerGLTest PRIVATE TgaImporter) + endif() + endif() corrade_add_test(ShadersPhongGLTest PhongGLTest.cpp LIBRARIES @@ -158,8 +210,54 @@ if(BUILD_GL_TESTS) endif() endif() - corrade_add_test(ShadersVectorGLTest VectorGLTest.cpp LIBRARIES MagnumShaders MagnumOpenGLTester) - corrade_add_test(ShadersVertexColorGLTest VertexColorGLTest.cpp LIBRARIES MagnumShaders MagnumOpenGLTester) + corrade_add_test(ShadersVectorGLTest VectorGLTest.cpp + LIBRARIES + MagnumDebugTools + MagnumMeshTools + MagnumPrimitives + MagnumShadersTestLib + MagnumOpenGLTester + FILES + TestFiles/vector.tga + + VectorTestFiles/defaults.tga + VectorTestFiles/vector2D.tga + VectorTestFiles/vector3D.tga) + if(NOT BUILD_PLUGINS_STATIC) + target_include_directories(ShadersVectorGLTest PRIVATE $) + else() + target_include_directories(ShadersVectorGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + if(WITH_ANYIMAGEIMPORTER) + target_link_libraries(ShadersVectorGLTest PRIVATE AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + target_link_libraries(ShadersVectorGLTest PRIVATE TgaImporter) + endif() + endif() + + corrade_add_test(ShadersVertexColorGLTest VertexColorGLTest.cpp + LIBRARIES + MagnumDebugTools + MagnumMeshTools + MagnumPrimitives + MagnumShadersTestLib + MagnumOpenGLTester + FILES + FlatTestFiles/defaults.tga + + VertexColorTestFiles/vertexColor2D.tga + VertexColorTestFiles/vertexColor3D.tga) + if(NOT BUILD_PLUGINS_STATIC) + target_include_directories(ShadersVertexColorGLTest PRIVATE $) + else() + target_include_directories(ShadersVertexColorGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + if(WITH_ANYIMAGEIMPORTER) + target_link_libraries(ShadersVertexColorGLTest PRIVATE AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + target_link_libraries(ShadersVertexColorGLTest PRIVATE TgaImporter) + endif() + endif() set_target_properties( ShadersDistanceFieldVectorGLTest diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index 7ac598038..9e14eab6d 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -23,8 +23,32 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include +#include +#include + +#include "Magnum/DebugTools/CompareImage.h" #include "Magnum/GL/OpenGLTester.h" +#include "Magnum/GL/Framebuffer.h" +#include "Magnum/GL/Mesh.h" +#include "Magnum/GL/Renderbuffer.h" +#include "Magnum/GL/RenderbufferFormat.h" +#include "Magnum/GL/Texture.h" +#include "Magnum/GL/TextureFormat.h" +#include "Magnum/Image.h" +#include "Magnum/ImageView.h" +#include "Magnum/PixelFormat.h" +#include "Magnum/MeshTools/Compile.h" +#include "Magnum/Primitives/Plane.h" +#include "Magnum/Primitives/Square.h" #include "Magnum/Shaders/DistanceFieldVector.h" +#include "Magnum/Trade/AbstractImporter.h" +#include "Magnum/Trade/ImageData.h" +#include "Magnum/Trade/MeshData2D.h" +#include "Magnum/Trade/MeshData3D.h" + +#include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -33,6 +57,46 @@ struct DistanceFieldVectorGLTest: GL::OpenGLTester { template void construct(); template void constructMove(); + + void renderSetup(); + void renderTeardown(); + + void renderDefaults2D(); + void renderDefaults3D(); + void render2D(); + void render3D(); + + private: + PluginManager::Manager _manager{"nonexistent"}; + + GL::Renderbuffer _color{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Renderbuffer _objectId{NoCreate}; + #endif + GL::Framebuffer _framebuffer{NoCreate}; +}; + +/* + Rendering tests done on: + + - Mesa Intel + - Mesa AMD + - SwiftShader ES2/ES3 + - ARM Mali (Huawei P10) ES2/ES3 + - WebGL 1 / 2 (on Mesa Intel) +*/ + +using namespace Math::Literals; + +constexpr struct { + const char* name; + Float outlineRangeStart, outlineRangeEnd, smoothness; + const char* file2D; + const char* file3D; +} RenderData[] { + {"smooth0.1", 0.5f, 1.0f, 0.1f, "smooth0.1-2D.tga", "smooth0.1-3D.tga"}, + {"smooth0.2", 0.5f, 1.0f, 0.2f, "smooth0.2-2D.tga", "smooth0.2-3D.tga"}, + {"outline", 0.6f, 0.45f, 0.05f, "outline2D.tga", "outline3D.tga"} }; DistanceFieldVectorGLTest::DistanceFieldVectorGLTest() { @@ -41,6 +105,26 @@ DistanceFieldVectorGLTest::DistanceFieldVectorGLTest() { &DistanceFieldVectorGLTest::construct<3>, &DistanceFieldVectorGLTest::constructMove<2>, &DistanceFieldVectorGLTest::constructMove<3>}); + + addTests({&DistanceFieldVectorGLTest::renderDefaults2D, + &DistanceFieldVectorGLTest::renderDefaults3D}, + &DistanceFieldVectorGLTest::renderSetup, + &DistanceFieldVectorGLTest::renderTeardown); + + addInstancedTests({&DistanceFieldVectorGLTest::render2D, + &DistanceFieldVectorGLTest::render3D}, + Containers::arraySize(RenderData), + &DistanceFieldVectorGLTest::renderSetup, + &DistanceFieldVectorGLTest::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(_manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + #ifdef TGAIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT(_manager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif } template void DistanceFieldVectorGLTest::construct() { @@ -77,6 +161,270 @@ template void DistanceFieldVectorGLTest::constructMove() CORRADE_VERIFY(!b.id()); } +constexpr Vector2i RenderSize{80, 80}; + +void DistanceFieldVectorGLTest::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 DistanceFieldVectorGLTest::renderTeardown() { + _framebuffer = GL::Framebuffer{NoCreate}; + _color = GL::Renderbuffer{NoCreate}; +} + +constexpr GL::TextureFormat TextureFormatR = + #ifndef MAGNUM_TARGET_GLES2 + GL::TextureFormat::R8 + #else + GL::TextureFormat::Luminance + #endif + ; + +void DistanceFieldVectorGLTest::renderDefaults2D() { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh square = MeshTools::compile(Primitives::squareSolid(Primitives::SquareTextureCoords::Generate)); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/vector-distancefield.tga")) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif + + DistanceFieldVector2D shader; + shader.bindVectorTexture(texture); + square.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + /* Should be almost the same as Shaders::Vector output, but due to various + differences in the SDF output and too sharp default shininess it can't + be exact */ + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "VectorTestFiles/defaults.tga"), + (DebugTools::CompareImageToFile{_manager, 189.0f, 6.1f})); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has off-by-one differences on edges, ARM Mali off-by-one in + all channels. */ + const Float maxThreshold = 3.0f, meanThreshold = 0.076f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 17.0f, meanThreshold = 0.480f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "VectorTestFiles/defaults-distancefield.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +void DistanceFieldVectorGLTest::renderDefaults3D() { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh plane = MeshTools::compile(Primitives::planeSolid(Primitives::PlaneTextureCoords::Generate)); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/vector-distancefield.tga")) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif + + DistanceFieldVector2D shader; + shader.bindVectorTexture(texture); + plane.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + /* Should be almost the same as Shaders::Vector output, but due to various + differences in the SDF output and too sharp default shininess it can't + be exact */ + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "VectorTestFiles/defaults.tga"), + (DebugTools::CompareImageToFile{_manager, 189.0f, 6.1f})); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has off-by-one differences on edges, ARM Mali off-by-one in + all channels. */ + const Float maxThreshold = 3.0f, meanThreshold = 0.076f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 17.0f, meanThreshold = 0.480f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "VectorTestFiles/defaults-distancefield.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +void DistanceFieldVectorGLTest::render2D() { + auto&& data = RenderData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh square = MeshTools::compile(Primitives::squareSolid(Primitives::SquareTextureCoords::Generate)); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/vector-distancefield.tga")) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif + + DistanceFieldVector2D shader; + shader + /** @todo implement background color */ + .setColor(0xffff99_rgbf) + .setOutlineColor(0x9999ff_rgbf) + .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) + .setSmoothness(data.smoothness) + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .bindVectorTexture(texture); + square.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has off-by-one differences when smoothing */ + const Float maxThreshold = 5.667f, meanThreshold = 0.268f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 17.0f, meanThreshold = 2.386f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join({SHADERS_TEST_DIR, "VectorTestFiles", data.file2D}), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +void DistanceFieldVectorGLTest::render3D() { + auto&& data = RenderData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh plane = MeshTools::compile(Primitives::planeSolid(Primitives::PlaneTextureCoords::Generate)); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/vector-distancefield.tga")) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif + + DistanceFieldVector3D shader; + shader + /** @todo implement background color */ + .setColor(0xffff99_rgbf) + .setOutlineColor(0x9999ff_rgbf) + .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) + .setSmoothness(data.smoothness) + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationZ(15.0_degf)) + .bindVectorTexture(texture); + plane.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has off-by-one differences when smoothing plus a bunch of + different pixels on primitive edges */ + const Float maxThreshold = 17.0f, meanThreshold = 0.192f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 17.0f, meanThreshold = 1.613f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join({SHADERS_TEST_DIR, "VectorTestFiles", data.file3D}), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::DistanceFieldVectorGLTest) diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 41477c75c..dbea6588f 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -23,10 +23,34 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include +#include +#include +#include + +#include "Magnum/DebugTools/CompareImage.h" #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" #include "Magnum/GL/OpenGLTester.h" +#include "Magnum/GL/Framebuffer.h" +#include "Magnum/GL/Mesh.h" +#include "Magnum/GL/Renderbuffer.h" +#include "Magnum/GL/RenderbufferFormat.h" +#include "Magnum/Image.h" +#include "Magnum/ImageView.h" +#include "Magnum/PixelFormat.h" +#include "Magnum/MeshTools/Compile.h" +#include "Magnum/MeshTools/Duplicate.h" +#include "Magnum/Primitives/Circle.h" +#include "Magnum/Primitives/Icosphere.h" +#include "Magnum/Primitives/UVSphere.h" #include "Magnum/Shaders/MeshVisualizer.h" +#include "Magnum/Trade/AbstractImporter.h" +#include "Magnum/Trade/MeshData2D.h" +#include "Magnum/Trade/MeshData3D.h" + +#include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -40,6 +64,52 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { void constructWireframeNoGeometryShader(); void constructMove(); + + void renderSetup(); + void renderTeardown(); + + void renderDefaults(); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + void renderDefaultsWireframe(); + #endif + void render(); + void renderWireframe(); + + private: + PluginManager::Manager _manager{"nonexistent"}; + + GL::Renderbuffer _color{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Renderbuffer _objectId{NoCreate}; + #endif + GL::Framebuffer _framebuffer{NoCreate}; +}; + +/* + Rendering tests done on: + + - Mesa Intel + - Mesa AMD + - SwiftShader ES2/ES3 + - ARM Mali (Huawei P10) ES2/ES3 + - WebGL 1 / 2 (on Mesa Intel) +*/ + +using namespace Math::Literals; + +constexpr struct { + const char* name; + MeshVisualizer::Flags flags; + Float width, smoothness; + const char* file; + const char* fileXfail; +} WireframeData[] { + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + {"", MeshVisualizer::Flags{}, 1.0f, 2.0f, "wireframe.tga", nullptr}, + {"wide/sharp", MeshVisualizer::Flags{}, 3.0f, 1.0f, "wireframe-wide.tga", nullptr}, + #endif + {"no geometry shader", MeshVisualizer::Flag::NoGeometryShader, 1.0f, 2.0f, "wireframe.tga", "wireframe-nogeo.tga"}, + {"no geometry shader, wide/sharp", MeshVisualizer::Flag::NoGeometryShader, 3.0f, 1.0f, "wireframe-wide.tga", "wireframe-nogeo.tga"} }; MeshVisualizerGLTest::MeshVisualizerGLTest() { @@ -50,6 +120,28 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::constructWireframeNoGeometryShader, &MeshVisualizerGLTest::constructMove}); + + addTests({&MeshVisualizerGLTest::renderDefaults, + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + &MeshVisualizerGLTest::renderDefaultsWireframe, + #endif + &MeshVisualizerGLTest::render}, + &MeshVisualizerGLTest::renderSetup, + &MeshVisualizerGLTest::renderTeardown); + + addInstancedTests({&MeshVisualizerGLTest::renderWireframe}, + Containers::arraySize(WireframeData), + &MeshVisualizerGLTest::renderSetup, + &MeshVisualizerGLTest::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(_manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + #ifdef TGAIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT(_manager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif } void MeshVisualizerGLTest::construct() { @@ -124,6 +216,239 @@ void MeshVisualizerGLTest::constructMove() { CORRADE_VERIFY(!b.id()); } +constexpr Vector2i RenderSize{80, 80}; + +void MeshVisualizerGLTest::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 MeshVisualizerGLTest::renderTeardown() { + _framebuffer = GL::Framebuffer{NoCreate}; + _color = GL::Renderbuffer{NoCreate}; +} + +void MeshVisualizerGLTest::renderDefaults() { + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); + + MeshVisualizer shader; + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on four pixels */ + const Float maxThreshold = 238.0f, meanThreshold = 0.298f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 238.0f, meanThreshold = 0.298f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "FlatTestFiles/defaults.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +void MeshVisualizerGLTest::renderDefaultsWireframe() { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported")); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() + std::string(" is not supported")); + #endif + + #ifdef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + Debug() << "Using" << GL::Extensions::NV::shader_noperspective_interpolation::string(); + #endif + + GL::Mesh sphere = MeshTools::compile(Primitives::icosphereSolid(1)); + + MeshVisualizer shader{MeshVisualizer::Flag::Wireframe}; + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + { + CORRADE_EXPECT_FAIL("Defaults don't work for wireframe as line width is derived from viewport size."); + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "MeshVisualizerTestFiles/defaults-wireframe.tga"), + (DebugTools::CompareImageToFile{_manager})); + } + + /** @todo make this unnecessary */ + shader.setViewportSize({80, 80}); + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "MeshVisualizerTestFiles/defaults-wireframe.tga"), + /* AMD has off-by-one errors on edges compared to Intel */ + (DebugTools::CompareImageToFile{_manager, 1.0f, 0.06f})); +} +#endif + +void MeshVisualizerGLTest::render() { + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); + + MeshVisualizer shader; + shader.setColor(0x9999ff_rgbf) + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)); + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on four pixels */ + const Float maxThreshold = 170.0f, meanThreshold = 0.133f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 170.0f, meanThreshold = 0.456f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "FlatTestFiles/colored3D.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +void MeshVisualizerGLTest::renderWireframe() { + auto&& data = WireframeData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + #ifndef MAGNUM_TARGET_GLES + if(!(data.flags & MeshVisualizer::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported")); + #else + if(!(data.flags & MeshVisualizer::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() + std::string(" is not supported")); + #endif + + #ifdef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + Debug() << "Using" << GL::Extensions::NV::shader_noperspective_interpolation::string(); + #endif + #endif + + const Trade::MeshData3D sphereData = Primitives::icosphereSolid(1); + + GL::Mesh sphere{NoCreate}; + if(data.flags & MeshVisualizer::Flag::NoGeometryShader) { + sphere = GL::Mesh{}; + sphere.setCount(sphereData.indices().size()); + + /* Duplicate the vertices */ + GL::Buffer positions; + positions.setData(MeshTools::duplicate(Containers::stridedArrayView(sphereData.indices()), Containers::stridedArrayView(sphereData.positions(0)))); + sphere.addVertexBuffer(std::move(positions), 0, MeshVisualizer::Position{}); + + /* Supply also the vertex ID, if needed */ + #ifndef MAGNUM_TARGET_GLES2 + if(!GL::Context::current().isExtensionSupported()) + #endif + { + Containers::Array vertexIndex{sphereData.indices().size()}; + std::iota(vertexIndex.begin(), vertexIndex.end(), 0.0f); + + GL::Buffer vertexId; + vertexId.setData(vertexIndex); + sphere.addVertexBuffer(std::move(vertexId), 0, MeshVisualizer::VertexIndex{}); + } + } else sphere = MeshTools::compile(sphereData); + + MeshVisualizer shader{data.flags|MeshVisualizer::Flag::Wireframe}; + shader.setColor(0xffff99_rgbf) + .setWireframeColor(0x9999ff_rgbf) + .setWireframeWidth(data.width) + .setSmoothness(data.smoothness) + .setViewportSize({80, 80}) + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)); + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + { + CORRADE_EXPECT_FAIL_IF(data.flags & MeshVisualizer::Flag::NoGeometryShader, + "Line width is currently not configurable w/o geometry shader."); + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on four pixels */ + const Float maxThreshold = 170.0f, meanThreshold = 0.327f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 170.0f, meanThreshold = 1.699f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join({SHADERS_TEST_DIR, "MeshVisualizerTestFiles", data.file}), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); + } + + /* Test it's not *too* off, at least */ + if(data.flags & MeshVisualizer::Flag::NoGeometryShader) { + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on four pixels */ + const Float maxThreshold = 170.0f, meanThreshold = 0.327f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 170.0f, meanThreshold = 1.699f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join({SHADERS_TEST_DIR, "MeshVisualizerTestFiles", data.fileXfail}), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); + } +} + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::MeshVisualizerGLTest) diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-wireframe.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-wireframe.tga new file mode 100644 index 000000000..7a61e17e8 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/defaults-wireframe.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo.tga new file mode 100644 index 000000000..d04880f84 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-nogeo.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-wide.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-wide.tga new file mode 100644 index 000000000..1314e14a2 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-wide.tga differ diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe.tga new file mode 100644 index 000000000..9d9724072 Binary files /dev/null and b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe.tga differ diff --git a/src/Magnum/Shaders/Test/TestFiles/README.md b/src/Magnum/Shaders/Test/TestFiles/README.md new file mode 100644 index 000000000..20d37e576 --- /dev/null +++ b/src/Magnum/Shaders/Test/TestFiles/README.md @@ -0,0 +1,22 @@ +Common shader test files +------------------------ + +The `*-texture.tga` files are exported quadrants from `checkerboard.svg` with +various layers enabled, saved either as three-channel or four-channel PNG of +128x128 pixels and then converted to TGA via `magnum-imageconverter` (so the +shader tests don't need any external plugins) --- if everything is correct, the +RGB files will be 48 kB and the RGBA ones 64 kB. + +The `vector.tga` file is exported from `magnum/doc/generated/vector.svg` as +128x128, converted to a single-channel format via ImageMagick: + +```sh +convert vector.png -flatten -colorspace Gray vector-r.png +``` + +and then to a single-channel (16 kB) TGA via `magnum-imageconverter`. Then, +this file is converted to a SDF via + +```sh +magnum-distancefieldconverter --output-size "64 64" --radius 16 vector.tga vector-distancefield.tga +``` diff --git a/src/Magnum/Shaders/Test/TestFiles/vector-distancefield.tga b/src/Magnum/Shaders/Test/TestFiles/vector-distancefield.tga new file mode 100644 index 000000000..adb78d3c1 Binary files /dev/null and b/src/Magnum/Shaders/Test/TestFiles/vector-distancefield.tga differ diff --git a/src/Magnum/Shaders/Test/TestFiles/vector.tga b/src/Magnum/Shaders/Test/TestFiles/vector.tga new file mode 100644 index 000000000..5b4117015 Binary files /dev/null and b/src/Magnum/Shaders/Test/TestFiles/vector.tga differ diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index 880f1edb9..df9cb3c0a 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -23,8 +23,32 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include +#include +#include + +#include "Magnum/DebugTools/CompareImage.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/GL/Texture.h" +#include "Magnum/GL/TextureFormat.h" +#include "Magnum/Image.h" +#include "Magnum/ImageView.h" +#include "Magnum/PixelFormat.h" +#include "Magnum/MeshTools/Compile.h" +#include "Magnum/Primitives/Plane.h" +#include "Magnum/Primitives/Square.h" #include "Magnum/Shaders/Vector.h" +#include "Magnum/Trade/AbstractImporter.h" +#include "Magnum/Trade/ImageData.h" +#include "Magnum/Trade/MeshData2D.h" +#include "Magnum/Trade/MeshData3D.h" + +#include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -33,14 +57,59 @@ struct VectorGLTest: GL::OpenGLTester { template void construct(); template void constructMove(); + + void renderSetup(); + void renderTeardown(); + + void renderDefaults2D(); + void renderDefaults3D(); + void render2D(); + void render3D(); + + private: + PluginManager::Manager _manager{"nonexistent"}; + + GL::Renderbuffer _color{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Renderbuffer _objectId{NoCreate}; + #endif + GL::Framebuffer _framebuffer{NoCreate}; }; +/* + Rendering tests done on: + + - Mesa Intel + - Mesa AMD + - SwiftShader ES2/ES3 + - ARM Mali (Huawei P10) ES2/ES3 + - WebGL 1 / 2 (on Mesa Intel) +*/ + +using namespace Math::Literals; + VectorGLTest::VectorGLTest() { addTests({ &VectorGLTest::construct<2>, &VectorGLTest::construct<3>, &VectorGLTest::constructMove<2>, &VectorGLTest::constructMove<3>}); + + addTests({&VectorGLTest::renderDefaults2D, + &VectorGLTest::renderDefaults3D, + &VectorGLTest::render2D, + &VectorGLTest::render3D}, + &VectorGLTest::renderSetup, + &VectorGLTest::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(_manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + #ifdef TGAIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT(_manager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif } template void VectorGLTest::construct() { @@ -77,6 +146,241 @@ template void VectorGLTest::constructMove() { CORRADE_VERIFY(!b.id()); } +constexpr Vector2i RenderSize{80, 80}; + +void VectorGLTest::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 VectorGLTest::renderTeardown() { + _framebuffer = GL::Framebuffer{NoCreate}; + _color = GL::Renderbuffer{NoCreate}; +} + +constexpr GL::TextureFormat TextureFormatR = + #ifndef MAGNUM_TARGET_GLES2 + GL::TextureFormat::R8 + #else + GL::TextureFormat::Luminance + #endif + ; + +void VectorGLTest::renderDefaults2D() { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh square = MeshTools::compile(Primitives::squareSolid(Primitives::SquareTextureCoords::Generate)); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/vector.tga")) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif + + Vector2D shader; + shader.bindVectorTexture(texture); + square.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has off-by-one differences on edges, ARM Mali a bit more of + them */ + const Float maxThreshold = 1.0f, meanThreshold = 0.022f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 17.0f, meanThreshold = 0.359f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "VectorTestFiles/defaults.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +void VectorGLTest::renderDefaults3D() { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh plane = MeshTools::compile(Primitives::planeSolid(Primitives::PlaneTextureCoords::Generate)); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/vector.tga")) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif + + Vector3D shader; + shader.bindVectorTexture(texture); + plane.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has off-by-one differences on edges, ARM Mali a bit more of + them */ + const Float maxThreshold = 1.0f, meanThreshold = 0.022f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 17.0f, meanThreshold = 0.359f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "VectorTestFiles/defaults.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +void VectorGLTest::render2D() { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh square = MeshTools::compile(Primitives::squareSolid(Primitives::SquareTextureCoords::Generate)); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/vector.tga")) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif + + Vector2D shader; + shader + .setBackgroundColor(0x9999ff_rgbf) + .setColor(0xffff99_rgbf) + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::rotation(5.0_degf)) + .bindVectorTexture(texture); + square.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on four pixels */ + const Float maxThreshold = 170.0f, meanThreshold = 0.146f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 170.0f, meanThreshold = 0.962f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "VectorTestFiles/vector2D.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +void VectorGLTest::render3D() { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh plane = MeshTools::compile(Primitives::planeSolid(Primitives::PlaneTextureCoords::Generate)); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D texture; + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/vector.tga")) && (image = importer->image2D(0))); + texture.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge); + + #ifdef MAGNUM_TARGET_GLES2 + /* Don't want to bother with the fiasco of single-channel formats and + texture storage extensions on ES2 */ + texture.setImage(0, TextureFormatR, *image); + #else + texture.setStorage(1, TextureFormatR, image->size()) + .setSubImage(0, {}, *image); + #endif + + Vector3D shader; + shader + .setBackgroundColor(0x9999ff_rgbf) + .setColor(0xffff99_rgbf) + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationZ(15.0_degf)) + .bindVectorTexture(texture); + plane.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on four pixels */ + const Float maxThreshold = 170.0f, meanThreshold = 0.171f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 170.0f, meanThreshold = 0.660f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "VectorTestFiles/vector3D.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::VectorGLTest) diff --git a/src/Magnum/Shaders/Test/VectorTestFiles/defaults-distancefield.tga b/src/Magnum/Shaders/Test/VectorTestFiles/defaults-distancefield.tga new file mode 100644 index 000000000..f6b007969 Binary files /dev/null and b/src/Magnum/Shaders/Test/VectorTestFiles/defaults-distancefield.tga differ diff --git a/src/Magnum/Shaders/Test/VectorTestFiles/defaults.tga b/src/Magnum/Shaders/Test/VectorTestFiles/defaults.tga new file mode 100644 index 000000000..56317d331 Binary files /dev/null and b/src/Magnum/Shaders/Test/VectorTestFiles/defaults.tga differ diff --git a/src/Magnum/Shaders/Test/VectorTestFiles/outline2D.tga b/src/Magnum/Shaders/Test/VectorTestFiles/outline2D.tga new file mode 100644 index 000000000..9ac9ac875 Binary files /dev/null and b/src/Magnum/Shaders/Test/VectorTestFiles/outline2D.tga differ diff --git a/src/Magnum/Shaders/Test/VectorTestFiles/outline3D.tga b/src/Magnum/Shaders/Test/VectorTestFiles/outline3D.tga new file mode 100644 index 000000000..057026528 Binary files /dev/null and b/src/Magnum/Shaders/Test/VectorTestFiles/outline3D.tga differ diff --git a/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.1-2D.tga b/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.1-2D.tga new file mode 100644 index 000000000..ffea06754 Binary files /dev/null and b/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.1-2D.tga differ diff --git a/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.1-3D.tga b/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.1-3D.tga new file mode 100644 index 000000000..76327db1e Binary files /dev/null and b/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.1-3D.tga differ diff --git a/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.2-2D.tga b/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.2-2D.tga new file mode 100644 index 000000000..4605cc559 Binary files /dev/null and b/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.2-2D.tga differ diff --git a/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.2-3D.tga b/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.2-3D.tga new file mode 100644 index 000000000..3ed7debb5 Binary files /dev/null and b/src/Magnum/Shaders/Test/VectorTestFiles/smooth0.2-3D.tga differ diff --git a/src/Magnum/Shaders/Test/VectorTestFiles/vector2D.tga b/src/Magnum/Shaders/Test/VectorTestFiles/vector2D.tga new file mode 100644 index 000000000..0519a950c Binary files /dev/null and b/src/Magnum/Shaders/Test/VectorTestFiles/vector2D.tga differ diff --git a/src/Magnum/Shaders/Test/VectorTestFiles/vector3D.tga b/src/Magnum/Shaders/Test/VectorTestFiles/vector3D.tga new file mode 100644 index 000000000..f8f07efbe Binary files /dev/null and b/src/Magnum/Shaders/Test/VectorTestFiles/vector3D.tga differ diff --git a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp index 55709e03d..9e0f3182c 100644 --- a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp @@ -23,8 +23,28 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include +#include + +#include "Magnum/DebugTools/CompareImage.h" #include "Magnum/GL/OpenGLTester.h" +#include "Magnum/GL/Framebuffer.h" +#include "Magnum/GL/Mesh.h" +#include "Magnum/GL/Renderbuffer.h" +#include "Magnum/GL/RenderbufferFormat.h" +#include "Magnum/Image.h" +#include "Magnum/ImageView.h" +#include "Magnum/PixelFormat.h" +#include "Magnum/MeshTools/Compile.h" +#include "Magnum/Primitives/Circle.h" +#include "Magnum/Primitives/UVSphere.h" #include "Magnum/Shaders/VertexColor.h" +#include "Magnum/Trade/AbstractImporter.h" +#include "Magnum/Trade/MeshData2D.h" +#include "Magnum/Trade/MeshData3D.h" + +#include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -33,14 +53,65 @@ struct VertexColorGLTest: GL::OpenGLTester { template void construct(); template void constructMove(); + + void renderSetup(); + void renderTeardown(); + + template void renderDefaults2D(); + template void renderDefaults3D(); + + template void render2D(); + template void render3D(); + + private: + PluginManager::Manager _manager{"nonexistent"}; + + GL::Renderbuffer _color{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Renderbuffer _objectId{NoCreate}; + #endif + GL::Framebuffer _framebuffer{NoCreate}; }; +/* + Rendering tests done on: + + - Mesa Intel + - Mesa AMD + - SwiftShader ES2/ES3 + - ARM Mali (Huawei P10) ES2/ES3 + - WebGL 1 / 2 (on Mesa Intel) +*/ + +using namespace Math::Literals; + VertexColorGLTest::VertexColorGLTest() { addTests({ &VertexColorGLTest::construct<2>, &VertexColorGLTest::construct<3>, &VertexColorGLTest::constructMove<2>, &VertexColorGLTest::constructMove<3>}); + + addTests({&VertexColorGLTest::renderDefaults2D, + &VertexColorGLTest::renderDefaults2D, + &VertexColorGLTest::renderDefaults3D, + &VertexColorGLTest::renderDefaults3D, + + &VertexColorGLTest::render2D, + &VertexColorGLTest::render2D, + &VertexColorGLTest::render3D, + &VertexColorGLTest::render3D}, + &VertexColorGLTest::renderSetup, + &VertexColorGLTest::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(_manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + #ifdef TGAIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT(_manager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif } template void VertexColorGLTest::construct() { @@ -77,6 +148,193 @@ template void VertexColorGLTest::constructMove() { CORRADE_VERIFY(!b.id()); } +constexpr Vector2i RenderSize{80, 80}; + +void VertexColorGLTest::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 VertexColorGLTest::renderTeardown() { + _framebuffer = GL::Framebuffer{NoCreate}; + _color = GL::Renderbuffer{NoCreate}; +} + +template void VertexColorGLTest::renderDefaults2D() { + setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); + + Trade::MeshData2D circleData = Primitives::circle2DSolid(32, + Primitives::CircleTextureCoords::Generate); + + /* All a single color */ + Containers::Array colorData{Containers::DirectInit, circleData.positions(0).size(), 0xffffff_rgbf}; + + GL::Buffer colors; + colors.setData(colorData); + GL::Mesh circle = MeshTools::compile(circleData); + circle.addVertexBuffer(colors, 0, GL::Attribute{}); + + VertexColor2D shader; + circle.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on eight pixels */ + const Float maxThreshold = 238.0f, meanThreshold = 0.298f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 238.0f, meanThreshold = 0.298f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "FlatTestFiles/defaults.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +template void VertexColorGLTest::renderDefaults3D() { + setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + Trade::MeshData3D sphereData = Primitives::uvSphereSolid(16, 32, + Primitives::UVSphereTextureCoords::Generate); + + /* All a single color */ + Containers::Array colorData{Containers::DirectInit, sphereData.positions(0).size(), 0xffffff_rgbf}; + + GL::Buffer colors; + colors.setData(colorData); + GL::Mesh sphere = MeshTools::compile(sphereData); + sphere.addVertexBuffer(colors, 0, GL::Attribute{}); + + VertexColor3D shader; + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has differently rasterized edges on eight pixels */ + const Float maxThreshold = 238.0f, meanThreshold = 0.298f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 238.0f, meanThreshold = 0.298f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "FlatTestFiles/defaults.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +template void VertexColorGLTest::render2D() { + setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); + + Trade::MeshData2D circleData = Primitives::circle2DSolid(32, + Primitives::CircleTextureCoords::Generate); + + /* Highlight a quarter */ + Containers::Array colorData{Containers::DirectInit, circleData.positions(0).size(), 0x9999ff_rgbf}; + for(std::size_t i = 8; i != 16; ++i) + colorData[i + 1] = 0xffff99_rgbf; + + GL::Buffer colors; + colors.setData(colorData); + GL::Mesh circle = MeshTools::compile(circleData); + circle.addVertexBuffer(colors, 0, GL::Attribute{}); + + VertexColor2D shader; + shader.setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})); + circle.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* AMD has minor rounding differences in the gradient compared to Intel, + SwiftShader as well */ + const Float maxThreshold = 1.0f, meanThreshold = 0.667f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 11.34f, meanThreshold = 1.479f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "VertexColorTestFiles/vertexColor2D.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + +template void VertexColorGLTest::render3D() { + setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + Trade::MeshData3D sphereData = Primitives::uvSphereSolid(16, 32, + Primitives::UVSphereTextureCoords::Generate); + + /* Highlight the middle rings */ + Containers::Array colorData{Containers::DirectInit, sphereData.positions(0).size(), 0x9999ff_rgbf}; + for(std::size_t i = 6*33; i != 9*33; ++i) + colorData[i + 1] = 0xffff99_rgbf; + + GL::Buffer colors; + colors.setData(colorData); + GL::Mesh sphere = MeshTools::compile(sphereData); + sphere.addVertexBuffer(colors, 0, GL::Attribute{}); + + VertexColor3D shader; + shader.setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)); + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* AMD has one different pixel compared to Intel, SwiftShader has + differently rasterized edges on five pixels */ + const Float maxThreshold = 204.0f, meanThreshold = 0.158f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 204.0f, meanThreshold = 1.284f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "VertexColorTestFiles/vertexColor3D.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::VertexColorGLTest) diff --git a/src/Magnum/Shaders/Test/VertexColorTestFiles/vertexColor2D.tga b/src/Magnum/Shaders/Test/VertexColorTestFiles/vertexColor2D.tga new file mode 100644 index 000000000..c6beb73c6 Binary files /dev/null and b/src/Magnum/Shaders/Test/VertexColorTestFiles/vertexColor2D.tga differ diff --git a/src/Magnum/Shaders/Test/VertexColorTestFiles/vertexColor3D.tga b/src/Magnum/Shaders/Test/VertexColorTestFiles/vertexColor3D.tga new file mode 100644 index 000000000..b1c79eb0e Binary files /dev/null and b/src/Magnum/Shaders/Test/VertexColorTestFiles/vertexColor3D.tga differ