Browse Source

Merge branch 'master' of github.com:mosra/magnum into feature/split-mainLoopIteration#577

pull/580/head
Andreas Leroux 4 years ago
parent
commit
ce65421b0a
  1. 2
      .editorconfig
  2. 170
      CMakeLists.txt
  3. 38
      doc/building.dox
  4. 2
      doc/changelog-old.dox
  5. 81
      doc/changelog.dox
  6. 11
      doc/cmake.dox
  7. 4
      doc/credits.dox
  8. 10
      doc/file-formats.dox
  9. 6
      doc/namespaces.dox
  10. 6
      doc/opengl-support.dox
  11. 4
      doc/opengl-wrapping.dox
  12. 28
      doc/shaders.dox
  13. 394
      doc/snippets/MagnumGL.cpp
  14. 6
      doc/snippets/MagnumMath.cpp
  15. 23
      doc/snippets/MagnumShaders-gl.cpp
  16. 71
      modules/FindMagnum.cmake
  17. 2
      package/archlinux/PKGBUILD-es2desktop
  18. 2
      package/archlinux/PKGBUILD-es3desktop
  19. 2
      package/ci/appveyor-desktop-gles.bat
  20. 18
      package/ci/appveyor-lcov.sh
  21. 38
      package/ci/circleci.yml
  22. 1
      package/ci/unix-desktop.sh
  23. 14
      src/Magnum/Animation/Track.h
  24. 9
      src/Magnum/Audio/AbstractImporter.cpp
  25. 2
      src/Magnum/Audio/Context.h
  26. 24
      src/Magnum/Audio/Test/AbstractImporterTest.cpp
  27. 8
      src/Magnum/DebugTools/CompareImage.h
  28. 2
      src/Magnum/DebugTools/TextureImage.cpp
  29. 75
      src/Magnum/GL/AbstractShaderProgram.cpp
  30. 166
      src/Magnum/GL/AbstractShaderProgram.h
  31. 6
      src/Magnum/GL/CMakeLists.txt
  32. 4
      src/Magnum/GL/Context.h
  33. 7
      src/Magnum/GL/Implementation/ShaderProgramState.cpp
  34. 3
      src/Magnum/GL/Implementation/ShaderProgramState.h
  35. 12
      src/Magnum/GL/Implementation/ShaderState.cpp
  36. 3
      src/Magnum/GL/Implementation/ShaderState.h
  37. 2
      src/Magnum/GL/OpenGL.h
  38. 14
      src/Magnum/GL/OpenGLTester.h
  39. 100
      src/Magnum/GL/Shader.cpp
  40. 147
      src/Magnum/GL/Shader.h
  41. 233
      src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp
  42. 13
      src/Magnum/GL/Test/MeshGLTest.cpp
  43. 4
      src/Magnum/GL/Test/RendererGLTest.cpp
  44. 4
      src/Magnum/GL/Test/SampleQueryGLTest.cpp
  45. 117
      src/Magnum/GL/Test/ShaderGLTest.cpp
  46. 7
      src/Magnum/GL/Test/TransformFeedbackGLTest.cpp
  47. 60
      src/Magnum/Magnum.h
  48. 2
      src/Magnum/Math/Complex.h
  49. 6
      src/Magnum/Math/FunctionsBatch.h
  50. 21
      src/Magnum/Math/Test/FunctionsBatchTest.cpp
  51. 10
      src/Magnum/Mesh.cpp
  52. 9
      src/Magnum/Mesh.h
  53. 47
      src/Magnum/MeshTools/Combine.cpp
  54. 15
      src/Magnum/MeshTools/Combine.h
  55. 38
      src/Magnum/MeshTools/Concatenate.cpp
  56. 26
      src/Magnum/MeshTools/Concatenate.h
  57. 9
      src/Magnum/MeshTools/Interleave.cpp
  58. 6
      src/Magnum/MeshTools/InterleaveFlags.h
  59. 2
      src/Magnum/MeshTools/Test/CombineTest.cpp
  60. 4
      src/Magnum/MeshTools/Test/FullScreenTriangleGLTest.cpp
  61. 10
      src/Magnum/PixelFormat.cpp
  62. 9
      src/Magnum/PixelFormat.h
  63. 88
      src/Magnum/Platform/CMakeLists.txt
  64. 10
      src/Magnum/Platform/GlfwApplication.cpp
  65. 3
      src/Magnum/Platform/GlxApplication.h
  66. 11
      src/Magnum/Platform/Sdl2Application.cpp
  67. 11
      src/Magnum/Platform/Test/CMakeLists.txt
  68. 55
      src/Magnum/Platform/Test/WindowlessWindowsEglApplicationTest.cpp
  69. 75
      src/Magnum/Platform/WindowlessEglApplication.cpp
  70. 7
      src/Magnum/Platform/WindowlessEglApplication.h
  71. 5
      src/Magnum/Platform/WindowlessGlxApplication.h
  72. 238
      src/Magnum/Platform/WindowlessWindowsEglApplication.cpp
  73. 538
      src/Magnum/Platform/WindowlessWindowsEglApplication.h
  74. 21
      src/Magnum/Platform/gl-info.cpp
  75. 17
      src/Magnum/SceneTools/CMakeLists.txt
  76. 1035
      src/Magnum/SceneTools/Implementation/sceneConverterUtilities.h
  77. 58
      src/Magnum/SceneTools/Test/CMakeLists.txt
  78. 1686
      src/Magnum/SceneTools/Test/SceneConverterTest.cpp
  79. 3
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-mesh.obj
  80. 22
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-scene.gltf
  81. 5
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/empty.gltf
  82. 12
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-animations.txt
  83. 12
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-cameras.txt
  84. 6
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-ignored-output.txt
  85. 3
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-images.txt
  86. 8
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-lights.txt
  87. 28
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-materials.txt
  88. 21
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-meshes-bounds.txt
  89. 16
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-meshes.txt
  90. 5
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-objects.txt
  91. 111
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-references.txt
  92. 25
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-scenes-objects.txt
  93. 11
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-scenes.txt
  94. 10
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-skins.txt
  95. 8
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-textures.txt
  96. 5
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info.txt
  97. 2
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/point.obj
  98. 12
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/quad-duplicates-fuzzy.obj
  99. 12
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/quad-duplicates.obj
  100. BIN
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/quad-duplicates.ply
  101. Some files were not shown because too many files have changed in this diff Show More

2
.editorconfig

@ -5,6 +5,6 @@ indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
[*.{css,html,yml,rb}]
[*.{css,html,yml,rb,gltf}]
indent_style = space
indent_size = 2

170
CMakeLists.txt

@ -91,7 +91,6 @@ set(_MAGNUM_DEPRECATED_UNPREFIXED_OPTIONS
WITH_WINDOWLESSGLXAPPLICATION
WITH_WINDOWLESSIOSAPPLICATION
WITH_WINDOWLESSWGLAPPLICATION
WITH_WINDOWLESSWINDOWSEGLAPPLICATION
WITH_CGLCONTEXT
WITH_EGLCONTEXT
WITH_GLXCONTEXT
@ -147,27 +146,20 @@ if(NOT DEFINED _MAGNUM_ACCEPT_DEPRECATED_UNPREFIXED_OPTIONS)
endforeach()
endif()
# If targeting iOS, Android, Emscripten or Windows RT, set explicit OpenGL ES
# support
if(NOT CORRADE_TARGET_IOS AND NOT CORRADE_TARGET_ANDROID AND NOT CORRADE_TARGET_EMSCRIPTEN AND NOT CORRADE_TARGET_WINDOWS_RT)
option(MAGNUM_TARGET_GLES "Build for OpenGL ES / WebGL" OFF)
else()
set(MAGNUM_TARGET_GLES ON)
endif()
cmake_dependent_option(MAGNUM_TARGET_GLES2 "Build for OpenGL ES 2 / WebGL 1.0" ON "MAGNUM_TARGET_GLES" OFF)
cmake_dependent_option(MAGNUM_TARGET_DESKTOP_GLES "Build for OpenGL ES on desktop" OFF "MAGNUM_TARGET_GLES" OFF)
# Magnum GL Info (currently only using GLX/CGL/EGL on *nix, WGL/EGL on Windows
# and EGL on Emscripten)
if(CORRADE_TARGET_UNIX OR CORRADE_TARGET_WINDOWS OR CORRADE_TARGET_EMSCRIPTEN)
option(MAGNUM_WITH_GL_INFO "Build magnum-gl-info utility" OFF)
endif()
# Desktop-only utilities
# Desktop-only utilities. Not guaranteed to build on GLES, but showing the
# option everywhere for simplicity.
if(CORRADE_TARGET_UNIX OR CORRADE_TARGET_WINDOWS)
cmake_dependent_option(MAGNUM_WITH_FONTCONVERTER "Build magnum-fontconverter utility" OFF "NOT MAGNUM_TARGET_GLES" OFF)
cmake_dependent_option(MAGNUM_WITH_DISTANCEFIELDCONVERTER "Build magnum-distancefieldconverter utility" OFF "NOT MAGNUM_TARGET_GLES" OFF)
option(MAGNUM_WITH_FONTCONVERTER "Build magnum-fontconverter utility" OFF)
option(MAGNUM_WITH_DISTANCEFIELDCONVERTER "Build magnum-distancefieldconverter utility" OFF)
set(MAGNUM_FONTCONVERTER_STATIC_PLUGINS "" CACHE STRING "Static plugins to link to the magnum-fontconverter utility")
set(MAGNUM_DISTANCEFIELDCONVERTER_STATIC_PLUGINS "" CACHE STRING "Static plugins to link to the magnum-distancefieldconverter utility")
endif()
# API-independent utilities
@ -175,6 +167,10 @@ option(MAGNUM_WITH_IMAGECONVERTER "Build magnum-imageconverter utility" OFF)
option(MAGNUM_WITH_SCENECONVERTER "Build magnum-sceneconverter utility" OFF)
option(MAGNUM_WITH_SHADERCONVERTER "Build magnum-shaderconverter utility" OFF)
set(MAGNUM_IMAGECONVERTER_STATIC_PLUGINS "" CACHE STRING "Static plugins to link to the magnum-imageconverter utility")
set(MAGNUM_SCENECONVERTER_STATIC_PLUGINS "" CACHE STRING "Static plugins to link to the magnum-sceneconverter utility")
set(MAGNUM_SHADERCONVERTER_STATIC_PLUGINS "" CACHE STRING "Static plugins to link to the magnum-shaderconverter utility")
# Magnum AL Info
option(MAGNUM_WITH_AL_INFO "Build magnum-al-info utility" OFF)
@ -200,21 +196,40 @@ cmake_dependent_option(MAGNUM_WITH_AUDIO "Build Audio library" OFF "NOT MAGNUM_W
option(MAGNUM_WITH_DEBUGTOOLS "Build DebugTools library" ON)
cmake_dependent_option(MAGNUM_WITH_MESHTOOLS "Build MeshTools library" ON "NOT MAGNUM_WITH_OBJIMPORTER;NOT MAGNUM_WITH_SCENECONVERTER" ON)
option(MAGNUM_WITH_SCENEGRAPH "Build SceneGraph library" ON)
cmake_dependent_option(MAGNUM_WITH_SCENETOOLS "Build SceneTools library" ON "NOT WITH_SCENECONVERTER" ON)
cmake_dependent_option(MAGNUM_WITH_SCENETOOLS "Build SceneTools library" ON "NOT MAGNUM_WITH_SCENECONVERTER" ON)
option(MAGNUM_WITH_SHADERS "Build Shaders library" ON)
cmake_dependent_option(MAGNUM_WITH_SHADERTOOLS "Build ShaderTools library" ON "NOT MAGNUM_WITH_SHADERCONVERTER" ON)
cmake_dependent_option(MAGNUM_WITH_TEXT "Build Text library" ON "NOT MAGNUM_WITH_FONTCONVERTER;NOT MAGNUM_WITH_MAGNUMFONT;NOT MAGNUM_WITH_MAGNUMFONTCONVERTER" ON)
cmake_dependent_option(MAGNUM_WITH_TEXTURETOOLS "Build TextureTools library" ON "NOT MAGNUM_WITH_TEXT;NOT MAGNUM_WITH_DISTANCEFIELDCONVERTER" ON)
cmake_dependent_option(MAGNUM_WITH_TRADE "Build Trade library" ON "NOT MAGNUM_WITH_MESHTOOLS;NOT MAGNUM_WITH_PRIMITIVES;NOT MAGNUM_WITH_SCENETOOLS;NOT MAGNUM_WITH_IMAGECONVERTER;NOT MAGNUM_WITH_ANYIMAGEIMPORTER;NOT MAGNUM_WITH_ANYIMAGECONVERTER;NOT MAGNUM_WITH_ANYSCENEIMPORTER;NOT MAGNUM_WITH_OBJIMPORTER;NOT MAGNUM_WITH_TGAIMAGECONVERTER;NOT MAGNUM_WITH_TGAIMPORTER" ON)
cmake_dependent_option(MAGNUM_WITH_GL "Build GL library" ON "NOT MAGNUM_WITH_SHADERS;NOT MAGNUM_WITH_GL_INFO;NOT MAGNUM_WITH_ANDROIDAPPLICATION;NOT MAGNUM_WITH_WINDOWLESSIOSAPPLICATION;NOT MAGNUM_WITH_CGLCONTEXT;NOT MAGNUM_WITH_GLXAPPLICATION;NOT MAGNUM_WITH_GLXCONTEXT;NOT MAGNUM_WITH_XEGLAPPLICATION;NOT MAGNUM_WITH_WINDOWLESSWGLAPPLICATION;NOT MAGNUM_WITH_WGLCONTEXT;NOT MAGNUM_WITH_WINDOWLESSWINDOWSEGLAPPLICATION;NOT MAGNUM_WITH_DISTANCEFIELDCONVERTER" ON)
cmake_dependent_option(MAGNUM_WITH_GL "Build GL library" ON "NOT MAGNUM_WITH_SHADERS;NOT MAGNUM_WITH_GL_INFO;NOT MAGNUM_WITH_ANDROIDAPPLICATION;NOT MAGNUM_WITH_WINDOWLESSIOSAPPLICATION;NOT MAGNUM_WITH_WINDOWLESSCGLAPPLICATION;NOT MAGNUM_WITH_WINDOWLESSGLXAPPLICATION;NOT MAGNUM_WITH_CGLCONTEXT;NOT MAGNUM_WITH_GLXAPPLICATION;NOT MAGNUM_WITH_GLXCONTEXT;NOT MAGNUM_WITH_XEGLAPPLICATION;NOT MAGNUM_WITH_WINDOWLESSWGLAPPLICATION;NOT MAGNUM_WITH_WGLCONTEXT;NOT MAGNUM_WITH_DISTANCEFIELDCONVERTER" ON)
option(MAGNUM_WITH_PRIMITIVES "Build Primitives library" ON)
cmake_dependent_option(MAGNUM_TARGET_HEADLESS "Build command-line utilities for use on a headless machines" OFF "MAGNUM_WITH_GL" OFF)
cmake_dependent_option(MAGNUM_TARGET_GL "Build libraries with OpenGL interoperability" ON "MAGNUM_WITH_GL" OFF)
# EGL context and windowless EGL application, available everywhere
cmake_dependent_option(MAGNUM_WITH_WINDOWLESSEGLAPPLICATION "Build WindowlessEglApplication library" OFF "NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES OR NOT MAGNUM_WITH_GL_INFO;NOT TARGET_HEADLESS" ON)
option(MAGNUM_WITH_EGLCONTEXT "Build EglContext library" OFF)
# If targeting iOS, Android, Emscripten or Windows RT, implicitly enable GLES.
# Otherwise default to desktop GL.
if(CORRADE_TARGET_IOS OR CORRADE_TARGET_ANDROID OR CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_WINDOWS_RT)
set(MAGNUM_TARGET_GLES ON)
elseif(MAGNUM_WITH_GL)
cmake_dependent_option(MAGNUM_TARGET_GLES "Build for OpenGL ES / WebGL" OFF "MAGNUM_WITH_GL" OFF)
endif()
# If targeting Android, Emscripten or Windows RT, implicitly enable EGL.
# Otherwise enable EGL by default only if targeting GLES and not on iOS (where
# it's EAGL instead)
if(CORRADE_TARGET_ANDROID OR CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_WINDOWS_RT)
set(MAGNUM_TARGET_EGL ON)
else()
if(MAGNUM_TARGET_GLES AND NOT CORRADE_TARGET_IOS)
set(_MAGNUM_TARGET_EGL_DEFAULT ON)
else()
set(_MAGNUM_TARGET_EGL_DEFAULT OFF)
endif()
cmake_dependent_option(MAGNUM_TARGET_EGL "Build for EGL instead of EAGL / CGL / GLX / WGL" ${_MAGNUM_TARGET_EGL_DEFAULT} "MAGNUM_WITH_GL" OFF)
endif()
cmake_dependent_option(MAGNUM_TARGET_GLES2 "Build for OpenGL ES 2 / WebGL 1.0" ON "MAGNUM_TARGET_GLES" OFF)
# Vulkan, everywhere except Emscripten
if(NOT CORRADE_TARGET_EMSCRIPTEN)
@ -222,6 +237,18 @@ if(NOT CORRADE_TARGET_EMSCRIPTEN)
cmake_dependent_option(MAGNUM_TARGET_VK "Build libraries with Vulkan interoperability" ON "MAGNUM_WITH_VK" OFF)
endif()
# EGL context and windowless EGL application, available everywhere. If
# targeting EGL and not on Windows, it's implied by the CLI tools, otherwise
# it's independent.
if(MAGNUM_TARGET_EGL AND NOT CORRADE_TARGET_WINDOWS)
cmake_dependent_option(MAGNUM_WITH_WINDOWLESSEGLAPPLICATION "Build WindowlessEglApplication library" OFF "NOT MAGNUM_WITH_GL_INFO;NOT MAGNUM_WITH_DISTANCEFIELDCONVERTER;NOT MAGNUM_WITH_FONTCONVERTER" ON)
else()
# TODO when CMake 3.22 can be relied on, clean this up to use a proper
# condition instead
cmake_dependent_option(MAGNUM_WITH_WINDWLESSEGLAPPLICATION "Build WindowlessEglApplication library" OFF "ON" OFF)
endif()
option(MAGNUM_WITH_EGLCONTEXT "Build EglContext library" OFF)
# Android-specific application libraries
if(CORRADE_TARGET_ANDROID)
option(MAGNUM_WITH_ANDROIDAPPLICATION "Build AndroidApplication library" OFF)
@ -235,27 +262,42 @@ elseif(CORRADE_TARGET_IOS)
option(MAGNUM_WITH_WINDOWLESSIOSAPPLICATION "Build WindowlessIosApplication library" OFF)
# macOS-specific application libraries
elseif(CORRADE_TARGET_APPLE AND NOT MAGNUM_TARGET_GLES)
elseif(CORRADE_TARGET_APPLE AND NOT MAGNUM_TARGET_EGL)
# WindowlessCglApplication implied by the CLI tools unless targeting EGL
if(NOT MAGNUM_TARGET_EGL)
cmake_dependent_option(MAGNUM_WITH_WINDOWLESSCGLAPPLICATION "Build WindowlessCglApplication library" OFF "NOT MAGNUM_WITH_GL_INFO;NOT MAGNUM_WITH_FONTCONVERTER;NOT MAGNUM_WITH_DISTANCEFIELDCONVERTER" ON)
else()
# TODO when CMake 3.22 can be relied on, clean this up to use a proper
# condition instead
cmake_dependent_option(MAGNUM_WITH_WINDOWLESSCGLAPPLICATION "Build WindowlessCglApplication library" OFF "ON" OFF)
endif()
option(MAGNUM_WITH_CGLCONTEXT "Build CglContext library" OFF)
# X11 + GLX/EGL-specific application libraries
elseif(CORRADE_TARGET_UNIX)
option(MAGNUM_WITH_GLXAPPLICATION "Build GlxApplication library" OFF)
if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)
# WindowlessGlxApplication implied by the CLI tools unless targeting EGL
if(NOT MAGNUM_TARGET_EGL)
cmake_dependent_option(MAGNUM_WITH_WINDOWLESSGLXAPPLICATION "Build WindowlessGlxApplication library" OFF "NOT MAGNUM_WITH_GL_INFO;NOT MAGNUM_WITH_FONTCONVERTER;NOT MAGNUM_WITH_DISTANCEFIELDCONVERTER" ON)
option(MAGNUM_WITH_GLXCONTEXT "Build GlxContext library" OFF)
else()
# TODO when CMake 3.22 can be relied on, clean this up to use a proper
# condition instead
cmake_dependent_option(MAGNUM_WITH_WINDOWLESSGLXAPPLICATION "Build WindowlessGlxApplication library" OFF "ON" OFF)
endif()
option(MAGNUM_WITH_GLXCONTEXT "Build GlxContext library" OFF)
option(MAGNUM_WITH_XEGLAPPLICATION "Build XEglApplication library" OFF)
# Windows-specific application libraries
elseif(CORRADE_TARGET_WINDOWS)
if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)
# WindowlessWglApplication implied by the CLI tools unless targeting EGL
if(NOT MAGNUM_TARGET_EGL)
cmake_dependent_option(MAGNUM_WITH_WINDOWLESSWGLAPPLICATION "Build WindowlessWglApplication library" OFF "NOT MAGNUM_WITH_GL_INFO;NOT MAGNUM_WITH_FONTCONVERTER;NOT MAGNUM_WITH_DISTANCEFIELDCONVERTER" ON)
option(MAGNUM_WITH_WGLCONTEXT "Build WglContext library" OFF)
else()
cmake_dependent_option(MAGNUM_WITH_WINDOWLESSWINDOWSEGLAPPLICATION "Build WindowlessWindowsEglApplication library" OFF "NOT MAGNUM_WITH_GL_INFO;NOT MAGNUM_WITH_FONTCONVERTER;NOT MAGNUM_WITH_DISTANCEFIELDCONVERTER" ON)
# TODO when CMake 3.22 can be relied on, clean this up to use a proper
# condition instead
cmake_dependent_option(MAGNUM_WITH_WINDOWLESSWGLAPPLICATION "Build WindowlessWglApplication library" OFF "ON" OFF)
endif()
option(MAGNUM_WITH_WGLCONTEXT "Build WglContext library" OFF)
endif()
# Platform-independent (almost) application libraries
@ -268,18 +310,6 @@ endif()
option(MAGNUM_BUILD_DEPRECATED "Include deprecated API in the build" ON)
# BUILD_MULTITHREADED got moved to Corrade itself. In case we're building with
# deprecated features enabled, print a warning in case it's set but Corrade
# reports a different value. We can't print a warning in case it's set because
# that would cause false positives when both Corrade and Magnum are subprojects
# (and thus this option is visible to both). Otherwise it's silent --- for
# non-deprecated builds CMake will at most warn about "variable being unused".
if(MAGNUM_BUILD_DEPRECATED)
if(DEFINED BUILD_MULTITHREADED AND ((NOT CORRADE_BUILD_MULTITHREADED AND BUILD_MULTITHREADED) OR (CORRADE_BUILD_MULTITHREADED AND NOT BUILD_MULTITHREADED)))
message(DEPRECATION "BUILD_MULTITHREADED (set to ${BUILD_MULTITHREADED}) is now ignored — you need to set it when building Corrade instead (there it's ${CORRADE_BUILD_MULTITHREADED} now)")
endif()
endif()
set(MAGNUM_DEPLOY_PREFIX "."
CACHE STRING "Prefix where to put final application executables")
@ -389,6 +419,9 @@ if(_MAGNUM_ACCEPT_DEPRECATED_UNPREFIXED_OPTIONS AND MAGNUM_BUILD_DEPRECATED)
set(WITH_WINDOWLESSCGLAPPLICATION ON)
endif()
elseif(CORRADE_TARGET_UNIX)
# Checking the old deprecated options here, checking
# MAGNUM_TARGET_EGL wouldn't make sense as that's an option the
# old code definitely won't use.
if((NOT TARGET_GLES AND NOT TARGET_HEADLESS) OR TARGET_DESKTOP_GLES)
if(NOT DEFINED WITH_WINDOWLESSGLXAPPLICATION)
set(WITH_WINDOWLESSGLXAPPLICATION ON)
@ -404,8 +437,8 @@ if(_MAGNUM_ACCEPT_DEPRECATED_UNPREFIXED_OPTIONS AND MAGNUM_BUILD_DEPRECATED)
set(WITH_WINDOWLESSWGLAPPLICATION ON)
endif()
else()
if(NOT DEFINED WITH_WINDOWLESSWINDOWSEGLAPPLICATION)
set(WITH_WINDOWLESSWINDOWSEGLAPPLICATION ON)
if(NOT DEFINED WITH_WINDOWLESSEGLAPPLICATION)
set(WITH_WINDOWLESSEGLAPPLICATION ON)
endif()
endif()
endif()
@ -457,6 +490,45 @@ if(_MAGNUM_ACCEPT_DEPRECATED_UNPREFIXED_OPTIONS AND MAGNUM_BUILD_DEPRECATED)
endif()
endif()
# Handle other deprecated options. For non-deprecated builds CMake will at most
# warn about "variable being unused". Done after the MAGNUM_ prefix backwards
# compatibility above to pick up also the old names, i.e. TARGET_HEADLESS ->
# MAGNUM_TARGET_EGL.
if(MAGNUM_BUILD_DEPRECATED)
# BUILD_MULTITHREADED got moved to Corrade itself. Print a warning in case
# it's set but Corrade reports a different value. We can't print a warning
# in case it's set because that would cause false positives when both
# Corrade and Magnum are subprojects (and thus this option is visible to
# both).
if(DEFINED BUILD_MULTITHREADED AND ((NOT CORRADE_BUILD_MULTITHREADED AND BUILD_MULTITHREADED) OR (CORRADE_BUILD_MULTITHREADED AND NOT BUILD_MULTITHREADED)))
message(DEPRECATION "BUILD_MULTITHREADED (set to ${BUILD_MULTITHREADED}) is now ignored — you need to set it when building Corrade instead (there it's ${CORRADE_BUILD_MULTITHREADED} now)")
endif()
# The following two options were desktop-only, so don't handle any
# backwards compatibility on mobile / web platforms
if(NOT CORRADE_TARGET_IOS AND NOT CORRADE_TARGET_ANDROID AND NOT CORRADE_TARGET_EMSCRIPTEN AND NOT CORRADE_TARGET_WINDOWS_RT)
# MAGNUM_TARGET_HEADLESS is now MAGNUM_TARGET_EGL. Print a warning in
# case we're on desktop GL (where it was meant to be used) and the two
# are set to a different value, and sync them.
if(NOT MAGNUM_TARGET_GLES AND DEFINED MAGNUM_TARGET_HEADLESS AND ((NOT MAGNUM_TARGET_EGL AND MAGNUM_TARGET_HEADLESS) OR (MAGNUM_TARGET_EGL AND NOT MAGNUM_TARGET_HEADLESS)))
message(DEPRECATION "MAGNUM_TARGET_HEADLESS is deprecated, use MAGNUM_TARGET_EGL instead")
set(MAGNUM_TARGET_EGL ${MAGNUM_TARGET_HEADLESS})
endif()
# MAGNUM_TARGET_DESKTOP_GLES is now an inverse of MAGNUM_TARGET_EGL.
# Print a warning in case we're on GLES (where it was meant to be used)
# and the two are set to a different value, and sync them.
if(MAGNUM_TARGET_GLES AND DEFINED MAGNUM_TARGET_DESKTOP_GLES AND ((MAGNUM_TARGET_EGL AND MAGNUM_TARGET_DESKTOP_GLES) OR (NOT MAGNUM_TARGET_EGL AND NOT MAGNUM_TARGET_DESKTOP_GLES)))
message(DEPRECATION "MAGNUM_TARGET_DESKTOP_GLES is deprecated, use MAGNUM_TARGET_EGL instead")
if(MAGNUM_TARGET_DESKTOP_GLES)
set(MAGNUM_TARGET_EGL OFF)
else()
set(MAGNUM_TARGET_EGL ON)
endif()
endif()
endif()
endif()
# Dynamic linking is meaningless on Emscripten and too inconvenient on Android
if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID)
set(MAGNUM_BUILD_STATIC ON)
@ -468,7 +540,7 @@ endif()
# Check dependencies
if(MAGNUM_WITH_GL)
if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)
if(NOT MAGNUM_TARGET_GLES OR (MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_EGL AND NOT CORRADE_TARGET_IOS))
# OpenGL library preference. Prefer to use GLVND, since that's the
# better approach nowadays, but allow the users to override it from
# outside in case it is broken for some reason (Nvidia drivers in
@ -490,7 +562,7 @@ else()
# consistency
set(MAGNUM_TARGET_GLES OFF)
set(MAGNUM_TARGET_GLES2 OFF)
set(MAGNUM_TARGET_DESKTOP_GLES OFF)
set(MAGNUM_TARGET_EGL OFF)
endif()
if(NOT MAGNUM_WITH_VK)
@ -526,7 +598,7 @@ if(MAGNUM_BUILD_TESTS)
endif()
if(MAGNUM_WITH_OPENGLTESTER)
if(MAGNUM_TARGET_HEADLESS OR CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID)
if(MAGNUM_TARGET_EGL)
set(MAGNUM_WITH_WINDOWLESSEGLAPPLICATION ON)
set(OPENGLTESTER_APPLICATION MagnumWindowlessEglApplication)
elseif(CORRADE_TARGET_IOS)
@ -536,21 +608,11 @@ if(MAGNUM_WITH_OPENGLTESTER)
set(MAGNUM_WITH_WINDOWLESSCGLAPPLICATION ON)
set(OPENGLTESTER_APPLICATION MagnumWindowlessCglApplication)
elseif(CORRADE_TARGET_UNIX)
if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES)
set(MAGNUM_WITH_WINDOWLESSEGLAPPLICATION ON)
set(OPENGLTESTER_APPLICATION MagnumWindowlessEglApplication)
else()
set(MAGNUM_WITH_WINDOWLESSGLXAPPLICATION ON)
set(OPENGLTESTER_APPLICATION MagnumWindowlessGlxApplication)
endif()
elseif(CORRADE_TARGET_WINDOWS)
if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)
set(MAGNUM_WITH_WINDOWLESSWGLAPPLICATION ON)
set(OPENGLTESTER_APPLICATION MagnumWindowlessWglApplication)
else()
set(MAGNUM_WITH_WINDOWLESSWINDOWSEGLAPPLICATION ON)
set(OPENGLTESTER_APPLICATION MagnumWindowlessWindowsEglApplication)
endif()
else()
# Assuming this gets hit only if MAGNUM_BUILD_GL_TESTS are enabled
message(FATAL_ERROR "Cannot run tests for OpenGL code on this platform. Set MAGNUM_BUILD_GL_TESTS to OFF to skip building them.")

38
doc/building.dox

@ -461,15 +461,10 @@ available for desktop OpenGL only, see @ref requires-gl.
- `MAGNUM_TARGET_GLES2` --- Target OpenGL ES 2.0 instead of 3.0 and later.
Available only when `MAGNUM_WITH_GL` is enabled. Currently enabled by
default when `MAGNUM_TARGET_GLES` is set.
- `MAGNUM_TARGET_DESKTOP_GLES` --- Target OpenGL ES on desktop, i.e. use
OpenGL ES emulation in desktop OpenGL drivers. Available on Linux and
Windows, though might not be supported by all drivers. Available only when
`MAGNUM_WITH_GL` is enabled.
- `MAGNUM_TARGET_HEADLESS` --- Build command-line utilities for use on a
headless machine. Basically it means that EGL with no display attachment is
being used everywhere instead of platform-specific toolkits like CGL, GLX
or WGL. Supported mainly on OpenGL ES drivers. Available only when
`MAGNUM_WITH_GL` is enabled.
- `MAGNUM_TARGET_EGL` --- Target EGL instead of a platform-specific OpenGL
support library like CGL, EAGL, GLX or WGL. Enabled implicitly on Android,
Emscripten and Windows RT, enabled by default when `MAGNUM_TARGET_GLES` is
set unless on iOS. Available only when `MAGNUM_WITH_GL` is enabled.
- `MAGNUM_TARGET_VK` --- Build libraries with Vulkan interoperability
enabled. Enabled by default when `MAGNUM_WITH_VK` is enabled. Disabling
this will cause libraries to not depend on the @ref Vk library, but doesn't
@ -546,9 +541,6 @@ going to build any of the @ref example-index "examples", you'll need it.
- `MAGNUM_WITH_WINDOWLESSWGLAPPLICATION` --- Build the
@ref Platform::WindowlessWglApplication "WindowlessWglApplication" library.
Requires `MAGNUM_TARGET_GL` to be enabled.
- `MAGNUM_WITH_WINDOWLESSWINDOWSEGLAPPLICATION` --- Build the
@ref Platform::WindowlessWindowsEglApplication "WindowlessWindowsEglApplication"
library. Requires `MAGNUM_TARGET_GL` to be enabled.
None of the context libraries is built by default. Similarly to the application
libraries, they are always built as static. You need them only if you chose to
@ -682,7 +674,24 @@ Options controlling the build:
update your code whenever there's a breaking API change. It's however
recommended to have this option disabled when deploying a final application
as it can result in smaller binaries.
- Additional options are inherited from the @ref CORRADE_BUILD_MULTITHREADED
- `MAGNUM_DISTANCEFIELDCONVERTER_STATIC_PLUGINS`,
`MAGNUM_FONTCONVERTER_STATIC_PLUGINS`,
`MAGNUM_IMAGECONVERTER_STATIC_PLUGINS`,
`MAGNUM_SCENECONVERTER_STATIC_PLUGINS` and
`MAGNUM_SHADERCONVERTER_STATIC_PLUGINS` --- Static plugins to link to the
@ref magnum-distancefieldconverter "magnum-distancefieldconverter",
@ref magnum-fontconverter "magnum-fontconverter",
@ref magnum-imageconverter "magnum-imageconverter",
@ref magnum-sceneconverter "magnum-sceneconverter" and
@ref magnum-shaderconverter "magnum-shaderconverter" utilities,
respectively. Intended for use in scenarios where both
`MAGNUM_BUILD_STATIC` and `MAGNUM_BUILD_PLUGINS_STATIC` is enabled, in
which case these executables don't have a possibility to load dynamic
plugins from a filesystem. Plugins from the Magnum Plugins repository
(and elsewhere) can be linked if it's added as a CMake subproject. Expects
a semicolon-separated list of existing CMake targets, for example `Magnum::AnyImageImporter;MagnumPlugins::StbImageImporter`.
- Additional options are inherited from the @ref CORRADE_BUILD_MULTITHREADED,
@ref CORRADE_BUILD_CPU_RUNTIME_DISPATCH and @ref CORRADE_CPU_USE_IFUNC
options specified when building Corrade.
The features used can be conveniently detected in depending projects both in
@ -739,6 +748,9 @@ compatibility if `MAGNUM_BUILD_DEPRECATED` isn't disabled.
this was used to handle that case. Nowadays please use NDK r19 and newer,
with the unified sysroot layout. Defaults to ``.``. If a relative path is
used, it's relative to `CMAKE_INSTALL_PREFIX`.
- `MAGNUM_TARGET_HEADLESS` --- Alias to `MAGNUM_TARGET_EGL`.
- `MAGNUM_TARGET_DESKTOP_GLES` --- Inverse of `MAGNUM_TARGET_EGL` if
`MAGNUM_TARGET_GLES` is enabled.
Various plugin interfaces search for plugins in locations and order documented
in @ref Corrade::PluginManager::implicitPluginSearchPaths(),

2
doc/changelog-old.dox

@ -744,7 +744,7 @@ a high-level overview.
[mosra/magnum-bootstrap#6](https://github.com/mosra/magnum-bootstrap/pull/6))
- Text input support in @ref Platform::Sdl2Application and
@ref Platform::GlfwApplication (see [mosra/magnum#129](https://github.com/mosra/magnum/issues/129))
- Added @ref Platform::WindowlessWindowsEglApplication and
- Added @cpp Platform::WindowlessWindowsEglApplication @ce and
@ref Platform::WindowlessIosApplication for ANGLE and iOS
- New @ref Platform::WindowlessEglApplication that works on headless NVidia,
Mesa drivers and Emscripten (see [mosra/magnum#133](https://github.com/mosra/magnum/pull/133))

81
doc/changelog.dox

@ -110,9 +110,11 @@ See also:
- Recognizing @webgl_extension{EXT,float_blend} and
@webgl_extension{WEBGL,debug_shaders} WebGL extensions, no implementation
done yet
- Recognizing @gl_extension{KHR,parallel_shader_compile} GL, GLES and
@webgl_extension{KHR,parallel_shader_compile} WebGL extensions, no
implementation done yet
- Implemented @ref GL-AbstractShaderProgram-async "Async shader compilation and linking"
including the @gl_extension{KHR,parallel_shader_compile} GL, GLES and
@webgl_extension{KHR,parallel_shader_compile} WebGL extension (see
[mosra/magnum#534](https://github.com/mosra/magnum/issues/534) and
[mosra/magnum#576](https://github.com/mosra/magnum/pull/576))
- Recognizing ANGLE GLES and WebGL base vertex, base instance and multi-draw
extensions and using them in @ref GL::AbstractShaderProgram::draw(Mesh&)
and @ref GL::AbstractShaderProgram::draw(Containers::ArrayView<const Containers::Reference<MeshView>>):
@ -203,8 +205,7 @@ See also:
- Implemented @relativeref{Platform::WindowlessEglApplication,Configuration::Flag::NoError}
in @ref Platform::WindowlessEglApplication,
@ref Platform::WindowlessGlxApplication,
@ref Platform::WindowlessWglApplication,
@ref Platform::WindowlessWindowsEglApplication and
@ref Platform::WindowlessWglApplication and
@ref Platform::Sdl2Application
@subsubsection changelog-latest-new-scenegraph SceneGraph library
@ -229,6 +230,10 @@ See also:
OpenGL ES 3.0+ and WebGL 2.0, including multi-draw functionality for
massive driver overhead reduction. The @ref shaders overview page was
updated with an introduction the new features.
- All builtin shaders now have opt-in capability of
@ref shaders-async "async compilation and linking" (see
[mosra/magnum534](https://github.com/mosra/magnum/issues/534) and
[mosra/magnum#576](https://github.com/mosra/magnum/pull/576))
- @ref Shaders::FlatGL and @ref Shaders::PhongGL now support texture arrays,
available also in multi-draw and instanced scenarios
- @ref Shaders::FlatGL and @ref Shaders::PhongGL now support object ID
@ -285,6 +290,10 @@ See also:
- New @ref Trade::SkinData class and @ref Trade::AbstractImporter::skin2D() /
@ref Trade::AbstractImporter::skin3D() family of APIs for skin import, as
well as support in @ref Trade::AnySceneImporter "AnySceneImporter"
- The @ref Trade::AbstractSceneConverter plugin interface gained support for
batch conversion of whole scenes --- meshes, hierarchies, materials,
textures, animations and other data; @relativeref{Trade,AnySceneConverter}
is updated to support batch conversion as well
- 1D and 3D image support in @ref Trade::AbstractImageConverter
- @ref Trade::LightData got extended to support light attenuation and range
parameters as well and spot light inner and outer angle
@ -341,6 +350,9 @@ See also:
- @ref Image, @ref CompressedImage, @ref ImageView, @ref CompressedImageView
as well as @ref Trade::ImageData are now able to annotate array, cube map
and cube map array images using @ref ImageFlags
- Removed unnecessary @ref std::string usage from certain
@relativeref{Corrade,Utility::ConfigurationValue} specializations (see
[mosra/magnum#582](https://github.com/mosra/magnum/pull/582))
@subsubsection changelog-latest-changes-debugtools DebugTools library
@ -399,6 +411,7 @@ See also:
complement @relativeref{GL::Framebuffer::InvalidationAttachment,Depth} and
@relativeref{GL::Framebuffer::InvalidationAttachment,Stencil} (see
[mosra/magnum#554](https://github.com/mosra/magnum/pull/554))
- Added @ref GL::Shader::wrap() and @relativeref{GL::Shader,release()}
@subsubsection changelog-latest-changes-math Math library
@ -428,7 +441,7 @@ See also:
- @ref MeshTools::interleavedLayout(const Trade::MeshData&, UnsignedInt, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags),
@ref MeshTools::interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) and
@ref MeshTools::concatenate(Containers::ArrayView<const Containers::Reference<const Trade::MeshData>>, InterleaveFlags)
@ref MeshTools::concatenate(Containers::Iterable<const Trade::MeshData>, InterleaveFlags)
optionally take a @ref MeshTools::InterleaveFlags parameter affecting the
output, in particular whether to preserve the original interleaved layout.
@ -436,9 +449,11 @@ See also:
- Added a @ref Platform::GlfwApplication::setWindowIcon() overload taking a
@ref Corrade::Containers::ArrayView in addition to @ref std::initializer_list
- @ref Platform::GlfwApplication now defaults to EGL on
@ref MAGNUM_TARGET_DESKTOP_GLES "non-desktop"
@ref MAGNUM_TARGET_GLES "GLES builds" (see [mosra/magnum#470](https://github.com/mosra/magnum/pull/470))
- @ref Platform::GlfwApplication now properly uses
@ref MAGNUM_TARGET_EGL "EGL" on @ref MAGNUM_TARGET_GLES "GLES builds" (see
[mosra/magnum#470](https://github.com/mosra/magnum/pull/470))
- @ref Platform::GlfwApplication and @ref Platform::Sdl2Application can now
work with EGL on desktop GL as well if @ref MAGNUM_TARGET_EGL is enabled
- On Emscripten, @ref Platform::EmscriptenApplication used an internal
allocation function, which changed signature in 2.0.5 and caused runtime
failures when `-s ASSERTIONS` was enabled. A public stable API is now used
@ -481,10 +496,10 @@ See also:
- Added a `--bounds` option to @ref magnum-sceneconverter "magnum-sceneconverter",
showing data ranges of known attributes
- @ref magnum-sceneconverter "magnum-sceneconverter" now has separate
`--info-animations`, `--info-images`, `--info-lights`, `--info-materials`,
`--info-meshes`, `--info-skins` and `--info-textures` for printing
information just about particular data type, with `--info` being a shortcut
for all specified together
`--info-animations`, `--info-images`, `--info-lights`, `--info-cameras`,
`--info-materials`, `--info-meshes`, `--info-skins` and `--info-textures`
for printing information just about particular data type, with `--info`
being a shortcut for all specified together
@subsubsection changelog-latest-changes-shaders Shaders library
@ -520,6 +535,10 @@ See also:
@relativeref{Trade::AbstractImageConverter,doConvertToData()}, for example
when the implementation only neeeds to do a format detection based on file
extension
- New @ref Trade::AbstractImageConverter::extension() and
@relativeref{Trade::AbstractImageConverter,mimeType()} interfaces to get
a file extension and MIME type corresponding to a file format produced by
a particular plugin.
- Recognizing BMP and TIFF file header magic in @relativeref{Trade,AnyImageImporter}
- Recognizing ASTC and WebP files and data in
@relativeref{Trade,AnyImageImporter}
@ -528,6 +547,7 @@ See also:
[mosra/magnum#529](https://github.com/mosra/magnum/pull/529))
- Recognizing KTX2 for (compressed) 1D/2D/3D and multi-level 1D/2D/3D images
in @relativeref{Trade,AnyImageConverter}
- Recognizing glTF files in @relativeref{Trade,AnySceneConverter}
- Recognizing 3MF files in @relativeref{Trade,AnySceneImporter}
- Recognizing OpenVBD files in @relativeref{Trade,AnyImageImporter} and
@relativeref{Trade,AnyImageConverter}
@ -652,6 +672,13 @@ See also:
[mosra/magnum#570](https://github.com/mosra/magnum/pull/570).
- Fixed wrong `.gitattributes` option for LF line endings in MSYS PKGBUILDs
(see [mosra/magnum#574](https://github.com/mosra/magnum/issues/574))
- Added `MAGNUM_DISTANCEFIELDCONVERTER_STATIC_PLUGINS`,
`MAGNUM_FONTCONVERTER_STATIC_PLUGINS`,
`MAGNUM_IMAGECONVERTER_STATIC_PLUGINS`,
`MAGNUM_SCENECONVERTER_STATIC_PLUGINS` and
`MAGNUM_SHADERCONVERTER_STATIC_PLUGINS` CMake options for linking static
plugins to the command-line utilities. See @ref building-features for more
information.
@subsection changelog-latest-bugfixes Bug fixes
@ -750,6 +777,13 @@ See also:
@subsection changelog-latest-deprecated Deprecated APIs
- The (mutually exclusive) @ref MAGNUM_TARGET_HEADLESS and
@ref MAGNUM_TARGET_DESKTOP_GLES options, CMake variables and preprocessor
variables are deprecated in favor of @ref MAGNUM_TARGET_EGL. It's enabled
by default on GLES and disabled by default on desktop GL --- disabling it
on GLES will force creation of a GLES context using the GLX / WGL libraries
(if available); enabling it on desktop GL will allow running windowless
applications on headless machines.
- All @ref building-features "CMake build options" are now prefixed with
`MAGNUM_`. For backwards compatibility, unless @ref MAGNUM_BUILD_DEPRECATED
is disabled and unless a prefixed option is already set during the initial
@ -803,6 +837,16 @@ See also:
@ref DebugTools::FrameProfilerGL. The new name plays better with IDE
autocompletion and makes the GL-specific class appear next to the
API-independent base in alphabetically sorted lists.
- List-taking @cpp GL::Shader::compile() @ce and
@cpp GL::AbstractShaderProgram::link() @ce functions are deprecated in
favor of new @ref GL::Shader::submitCompile(),
@relativeref{GL::Shader,checkCompile()},
@ref GL::AbstractShaderProgram::submitLink() and
@relativeref{GL::AbstractShaderProgram,checkLink()} APIs. These were
originally meant to make use of parallel shader compilation, but in
practice that meant compiling at most two or three shaders at once. The new
API allows for much larger parallelism as well as an ability to query
completion status.
- @cpp Shaders::DistanceFieldVector @ce, @cpp Shaders::Flat @ce,
@cpp Shaders::Generic @ce, @cpp Shaders::MeshVisualizer2D @ce,
@cpp Shaders::MeshVisualizer3D @ce, @cpp Shaders::Phong @ce,
@ -1055,6 +1099,12 @@ See also:
@ref Math::RectangularMatrix::data() are no longer @cpp constexpr @ce in
order to make them return a reference to a fixed-size array instead of a
pointer, which was deemed a more useful property.
- @cpp Platform::WindowlessWindowsEglApplication @ce is now merged into
@ref Platform::WindowlessEglApplication. Since its use case was rather rare
(windowless applications on ANGLE on Windows) and it wasn't even built in
any packages, it's completely removed without providing any backwards
compatibility --- switch to @ref Platform::WindowlessEglApplication
instead.
- @ref SceneGraph::Object::addChild() no longer requires the type constructor
to have the last parameter a parent object object pointer, as that was
quite limiting. Instead it's calling @ref SceneGraph::Object::setParent()
@ -1343,7 +1393,8 @@ Released 2020-06-27, tagged as
@ref Platform::WindowlessEglApplication,
@ref Platform::WindowlessGlxApplication,
@ref Platform::WindowlessWglApplication and
@ref Platform::WindowlessWindowsEglApplication (see [mosra/magnum#433](https://github.com/mosra/magnum/pull/433)
@cpp Platform::WindowlessWindowsEglApplication @ce (see
[mosra/magnum#433](https://github.com/mosra/magnum/pull/433)
and [mosra/magnum#437](https://github.com/mosra/magnum/pull/437))
- CUDA device selection in @ref Platform::WindowlessEglApplication (see
[mosra/magnum#449](https://github.com/mosra/magnum/pull/449))
@ -2389,7 +2440,7 @@ Released 2019-10-24, tagged as
@ref Platform::Sdl2Application::exitEvent() "exitEvent()"
- On OpenGL ES builds, @ref Platform::Sdl2Application now tells SDL whether
to use a system GL driver or a dedicated GLES driver based on whether
@ref MAGNUM_TARGET_DESKTOP_GLES is defined. This allows for a smoother
@cpp MAGNUM_TARGET_DESKTOP_GLES @ce is defined. This allows for a smoother
experience for example when using ANGLE on Windows. See
@ref Platform-Sdl2Application-usage-gles for more information.
- Replaced uses of `Pointer_stringify()` in @ref Platform::Sdl2Application

11
doc/cmake.dox

@ -217,7 +217,6 @@ Platform namespace is split into more components:
- `WindowlessGlxApplication` --- @ref Platform::WindowlessGlxApplication "WindowlessGlxApplication"
- `WindowlessIosApplication` --- @ref Platform::WindowlessIosApplication "WindowlessIosApplication"
- `WindowlessWglApplication` --- @ref Platform::WindowlessWglApplication "WindowlessWglApplication"
- `WindowlessWindowsEglApplication` --- @ref Platform::WindowlessWindowsEglApplication "WindowlessWindowsEglApplication"
For manual context creation (without application wrappers) there are also
platform-specific context libraries (see @ref platform-custom for more
@ -328,11 +327,9 @@ are also available as preprocessor variables if including
- `MAGNUM_TARGET_GLES` --- Defined if compiled for OpenGL ES
- `MAGNUM_TARGET_GLES2` --- Defined if compiled for OpenGL ES 2.0
- `MAGNUM_TARGET_GLES3` --- Defined if compiled for OpenGL ES 3.0
- `MAGNUM_TARGET_DESKTOP_GLES` --- Defined if compiled with OpenGL ES
emulation on desktop OpenGL
- `MAGNUM_TARGET_WEBGL` --- Defined if compiled for WebGL
- `MAGNUM_TARGET_HEADLESS` --- Defined if compiled for headless machines. See
@ref MAGNUM_TARGET_HEADLESS documentation for more information.
- `MAGNUM_TARGET_EGL` --- Defined if compiled for EGL instead of a
platform-specific OpenGL support library such as CGL, EAGL, GLX or WGL.
- `MAGNUM_TARGET_VK` --- Defined if compiled with Vulkan interoperability
enabled
@ -342,6 +339,10 @@ release:
- `MAGNUM_BUILD_MULTITHREADED` --- Alias to `CORRADE_BUILD_MULTITHREADED`.
Use @ref CORRADE_BUILD_MULTITHREADED instead.
- `MAGNUM_TARGET_HEADLESS` --- Alias to `MAGNUM_TARGET_EGL`, unless on iOS,
Android, Emscripten or Windows RT. Use @ref MAGNUM_TARGET_EGL instead.
- `MAGNUM_TARGET_DESKTOP_GLES` --- Defined if compiled for OpenGL ES but
GLX / WGL is used instead of EGL. Use @ref MAGNUM_TARGET_EGL instead.
Corrade library provides also its own set of CMake macros and variables, see
@ref corrade-cmake "its documentation" for more information.

4
doc/credits.dox

@ -147,7 +147,7 @@ Are the below lists missing your name or something's wrong?
- **Hilario Pérez Corona** ([\@hpcorona](https://github.com/hpcorona)) ---
improvements to @cb{.cmake} android_create_apk() @ce
- **Hugo Amnov** ([\@hugoam](https://github.com/hugoam)) --- buildsystem
improvements
improvements and STL usage cleanup
- **Ivan P.** ([\@uzername](https://github.com/uzername)) --- documentation
improvements
- **Ivan Sanz Carasa** ([\@isc30](https://github.com/isc30)) --- buildsystem
@ -228,6 +228,8 @@ Are the below lists missing your name or something's wrong?
--- OpenGL ES compatibility improvements
- **Travis Watkins** ([\@amaranth](https://github.com/amaranth)) --- support
for windowless applications under macOS
- **Vladislav** ([\@dranikpg](https://github.com/dranikpg)) --- Async @ref GL
shader compilation
*/
}

10
doc/file-formats.dox

@ -583,6 +583,16 @@ Derived from @ref Trade::AbstractSceneConverter.
<td class="m-text-center">@m_span{m-text m-dim} none @m_endspan </td>
<td class="m-text-center"></td>
</tr>
<tr><td colspan="6"></td></tr>
<tr>
<th>glTF (`*.gltf`, `*.glb`)</th>
<td>`GltfSceneConverter`</td>
<td>@relativeref{Trade,GltfSceneConverter}</td>
<td class="m-text-center m-warning">@ref Trade-GltfSceneConverter-behavior "some"</td>
<td class="m-text-center">@m_span{m-text m-dim} none @m_endspan </td>
<td class="m-text-center"></td>
</tr>
</table>
@endparblock

6
doc/namespaces.dox

@ -109,8 +109,10 @@ more information.
#include <MagnumMath.hpp>
@endcode
@par
In addition, contents of the @ref GlmIntegration and @ref EigenIntegration
libraries are included as well --- opt-in by specifying either
If you need the deinlined symbols to be exported from a shared library,
@cpp #define MAGNUM_EXPORT @ce as appropriate. In addition, contents of the
@ref GlmIntegration and @ref EigenIntegration libraries are included as
well --- opt-in by specifying either
@cpp #define MAGNUM_MATH_GLM_INTEGRATION @ce or
@cpp #define MAGNUM_MATH_EIGEN_INTEGRATION @ce before including the file.
Including it multiple times with different macros defined works as well.

6
doc/opengl-support.dox

@ -297,7 +297,7 @@ Extension | Status
@gl_extension{KHR,blend_equation_advanced} | done
@gl_extension2{KHR,blend_equation_advanced_coherent,KHR_blend_equation_advanced} | done
@gl_extension{KHR,texture_compression_astc_sliced_3d} | done (nothing to do)
@gl_extension{KHR,parallel_shader_compile} | |
@gl_extension{KHR,parallel_shader_compile} | missing thread count setting
@subsection opengl-support-extensions-vendor Vendor OpenGL extensions
@ -490,7 +490,7 @@ Extension | Status
@gl_extension{KHR,context_flush_control} | |
@gl_extension{KHR,no_error} | done
@gl_extension{KHR,texture_compression_astc_sliced_3d} | done (nothing to do)
@gl_extension{KHR,parallel_shader_compile} | |
@gl_extension{KHR,parallel_shader_compile} | missing thread count setting
@gl_extension2{NV,read_buffer_front,NV_read_buffer} | done
@gl_extension2{NV,read_depth,NV_read_depth_stencil} | done
@gl_extension2{NV,read_stencil,NV_read_depth_stencil} | done
@ -566,7 +566,7 @@ Extension | Status
@webgl_extension{EXT,clip_cull_distance} | done
@webgl_extension{EXT,texture_norm16} | done
@webgl_extension{EXT,draw_buffers_indexed} | done
@webgl_extension{KHR,parallel_shader_compile} | |
@webgl_extension{KHR,parallel_shader_compile} | done
@webgl_extension{OES,texture_float_linear} | done
@webgl_extension{OVR,multiview2} | |
@webgl_extension{WEBGL,lose_context} | |

4
doc/opengl-wrapping.dox

@ -61,8 +61,8 @@ Magnum object instance using @cpp wrap() @ce:
@snippet MagnumGL.cpp opengl-wrapping-transfer
The @cpp wrap() @ce and @cpp release() @ce functions are available for all
OpenGL classes except for @ref GL::Shader, instances of which are rather
short-lived and thus wrapping external instances makes less sense.
OpenGL classes except for @ref GL::AbstractShaderProgram, where the desired
usage via subclassing isn't really suited for wrapping external objects.
@attention
Note that interaction with third-party OpenGL code as shown above usually

28
doc/shaders.dox

@ -287,6 +287,34 @@ While the primary use case of texture arrays is with uniform buffers and
multidraw, they work in the classic uniform workflow as well --- use
@relativeref{Shaders::PhongGL,setTextureLayer()} there instead.
@section shaders-async Async shader compilation and linking
By default, shaders are compiled and linked directly in their constructor.
While that's convenient and easy to use, applications using heavier shaders,
many shader combinations or running on platforms that translate GLSL to other
APIs such as HLSL or MSL, may spend a significant portion of their startup
time just on shader compilation and linking.
To mitigate this problem, shaders can be compiled in an asynchronous way.
Depending on the driver and system, this can mean that for example eight
shaders get compiled at the same time in eight parallel threads, instead of
sequentially one after another. To achieve such parallelism, the construction
needs to be broken into two parts --- first submitting compilation of all
shaders using @ref Shaders::FlatGL::compile() "Shaders::*GL::compile()",
forming temporary @ref Shaders::FlatGL::CompileState "Shaders::*GL::CompileState"
instances, then possibly doing other work until it's completed, and finally
constructing final shader instances out of the temporary state:
@snippet MagnumShaders-gl.cpp shaders-async
The above code will work correctly also on drivers that implement async
compilation partially or not at all --- there
@ref GL::AbstractShaderProgram::isLinkFinished() will implicitly return
@cpp true @ce, and the final construction will stall if it happens before a
(potentially async) compilation is finished. See also the
@ref GL-AbstractShaderProgram-async "GL::AbstractShaderProgram documentation"
for more information.
@section shaders-generic Generic vertex attributes and framebuffer attachments
Many shaders share the same vertex attribute definitions, such as positions,

394
doc/snippets/MagnumGL.cpp

@ -25,6 +25,7 @@
#include <tuple> /* for std::tie() :( */
#include <Corrade/Containers/ArrayViewStl.h>
#include <Corrade/Containers/Iterable.h>
#include <Corrade/Containers/Reference.h>
#include <Corrade/TestSuite/Tester.h>
@ -90,165 +91,6 @@
using namespace Magnum;
using namespace Magnum::Math::Literals;
int main() {
#ifndef MAGNUM_TARGET_GLES2
{
ImageView2D diffuse{PixelFormat::RGBA8Unorm, {}};
ImageView2D specular{PixelFormat::RGBA8Unorm, {}};
ImageView2D bump{PixelFormat::RGBA8Unorm, {}};
/* [method-chaining-texture] */
GL::Texture2D carDiffuseTexture, carSpecularTexture, carBumpTexture;
carDiffuseTexture.setStorage(5, GL::TextureFormat::SRGB8, {256, 256});
carSpecularTexture.setStorage(3, GL::TextureFormat::R8, {256, 256});
carBumpTexture.setStorage(5, GL::TextureFormat::RGB8, {256, 256});
carDiffuseTexture.setSubImage(0, {}, diffuse);
carSpecularTexture.setSubImage(0, {}, specular);
carBumpTexture.setSubImage(0, {}, bump);
carDiffuseTexture.generateMipmap();
carSpecularTexture.generateMipmap();
carBumpTexture.generateMipmap();
/* [method-chaining-texture] */
/* [method-chaining-texture-chained] */
carDiffuseTexture.setStorage(5, GL::TextureFormat::SRGB8, {256, 256})
.setSubImage(0, {}, diffuse)
.generateMipmap();
carSpecularTexture.setStorage(3, GL::TextureFormat::R8, {256, 256})
.setSubImage(0, {}, diffuse)
.generateMipmap();
carBumpTexture.setStorage(5, GL::TextureFormat::RGB8, {256, 256})
.setSubImage(0, {}, bump)
.generateMipmap();
/* [method-chaining-texture-chained] */
}
#endif
{
struct Foo {
void setSomeBuffer(GLuint) {}
GLuint someBuffer() { return {}; }
} externalLib;
char someData[1];
/* [opengl-wrapping-transfer] */
/* Transferring the instance to external library */
{
GL::Buffer buffer;
buffer.setData(someData, GL::BufferUsage::StaticDraw);
GLuint id = buffer.release();
externalLib.setSomeBuffer(id); /* The library is responsible for deletion */
}
/* Acquiring an instance from external library */
{
GLuint id = externalLib.someBuffer();
GL::Buffer buffer = GL::Buffer::wrap(id, GL::ObjectFlag::DeleteOnDestruction);
/* The buffer instance now handles deletion */
}
/* [opengl-wrapping-transfer] */
}
#ifndef MAGNUM_TARGET_GLES
{
struct: GL::AbstractShaderProgram {} someShader;
/* [opengl-wrapping-state] */
GL::Buffer buffer;
GL::Mesh mesh;
// ...
someShader.draw(mesh);
{
/* Entering a section with 3rd-party OpenGL code -- clean up all state that
could cause accidental modifications of our objects from outside */
GL::Context::current().resetState(GL::Context::State::EnterExternal);
/* Raw OpenGL calls */
glBindBuffer(GL_ARRAY_BUFFER, buffer.id());
glBufferStorage(GL_ARRAY_BUFFER, 32768, nullptr, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
// ...
/* Exiting a section with 3rd-party OpenGL code -- reset our state tracker */
GL::Context::current().resetState(GL::Context::State::ExitExternal);
}
/* Use the buffer through Magnum again */
auto data = buffer.map(0, 32768, GL::Buffer::MapFlag::Read|GL::Buffer::MapFlag::Write);
// ...
/* [opengl-wrapping-state] */
static_cast<void>(data);
}
#endif
#ifndef MAGNUM_TARGET_GLES
{
/* [opengl-wrapping-extensions] */
GL::TextureFormat format;
if(GL::Context::current().isExtensionSupported<GL::Extensions::ARB::depth_buffer_float>())
format = GL::TextureFormat::DepthComponent32F;
else
format = GL::TextureFormat::DepthComponent24;
/* [opengl-wrapping-extensions] */
static_cast<void>(format);
}
#endif
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
{
/* [opengl-wrapping-dsa] */
GL::Texture2D texture;
/* - on OpenGL 4.5+/ARB_direct_state_access this calls glTextureStorage2D()
- on OpenGL 4.2+/ARB_texture_storage and OpenGL ES 3.0+ calls glTexStorage2D()
- on OpenGL ES 2.0 with EXT_texture_storage calls glTexStorage2DEXT()
- otherwise emulated using a sequence of four glTexImage2D() calls */
texture.setStorage(4, GL::TextureFormat::RGBA8, {256, 256});
/* [opengl-wrapping-dsa] */
}
#endif
{
/* [portability-targets] */
#ifndef MAGNUM_TARGET_GLES
GL::Renderer::setPolygonMode(GL::Renderer::PolygonMode::Line);
// draw mesh as wireframe...
#else
// use different mesh, as polygon mode is not supported in OpenGL ES...
#endif
/* [portability-targets] */
}
#ifndef MAGNUM_TARGET_GLES
{
/* [portability-extensions] */
if(GL::Context::current().isExtensionSupported<GL::Extensions::ARB::geometry_shader4>()) {
// draw mesh with wireframe on top in one pass using geometry shader...
} else {
// draw underlying mesh...
GL::Renderer::setPolygonMode(GL::Renderer::PolygonMode::Line);
// draw mesh as wirefreame in second pass...
}
/* [portability-extensions] */
}
{
/* [portability-extension-assert] */
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::geometry_shader4);
// just use geometry shader and don't care about old hardware
/* [portability-extension-assert] */
}
{
/* [portability-shaders] */
// MyShader.cpp
GL::Version version = GL::Context::current().supportedVersion({
GL::Version::GL430, GL::Version::GL330, GL::Version::GL210});
GL::Shader vert{version, GL::Shader::Type::Vertex};
vert.addFile("MyShader.vert");
/* [portability-shaders] */
}
#endif
#ifndef MAGNUM_TARGET_GLES
struct MyShader: GL::AbstractShaderProgram {
/* [AbstractShaderProgram-input-attributes] */
@ -305,8 +147,8 @@ explicit MyShader() {
vert.addFile("MyShader.vert");
frag.addFile("MyShader.frag");
/* Invoke parallel compilation for best performance */
CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag}));
/* Compile them */
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
/* Attach the shaders */
attachShaders({vert, frag});
@ -416,13 +258,225 @@ setTransformFeedbackOutputs({
};
#endif
#ifndef MAGNUM_TARGET_GLES
namespace Foo {
struct MyShader: GL::AbstractShaderProgram {
class CompileState;
MyShader(NoInitT);
MyShader(CompileState&&);
MyShader(int);
static CompileState compile(int);
};
/* [AbstractShaderProgram-async] */
class MyShader::CompileState: public MyShader {
friend MyShader;
explicit CompileState(MyShader&& shader, GL::Shader&& vert, GL::Shader&& frag):
MyShader{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)} {}
GL::Shader _vert, _frag;
};
MyShader::CompileState MyShader::compile(DOXYGEN_ELLIPSIS(int)) {
GL::Shader vert{GL::Version::GL430, GL::Shader::Type::Vertex};
GL::Shader frag{GL::Version::GL430, GL::Shader::Type::Fragment};
DOXYGEN_ELLIPSIS()
vert.submitCompile();
frag.submitCompile();
MyShader out{NoInit};
DOXYGEN_ELLIPSIS()
out.attachShaders({vert, frag});
out.submitLink();
return CompileState{std::move(out), std::move(vert), std::move(frag)};
}
MyShader::MyShader(NoInitT) {}
MyShader::MyShader(CompileState&& state):
MyShader{static_cast<MyShader&&>(std::move(state))}
{
CORRADE_INTERNAL_ASSERT_OUTPUT(checkLink({state._vert, state._frag}));
DOXYGEN_ELLIPSIS()
}
MyShader::MyShader(DOXYGEN_ELLIPSIS(int a)): MyShader{compile(DOXYGEN_ELLIPSIS(a))} {}
/* [AbstractShaderProgram-async] */
}
#endif
int main() {
#ifndef MAGNUM_TARGET_GLES2
{
ImageView2D diffuse{PixelFormat::RGBA8Unorm, {}};
ImageView2D specular{PixelFormat::RGBA8Unorm, {}};
ImageView2D bump{PixelFormat::RGBA8Unorm, {}};
/* [method-chaining-texture] */
GL::Texture2D carDiffuseTexture, carSpecularTexture, carBumpTexture;
carDiffuseTexture.setStorage(5, GL::TextureFormat::SRGB8, {256, 256});
carSpecularTexture.setStorage(3, GL::TextureFormat::R8, {256, 256});
carBumpTexture.setStorage(5, GL::TextureFormat::RGB8, {256, 256});
carDiffuseTexture.setSubImage(0, {}, diffuse);
carSpecularTexture.setSubImage(0, {}, specular);
carBumpTexture.setSubImage(0, {}, bump);
carDiffuseTexture.generateMipmap();
carSpecularTexture.generateMipmap();
carBumpTexture.generateMipmap();
/* [method-chaining-texture] */
/* [method-chaining-texture-chained] */
carDiffuseTexture.setStorage(5, GL::TextureFormat::SRGB8, {256, 256})
.setSubImage(0, {}, diffuse)
.generateMipmap();
carSpecularTexture.setStorage(3, GL::TextureFormat::R8, {256, 256})
.setSubImage(0, {}, diffuse)
.generateMipmap();
carBumpTexture.setStorage(5, GL::TextureFormat::RGB8, {256, 256})
.setSubImage(0, {}, bump)
.generateMipmap();
/* [method-chaining-texture-chained] */
}
#endif
{
struct Foo {
void setSomeBuffer(GLuint) {}
GLuint someBuffer() { return {}; }
} externalLib;
char someData[1];
/* [opengl-wrapping-transfer] */
/* Transferring the instance to external library */
{
GL::Buffer buffer;
buffer.setData(someData, GL::BufferUsage::StaticDraw);
GLuint id = buffer.release();
externalLib.setSomeBuffer(id); /* The library is responsible for deletion */
}
/* Acquiring an instance from external library */
{
GLuint id = externalLib.someBuffer();
GL::Buffer buffer = GL::Buffer::wrap(id, GL::ObjectFlag::DeleteOnDestruction);
/* The buffer instance now handles deletion */
}
/* [opengl-wrapping-transfer] */
}
#ifndef MAGNUM_TARGET_GLES
{
struct: GL::AbstractShaderProgram {} someShader;
/* [opengl-wrapping-state] */
GL::Buffer buffer;
GL::Mesh mesh;
// ...
someShader.draw(mesh);
{
/* Entering a section with 3rd-party OpenGL code -- clean up all state that
could cause accidental modifications of our objects from outside */
GL::Context::current().resetState(GL::Context::State::EnterExternal);
/* Raw OpenGL calls */
glBindBuffer(GL_ARRAY_BUFFER, buffer.id());
glBufferStorage(GL_ARRAY_BUFFER, 32768, nullptr, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
// ...
/* Exiting a section with 3rd-party OpenGL code -- reset our state tracker */
GL::Context::current().resetState(GL::Context::State::ExitExternal);
}
/* Use the buffer through Magnum again */
auto data = buffer.map(0, 32768, GL::Buffer::MapFlag::Read|GL::Buffer::MapFlag::Write);
// ...
/* [opengl-wrapping-state] */
static_cast<void>(data);
}
#endif
#ifndef MAGNUM_TARGET_GLES
{
/* [opengl-wrapping-extensions] */
GL::TextureFormat format;
if(GL::Context::current().isExtensionSupported<GL::Extensions::ARB::depth_buffer_float>())
format = GL::TextureFormat::DepthComponent32F;
else
format = GL::TextureFormat::DepthComponent24;
/* [opengl-wrapping-extensions] */
static_cast<void>(format);
}
#endif
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
{
/* [opengl-wrapping-dsa] */
GL::Texture2D texture;
/* - on OpenGL 4.5+/ARB_direct_state_access this calls glTextureStorage2D()
- on OpenGL 4.2+/ARB_texture_storage and OpenGL ES 3.0+ calls glTexStorage2D()
- on OpenGL ES 2.0 with EXT_texture_storage calls glTexStorage2DEXT()
- otherwise emulated using a sequence of four glTexImage2D() calls */
texture.setStorage(4, GL::TextureFormat::RGBA8, {256, 256});
/* [opengl-wrapping-dsa] */
}
#endif
{
/* [portability-targets] */
#ifndef MAGNUM_TARGET_GLES
GL::Renderer::setPolygonMode(GL::Renderer::PolygonMode::Line);
// draw mesh as wireframe...
#else
// use different mesh, as polygon mode is not supported in OpenGL ES...
#endif
/* [portability-targets] */
}
#ifndef MAGNUM_TARGET_GLES
{
/* [portability-extensions] */
if(GL::Context::current().isExtensionSupported<GL::Extensions::ARB::geometry_shader4>()) {
// draw mesh with wireframe on top in one pass using geometry shader...
} else {
// draw underlying mesh...
GL::Renderer::setPolygonMode(GL::Renderer::PolygonMode::Line);
// draw mesh as wirefreame in second pass...
}
/* [portability-extensions] */
}
{
/* [portability-extension-assert] */
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::geometry_shader4);
// just use geometry shader and don't care about old hardware
/* [portability-extension-assert] */
}
{
/* [portability-shaders] */
// MyShader.cpp
GL::Version version = GL::Context::current().supportedVersion({
GL::Version::GL430, GL::Version::GL330, GL::Version::GL210});
GL::Shader vert{version, GL::Shader::Type::Vertex};
vert.addFile("MyShader.vert");
/* [portability-shaders] */
}
#endif
#ifndef MAGNUM_TARGET_GLES
{
MyShader shader;
GL::Mesh mesh;
Matrix4 transformation, projection;
GL::Texture2D diffuseTexture, specularTexture;
/* [AbstractShaderProgram-rendering] */
MyShader shader;
shader.setTransformationMatrix(transformation)
.setProjectionMatrix(projection)
.bindDiffuseTexture(diffuseTexture)
@ -432,6 +486,22 @@ shader.setTransformationMatrix(transformation)
}
#endif
#ifndef MAGNUM_TARGET_GLES
{
using Foo::MyShader;
/* [AbstractShaderProgram-async-usage] */
MyShader::CompileState state = MyShader::compile(DOXYGEN_ELLIPSIS(0));
// Other shaders to compile....
while(!state.isLinkFinished()) {
// Do other work...
}
MyShader shader{std::move(state)};
/* [AbstractShaderProgram-async-usage] */
}
#endif
{
GL::Framebuffer framebuffer{{}};
/* [AbstractFramebuffer-read1] */

6
doc/snippets/MagnumMath.cpp

@ -994,8 +994,8 @@ static_cast<void>(bClamped);
using namespace Math::Literals;
Half a = 3.14159_h;
Debug{} << a; // Prints 3.14159
Debug{} << Float(a); // Prints 3.14159
Debug{} << a; // Prints 3.141
Debug{} << Float(a); // Prints 3.14062
Debug{} << UnsignedShort(a); // Prints 25675
/* [Half-usage] */
}
@ -1004,7 +1004,7 @@ Debug{} << UnsignedShort(a); // Prints 25675
/* [Half-usage-vector] */
Vector3h a{3.14159_h, -1.4142_h, 1.618_h};
Vector3 b{a}; // converts to 32-bit floats
Debug{} << a; // prints {3.14159, -1.4142, 1.618}
Debug{} << a; // prints {3.141, -1.414, 1.618}
Debug{} << Vector3us{a}; // prints {16968, 48552, 15993}
/* [Half-usage-vector] */
}

23
doc/snippets/MagnumShaders-gl.cpp

@ -284,7 +284,7 @@ ImageView2D coneDiffuse{DOXYGEN_ELLIPSIS({}, {})}, cubeDiffuse{DOXYGEN_ELLIPSIS(
GL::Texture2DArray diffuseTexture;
diffuseTexture
DOXYGEN_ELLIPSIS()
/* Assuming all iamges have the same format and size */
/* Assuming all images have the same format and size */
.setStorage(1, GL::textureFormat(coneDiffuse.format()),
{coneDiffuse.size(), 3})
.setSubImage(0, {}, coneDiffuse)
@ -341,6 +341,27 @@ shader
/* [shaders-meshvisualizer] */
}
{
/* [shaders-async] */
Shaders::FlatGL3D::CompileState flatState =
Shaders::FlatGL3D::compile();
Shaders::FlatGL3D::CompileState flatTexturedState =
Shaders::FlatGL3D::compile(Shaders::FlatGL3D::Flag::Textured);
Shaders::MeshVisualizerGL3D::CompileState meshVisualizerState =
Shaders::MeshVisualizerGL3D::compile(DOXYGEN_ELLIPSIS(Shaders::MeshVisualizerGL3D::Flag::Wireframe));
while(!flatState.isLinkFinished() ||
!flatTexturedState.isLinkFinished() ||
!meshVisualizerState.isLinkFinished()) {
// Do other work ...
}
Shaders::FlatGL3D flat{std::move(flatState)};
Shaders::FlatGL3D flatTextured{std::move(flatTexturedState)};
Shaders::MeshVisualizerGL3D meshVisualizer{std::move(meshVisualizerState)};
/* [shaders-async] */
}
/* internal compiler error: in gimplify_init_constructor, at gimplify.c:4271
on GCC 4.8 in the [60] array */
#if !defined(CORRADE_TARGET_GCC) || defined(CORRADE_TARGET_CLANG) || __GNUC__ >= 5

71
modules/FindMagnum.cmake

@ -78,7 +78,6 @@
# WindowlessGlxApplication - Windowless GLX application
# WindowlessIosApplication - Windowless iOS application
# WindowlessWglApplication - Windowless WGL application
# WindowlessWindowsEglApplication - Windowless Windows/EGL application
# CglContext - CGL context
# EglContext - EGL context
# GlxContext - GLX context
@ -137,10 +136,9 @@
# MAGNUM_TARGET_GLES - Defined if compiled for OpenGL ES
# MAGNUM_TARGET_GLES2 - Defined if compiled for OpenGL ES 2.0
# MAGNUM_TARGET_GLES3 - Defined if compiled for OpenGL ES 3.0
# MAGNUM_TARGET_DESKTOP_GLES - Defined if compiled with OpenGL ES
# emulation on desktop OpenGL
# MAGNUM_TARGET_WEBGL - Defined if compiled for WebGL
# MAGNUM_TARGET_HEADLESS - Defined if compiled for headless machines
# MAGNUM_TARGET_EGL - Defined if compiled for EGL instead of a
# platform-specific OpenGL support library like CGL, EAGL, GLX or WGL
# MAGNUM_TARGET_VK - Defined if compiled with Vulkan interop
#
# The following variables are provided for backwards compatibility purposes
@ -149,6 +147,10 @@
#
# MAGNUM_BUILD_MULTITHREADED - Alias to CORRADE_BUILD_MULTITHREADED. Use
# CORRADE_BUILD_MULTITHREADED instead.
# MAGNUM_TARGET_HEADLESS - Alias to MAGNUM_TARGET_EGL, unless on iOS,
# Android, Emscripten or Windows RT. Use MAGNUM_TARGET_EGL instead.
# MAGNUM_TARGET_DESKTOP_GLES` - Defined if compiled for OpenGL ES but
# GLX / WGL is used instead of EGL. Use MAGNUM_TARGET_EGL instead.
#
# Additionally these variables are defined for internal usage:
#
@ -269,9 +271,8 @@ set(_magnumFlags
TARGET_GLES
TARGET_GLES2
TARGET_GLES3
TARGET_DESKTOP_GLES
TARGET_WEBGL
TARGET_HEADLESS
TARGET_EGL
TARGET_VK)
foreach(_magnumFlag ${_magnumFlags})
list(FIND _magnumConfigure "#define MAGNUM_${_magnumFlag}" _magnum_${_magnumFlag})
@ -280,10 +281,21 @@ foreach(_magnumFlag ${_magnumFlags})
endif()
endforeach()
# For compatibility only, to be removed at some point
if(MAGNUM_BUILD_DEPRECATED AND CORRADE_BUILD_MULTITHREADED)
# For compatibility only, to be removed at some point. Refer to
# src/Magnum/configure.h.cmake for the decision logic here.
if(MAGNUM_BUILD_DEPRECATED)
if(CORRADE_BUILD_MULTITHREADED)
set(MAGNUM_BUILD_MULTITHREADED 1)
endif()
if(NOT CORRADE_TARGET_IOS AND NOT CORRADE_TARGET_ANDROID AND NOT CORRADE_TARGET_EMSCRIPTEN AND NOT CORRADE_TARGET_WINDOWS_RT)
if(NOT MAGNUM_TARGET_GLES AND MAGNUM_TARGET_EGL)
set(MAGNUM_TARGET_HEADLESS 1)
endif()
if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_EGL)
set(MAGNUM_TARGET_DESKTOP_GLES 1)
endif()
endif()
endif()
# OpenGL library preference. Prefer to use GLVND, since that's the better
# approach nowadays, but allow the users to override it from outside in case
@ -395,7 +407,7 @@ if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE)
list(APPEND _MAGNUM_LIBRARY_COMPONENTS GlxApplication XEglApplication WindowlessGlxApplication GlxContext)
endif()
if(CORRADE_TARGET_WINDOWS)
list(APPEND _MAGNUM_LIBRARY_COMPONENTS WindowlessWglApplication WglContext WindowlessWindowsEglApplication)
list(APPEND _MAGNUM_LIBRARY_COMPONENTS WindowlessWglApplication WglContext)
endif()
if(CORRADE_TARGET_UNIX OR CORRADE_TARGET_WINDOWS)
list(APPEND _MAGNUM_EXECUTABLE_COMPONENTS fontconverter distancefieldconverter)
@ -426,24 +438,16 @@ if(MAGNUM_TARGET_GL)
endif()
set(_MAGNUM_OpenGLTester_DEPENDENCIES GL)
if(MAGNUM_TARGET_HEADLESS OR CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID)
if(MAGNUM_TARGET_EGL)
list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessEglApplication)
elseif(CORRADE_TARGET_IOS)
list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessIosApplication)
elseif(CORRADE_TARGET_APPLE AND NOT MAGNUM_TARGET_GLES)
elseif(CORRADE_TARGET_APPLE)
list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessCglApplication)
elseif(CORRADE_TARGET_UNIX)
if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES)
list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessEglApplication)
else()
list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessGlxApplication)
endif()
elseif(CORRADE_TARGET_WINDOWS)
if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)
list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessWglApplication)
else()
list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessWindowsEglApplication)
endif()
endif()
set(_MAGNUM_Primitives_DEPENDENCIES MeshTools Trade)
@ -492,7 +496,6 @@ set(_MAGNUM_WindowlessEglApplication_DEPENDENCIES GL)
set(_MAGNUM_WindowlessGlxApplication_DEPENDENCIES GL)
set(_MAGNUM_WindowlessIosApplication_DEPENDENCIES GL)
set(_MAGNUM_WindowlessWglApplication_DEPENDENCIES GL)
set(_MAGNUM_WindowlessWindowsEglApplication_DEPENDENCIES GL)
set(_MAGNUM_XEglApplication_DEPENDENCIES GL)
set(_MAGNUM_CglContext_DEPENDENCIES GL)
set(_MAGNUM_EglContext_DEPENDENCIES GL)
@ -708,16 +711,16 @@ foreach(_component ${Magnum_FIND_COMPONENTS})
# OPENGL_opengl_LIBRARY because that's set even if
# OpenGL_GL_PREFERENCE is explicitly set to LEGACY.
if(MAGNUM_TARGET_GL)
if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES))
if(MAGNUM_TARGET_EGL)
find_package(EGL)
set_property(TARGET Magnum::${_component} APPEND
PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL)
elseif(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE)
find_package(OpenGL)
if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND)
set_property(TARGET Magnum::${_component} APPEND
PROPERTY INTERFACE_LINK_LIBRARIES OpenGL::GLX)
endif()
elseif(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES AND NOT CORRADE_TARGET_EMSCRIPTEN)
find_package(EGL)
set_property(TARGET Magnum::${_component} APPEND
PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL)
endif()
endif()
@ -747,16 +750,16 @@ foreach(_component ${Magnum_FIND_COMPONENTS})
# OPENGL_opengl_LIBRARY because that's set even if
# OpenGL_GL_PREFERENCE is explicitly set to LEGACY.
if(MAGNUM_TARGET_GL)
if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES))
if(MAGNUM_TARGET_EGL)
find_package(EGL)
set_property(TARGET Magnum::${_component} APPEND
PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL)
elseif(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE)
find_package(OpenGL)
if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND)
set_property(TARGET Magnum::${_component} APPEND
PROPERTY INTERFACE_LINK_LIBRARIES OpenGL::GLX)
endif()
elseif(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES AND NOT CORRADE_TARGET_EMSCRIPTEN)
find_package(EGL)
set_property(TARGET Magnum::${_component} APPEND
PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL)
endif()
endif()
@ -799,12 +802,6 @@ foreach(_component ${Magnum_FIND_COMPONENTS})
# Windowless WGL application has no additional dependencies
# Windowless Windows/EGL application dependencies
elseif(_component STREQUAL WindowlessWindowsEglApplication)
find_package(EGL)
set_property(TARGET Magnum::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES EGL::EGL)
# X/EGL application dependencies
elseif(_component STREQUAL XEglApplication)
find_package(EGL)
@ -860,7 +857,7 @@ foreach(_component ${Magnum_FIND_COMPONENTS})
# GL library
elseif(_component STREQUAL GL)
if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)
if(NOT MAGNUM_TARGET_GLES OR (MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_EGL AND NOT CORRADE_TARGET_IOS))
# If the GLVND library (CMake 3.11+) was found, link to the
# imported target. Otherwise (and also on all systems except
# Linux) link to the classic libGL. Can't use

2
package/archlinux/PKGBUILD-es2desktop

@ -22,7 +22,7 @@ build() {
-DCMAKE_INSTALL_PREFIX=/usr \
-DMAGNUM_TARGET_GLES=ON \
-DMAGNUM_TARGET_GLES2=ON \
-DMAGNUM_TARGET_DESKTOP_GLES=ON \
-DMAGNUM_TARGET_EGL=OFF \
-DMAGNUM_WITH_AUDIO=ON \
-DMAGNUM_WITH_GLFWAPPLICATION=ON \
-DMAGNUM_WITH_GLXAPPLICATION=ON \

2
package/archlinux/PKGBUILD-es3desktop

@ -22,7 +22,7 @@ build() {
-DCMAKE_INSTALL_PREFIX=/usr \
-DMAGNUM_TARGET_GLES=ON \
-DMAGNUM_TARGET_GLES2=OFF \
-DMAGNUM_TARGET_DESKTOP_GLES=ON \
-DMAGNUM_TARGET_EGL=OFF \
-DMAGNUM_WITH_AUDIO=ON \
-DMAGNUM_WITH_GLFWAPPLICATION=ON \
-DMAGNUM_WITH_GLXAPPLICATION=ON \

2
package/ci/appveyor-desktop-gles.bat

@ -27,7 +27,7 @@ cmake .. ^
-DCMAKE_PREFIX_PATH="%APPVEYOR_BUILD_FOLDER%/openal" ^
-DMAGNUM_TARGET_GLES=ON ^
-DMAGNUM_TARGET_GLES2=%TARGET_GLES2% ^
-DMAGNUM_TARGET_DESKTOP_GLES=ON ^
-DMAGNUM_TARGET_EGL=OFF ^
-DMAGNUM_WITH_AUDIO=OFF ^
-DMAGNUM_WITH_VK=OFF ^
-DMAGNUM_WITH_SCENETOOLS=OFF ^

18
package/ci/appveyor-lcov.sh

@ -20,12 +20,16 @@ set -ev
# AppVeyor ships Perl on its own and since we fetch our own lcov anyway, the
# MSYS insanity is not needed for ANYTHING AT ALL, in fact.
wget https://github.com/linux-test-project/lcov/archive/v1.15.tar.gz
tar -xzf v1.15.tar.gz
# Important: 1.13 is the only version that actually works. 1.15 doesn't, tries
# to find the original source files in build/.../CMakeFiles/src/Magnum and
# results in a zero-byte coverage being happily uploaded, with no error message
# produced whatsoever. How nice.
wget https://github.com/linux-test-project/lcov/archive/v1.13.tar.gz
tar -xzf v1.13.tar.gz
# Keep in sync with PKBUILD-coverage and circleci.yml, please
lcov-1.15/bin/lcov --gcov-tool /c/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/gcov --directory . --capture --output-file coverage.info > /dev/null
lcov-1.15/bin/lcov --gcov-tool /c/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/gcov --extract coverage.info "*/src/Magnum*/*" --output-file coverage.info > /dev/null
lcov-1.15/bin/lcov --gcov-tool /c/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/gcov --remove coverage.info "*/src/MagnumExternal/*" --output-file coverage.info > /dev/null
lcov-1.15/bin/lcov --gcov-tool /c/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/gcov --remove coverage.info "*/Test/*" --output-file coverage.info > /dev/null
lcov-1.15/bin/lcov --gcov-tool /c/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/gcov --remove coverage.info "*/build/src/*" --output-file coverage.info > /dev/null
lcov-1.13/bin/lcov --gcov-tool /c/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/gcov --directory . --capture --output-file coverage.info > /dev/null
lcov-1.13/bin/lcov --gcov-tool /c/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/gcov --extract coverage.info "*/src/Magnum*/*" --output-file coverage.info > /dev/null
lcov-1.13/bin/lcov --gcov-tool /c/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/gcov --remove coverage.info "*/src/MagnumExternal/*" --output-file coverage.info > /dev/null
lcov-1.13/bin/lcov --gcov-tool /c/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/gcov --remove coverage.info "*/Test/*" --output-file coverage.info > /dev/null
lcov-1.13/bin/lcov --gcov-tool /c/mingw-w64/x86_64-7.2.0-posix-seh-rt_v5-rev1/mingw64/bin/gcov --remove coverage.info "*/build/src/*" --output-file coverage.info > /dev/null

38
package/ci/circleci.yml

@ -67,6 +67,10 @@ commands:
if [[ "$CMAKE_CXX_FLAGS" == *"--coverage"* ]]; then export LCOV_PACKAGES="lcov curl"; fi
sudo apt install -y ninja-build gcc cmake $LCOV_PACKAGES << parameters.extra >>
# TODO this might get resolved with 1.11.1:
# https://github.com/ninja-build/ninja/pull/1827
# https://github.com/ninja-build/ninja/pull/2174
# But wouldn't it build too slow then? Heh
cap-ninja-jobs:
parameters:
count:
@ -271,7 +275,7 @@ jobs:
extra: libgl1-mesa-dev libsdl2-dev libglfw3-dev libopenal-dev libvulkan-dev
# In this case it gets stuck even with 24 jobs. Only on GCC, usually when
# the huge TradeAbstractImporterTest / TradeMaterialDataTest get involved.
# TODO: revisit when we get rid of more STL
# TODO: revisit when we get rid of more STL / deprecated includes
- cap-ninja-jobs:
count: 20
- install-gcc-4_8
@ -310,7 +314,7 @@ jobs:
# It crashes with the default setting. Only on GCC, usually when the huge
# TradeAbstractImporterTest / TradeMaterialDataTest / TradeSceneDataTest
# get involved.
# TODO: revisit when we get rid of more STL
# TODO: revisit when we get rid of more STL / deprecated includes
- cap-ninja-jobs
- install-gcc-4_8
- install-swiftshader-vulkan:
@ -331,11 +335,11 @@ jobs:
steps:
- install-base-linux:
extra: libsdl2-dev libglfw3-dev wget unzip
# It crashes with the default setting. Only on GCC, usually when the huge
# TradeAbstractImporterTest / TradeMaterialDataTest / TradeSceneDataTest
# get involved.
# TODO: revisit when we get rid of more STL
- cap-ninja-jobs
# In this case it gets stuck even with 24 jobs. Only on GCC, usually when
# the huge TradeAbstractImporterTest / TradeMaterialDataTest get involved.
# TODO: revisit when we get rid of more STL / deprecated includes
- cap-ninja-jobs:
count: 20
- install-gcc-4_8
- install-cmake:
version: "3.4.3"
@ -357,11 +361,11 @@ jobs:
steps:
- install-base-linux:
extra: libsdl2-dev libglfw3-dev wget unzip
# It crashes with the default setting. Only on GCC, usually when the huge
# TradeAbstractImporterTest / TradeMaterialDataTest / TradeSceneDataTest
# get involved.
# TODO: revisit when we get rid of more STL
- cap-ninja-jobs
# In this case it gets stuck even with 20 jobs. Only on GCC, usually when
# the huge TradeAbstractImporterTest / TradeMaterialDataTest get involved.
# TODO: revisit when we get rid of more STL / deprecated includes
- cap-ninja-jobs:
count: 16
- install-gcc-4_8
- install-cmake:
version: "3.4.3"
@ -377,6 +381,11 @@ jobs:
# STUPID yml interprets unquoted ON as a boolean
# https://stackoverflow.com/questions/53648244/specifying-the-string-value-yes-in-a-yaml-property
BUILD_STATIC: "ON"
# Testing magnum-sceneconverter and other utilities requires the plugins
# to be either installed or static. Tests are however deliberately run
# before install, so the static builds are the only case where the
# utilities get thoroughly tested.
EXTRA_OPTS: -DMAGNUM_SCENECONVERTER_STATIC_PLUGINS=Magnum::AnySceneImporter;Magnum::ObjImporter
CMAKE_CXX_FLAGS: --coverage
LCOV_EXTRA_OPTS: --gcov-tool /usr/bin/gcov-4.8
CONFIGURATION: Debug
@ -385,7 +394,7 @@ jobs:
- install-base-linux:
extra: libgl1-mesa-dev libsdl2-dev libglfw3-dev libopenal-dev libvulkan-dev
# 24 is not enough, unlike the other GCC-based builds
# TODO: revisit when we get rid of more STL
# TODO: revisit when we get rid of more STL / deprecated includes
- cap-ninja-jobs:
count: 20
- install-gcc-4_8
@ -510,6 +519,9 @@ jobs:
environment:
# STUPID yml interprets unquoted ON as a boolean
BUILD_STATIC: "ON"
# Same comment as with the linux-static build -- these are the only jobs
# that test command-line tools thoroughly
EXTRA_OPTS: -DMAGNUM_SCENECONVERTER_STATIC_PLUGINS=Magnum::AnySceneImporter;Magnum::ObjImporter
CMAKE_CXX_FLAGS: --coverage
CONFIGURATION: Debug
PLATFORM_GL_API: CGL

1
package/ci/unix-desktop.sh

@ -65,6 +65,7 @@ cmake .. \
-DMAGNUM_BUILD_DEPRECATED=$BUILD_DEPRECATED \
-DMAGNUM_BUILD_STATIC=$BUILD_STATIC \
-DMAGNUM_BUILD_PLUGINS_STATIC=$BUILD_STATIC \
$EXTRA_OPTS \
-G Ninja
ninja $NINJA_JOBS
ASAN_OPTIONS="color=always" LSAN_OPTIONS="color=always suppressions=$(pwd)/../package/ci/leaksanitizer.conf" TSAN_OPTIONS="color=always" CORRADE_TEST_COLOR=ON ctest -V -E "GLTest|GLBenchmark|VkTest"

14
src/Magnum/Animation/Track.h

@ -549,6 +549,8 @@ template<class K, class V, class R
* and to @cpp const std::pair<K_, V_> @ce (where @p K_ / @p V_ are
* with @cpp const @ce removed) when they are @cpp const @ce.
*/
/** @todo drop this, supplying strided array views is the usual
workflow at this point */
typedef typename std::conditional<std::is_const<K>::value, const std::pair<typename std::remove_const<K>::type, typename std::remove_const<V>::type>, std::pair<K, V>>::type KeyValueType;
/** @brief Animation result type */
@ -599,12 +601,16 @@ template<class K, class V, class R
* Converts @p data to a pair of strided array views and calls
* @ref TrackView(const Containers::StridedArrayView1D<K>&, const Containers::StridedArrayView1D<V>&, Interpolator, Extrapolation, Extrapolation).
*/
/** @todo drop this, supplying strided array views is the usual
workflow at this point */
/*implicit*/ TrackView(Containers::ArrayView<KeyValueType> data, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackView<K, V, R>{Containers::StridedArrayView1D<K>{data, data ? &data[0].first : nullptr, data.size(), sizeof(std::pair<K, V>)}, Containers::StridedArrayView1D<V>{data, data ? &data[0].second : nullptr, data.size(), sizeof(std::pair<K, V>)}, interpolator, before, after} {}
/** @overload
* Equivalent to calling @ref TrackView(Containers::ArrayView<KeyValueType>, Interpolator, Extrapolation, Extrapolation)
* with both @p before and @p after set to @p extrapolation.
*/
/** @todo drop this, supplying strided array views is the usual
workflow at this point */
explicit TrackView(Containers::ArrayView<KeyValueType> data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant) noexcept: TrackView<K, V, R>{data, interpolator, extrapolation, extrapolation} {}
/**
@ -641,12 +647,16 @@ template<class K, class V, class R
* Converts @p data to a pair of strided array views and calls
* @ref TrackView(const Containers::StridedArrayView1D<K>&, const Containers::StridedArrayView1D<V>&, Interpolator, Extrapolation, Extrapolation).
*/
/** @todo drop this, supplying strided array views is the usual
workflow at this point */
/*implicit*/ TrackView(Containers::ArrayView<KeyValueType> data, Interpolation interpolation, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackViewStorage<K>{Containers::StridedArrayView1D<K>{data, data ? &data[0].first : nullptr, data.size(), sizeof(std::pair<K, V>)}, Containers::StridedArrayView1D<V>{data, data ? &data[0].second : nullptr, data.size(), sizeof(std::pair<K, V>)}, interpolation, interpolator, before, after} {}
/** @overload
* Equivalent to calling @ref TrackView(Containers::ArrayView<KeyValueType>, Interpolation, Interpolator, Extrapolation, Extrapolation)
* with both @p before and @p after set to @p extrapolation.
*/
/** @todo drop this, supplying strided array views is the usual
workflow at this point */
/*implicit*/ TrackView(Containers::ArrayView<KeyValueType> data, Interpolation interpolation, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant) noexcept: TrackView<K, V, R>{data, interpolation, interpolator, extrapolation, extrapolation} {}
/**
@ -681,12 +691,16 @@ template<class K, class V, class R
* Converts @p data to a pair of strided array views and calls
* @ref TrackView(const Containers::StridedArrayView1D<K>&, const Containers::StridedArrayView1D<V>&, Interpolator, Extrapolation, Extrapolation).
*/
/** @todo drop this, supplying strided array views is the usual
workflow at this point */
/*implicit*/ TrackView(Containers::ArrayView<KeyValueType> data, Interpolation interpolation, Extrapolation before, Extrapolation after) noexcept: TrackView<K, V, R>{Containers::StridedArrayView1D<K>{data, data ? &data[0].first : nullptr, data.size(), sizeof(std::pair<K, V>)}, Containers::StridedArrayView1D<V>{data, data ? &data[0].second : nullptr, data.size(), sizeof(std::pair<K, V>)}, interpolation, before, after} {}
/** @overload
* Equivalent to calling @ref TrackView(Containers::ArrayView<KeyValueType>, Interpolation, Extrapolation, Extrapolation)
* with both @p before and @p after set to @p extrapolation.
*/
/** @todo drop this, supplying strided array views is the usual
workflow at this point */
/*implicit*/ TrackView(Containers::ArrayView<KeyValueType> data, Interpolation interpolation, Extrapolation extrapolation = Extrapolation::Constant) noexcept: TrackView<K, V, R>{data, interpolation, extrapolation, extrapolation} {}
/** @brief Convert a mutable view to a const one */

9
src/Magnum/Audio/AbstractImporter.cpp

@ -145,21 +145,24 @@ Containers::Array<char> AbstractImporter::data() {
}
Debug& operator<<(Debug& debug, const ImporterFeature value) {
const bool packed = debug.immediateFlags() >= Debug::Flag::Packed;
if(!packed)
debug << "Audio::ImporterFeature" << Debug::nospace;
switch(value) {
/* LCOV_EXCL_START */
#define _c(v) case ImporterFeature::v: return debug << "::" #v;
#define _c(v) case ImporterFeature::v: return debug << (packed ? "" : "::") << Debug::nospace << #v;
_c(OpenData)
#undef _c
/* LCOV_EXCL_STOP */
}
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedByte(value)) << Debug::nospace << ")";
return debug << (packed ? "" : "(") << Debug::nospace << reinterpret_cast<void*>(UnsignedByte(value)) << Debug::nospace << (packed ? "" : ")");
}
Debug& operator<<(Debug& debug, const ImporterFeatures value) {
return Containers::enumSetDebugOutput(debug, value, "Audio::ImporterFeatures{}", {
return Containers::enumSetDebugOutput(debug, value, debug.immediateFlags() >= Debug::Flag::Packed ? "{}" : "Audio::ImporterFeatures{}", {
ImporterFeature::OpenData});
}

2
src/Magnum/Audio/Context.h

@ -565,7 +565,7 @@ Example usage:
#define MAGNUM_ASSERT_AUDIO_EXTENSION_SUPPORTED(extension) \
do { \
if(!Magnum::Audio::Context::current().isExtensionSupported<extension>()) { \
Corrade::Utility::Error() << "Magnum: required OpenAL extension" << extension::string() << "is not supported"; \
Corrade::Utility::Error{} << "Magnum::Audio: required OpenAL extension" << extension::string() << "is not supported"; \
std::abort(); \
} \
} while(0)

24
src/Magnum/Audio/Test/AbstractImporterTest.cpp

@ -67,7 +67,9 @@ struct AbstractImporterTest: TestSuite::Tester {
void dataCustomDeleter();
void debugFeature();
void debugFeaturePacked();
void debugFeatures();
void debugFeaturesPacked();
};
AbstractImporterTest::AbstractImporterTest() {
@ -92,7 +94,9 @@ AbstractImporterTest::AbstractImporterTest() {
&AbstractImporterTest::dataCustomDeleter,
&AbstractImporterTest::debugFeature,
&AbstractImporterTest::debugFeatures});
&AbstractImporterTest::debugFeaturePacked,
&AbstractImporterTest::debugFeatures,
&AbstractImporterTest::debugFeaturesPacked});
}
void AbstractImporterTest::construct() {
@ -388,11 +392,25 @@ void AbstractImporterTest::debugFeature() {
CORRADE_COMPARE(out.str(), "Audio::ImporterFeature::OpenData Audio::ImporterFeature(0xf0)\n");
}
void AbstractImporterTest::debugFeaturePacked() {
std::ostringstream out;
/* Last is not packed, ones before should not make any flags persistent */
Debug{&out} << Debug::packed << ImporterFeature::OpenData << Debug::packed << ImporterFeature(0xf0) << ImporterFeature::OpenData;
CORRADE_COMPARE(out.str(), "OpenData 0xf0 Audio::ImporterFeature::OpenData\n");
}
void AbstractImporterTest::debugFeatures() {
std::ostringstream out;
Debug{&out} << ImporterFeature::OpenData << ImporterFeatures{};
CORRADE_COMPARE(out.str(), "Audio::ImporterFeature::OpenData Audio::ImporterFeatures{}\n");
Debug{&out} << (ImporterFeature::OpenData|ImporterFeature(0xf0)) << ImporterFeatures{};
CORRADE_COMPARE(out.str(), "Audio::ImporterFeature::OpenData|Audio::ImporterFeature(0xf0) Audio::ImporterFeatures{}\n");
}
void AbstractImporterTest::debugFeaturesPacked() {
std::ostringstream out;
/* Last is not packed, ones before should not make any flags persistent */
Debug{&out} << Debug::packed << (ImporterFeature::OpenData|ImporterFeature(0xf0)) << Debug::packed << ImporterFeatures{} << ImporterFeature::OpenData;
CORRADE_COMPARE(out.str(), "OpenData|0xf0 {} Audio::ImporterFeature::OpenData\n");
}
}}}}

8
src/Magnum/DebugTools/CompareImage.h

@ -92,7 +92,7 @@ class MAGNUM_DEBUGTOOLS_EXPORT ImageComparatorBase {
void saveDiagnostic(TestSuite::ComparisonStatusFlags flags, Utility::Debug& out, Containers::StringView path);
private:
class MAGNUM_DEBUGTOOLS_LOCAL State;
class State;
Containers::Pointer<State> _state;
};
@ -359,7 +359,7 @@ class CompareImage {
};
/**
@brief Image file comparator
@brief Image file comparator for @ref Corrade::TestSuite
Similar to @ref CompareImage, but comparing images loaded from files. Example
usage:
@ -465,7 +465,7 @@ class CompareImageFile {
};
/**
@brief Image-to-file comparator
@brief Image-to-file comparator for @ref Corrade::TestSuite
A combination of @ref CompareImage and @ref CompareImageFile, which allows to
compare an in-memory image to a image file. See their documentation for more
@ -528,7 +528,7 @@ class CompareImageToFile {
};
/**
@brief File-to-image comparator
@brief File-to-image comparator for @ref Corrade::TestSuite
A combination of @ref CompareImage and @ref CompareImageFile, which allows to
compare an image file to an in-memory image. See their documentation for more

2
src/Magnum/DebugTools/TextureImage.cpp

@ -87,7 +87,7 @@ FloatReinterpretShader::FloatReinterpretShader() {
vert.addSource(rs.getString("TextureImage.vert"));
frag.addSource(rs.getString("TextureImage.frag"));
CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag}));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
attachShaders({vert, frag});
if(!GL::Context::current().isExtensionSupported<GL::Extensions::MAGNUM::shader_vertex_id>()) {

75
src/Magnum/GL/AbstractShaderProgram.cpp

@ -3,6 +3,7 @@
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Copyright © Vladislav Oleshko <vladislav.oleshko@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@ -26,10 +27,12 @@
#include "AbstractShaderProgram.h"
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/Iterable.h>
#include <Corrade/Containers/StridedArrayView.h>
#ifndef MAGNUM_TARGET_WEBGL
#include <Corrade/Containers/String.h>
#endif
#include <Corrade/Containers/StringStl.h> /** @todo remove once <string>-free */
#include <Corrade/Containers/Reference.h>
#include <Corrade/Utility/DebugStl.h>
@ -533,7 +536,7 @@ void AbstractShaderProgram::attachShader(Shader& shader) {
glAttachShader(_id, shader.id());
}
void AbstractShaderProgram::attachShaders(std::initializer_list<Containers::Reference<Shader>> shaders) {
void AbstractShaderProgram::attachShaders(Containers::Iterable<Shader> shaders) {
for(Shader& s: shaders) attachShader(s);
}
@ -583,54 +586,74 @@ void AbstractShaderProgram::transformFeedbackVaryingsImplementationDanglingWorka
#endif
#endif
bool AbstractShaderProgram::link() { return link({*this}); }
bool AbstractShaderProgram::link() {
submitLink();
return checkLink({});
}
bool AbstractShaderProgram::link(std::initializer_list<Containers::Reference<AbstractShaderProgram>> shaders) {
bool allSuccess = true;
void AbstractShaderProgram::submitLink() {
glLinkProgram(_id);
}
/* Invoke (possibly parallel) linking on all shaders */
for(AbstractShaderProgram& shader: shaders) glLinkProgram(shader._id);
bool AbstractShaderProgram::checkLink(const Containers::Iterable<Shader> shaders) {
/* If any compilation failed, abort without even checking the link status.
The checkCompile() API is called always, to print also compilation
warnings even in case everything still manages to link well. */
for(Shader& shader: shaders)
if(!shader.checkCompile()) return false;
/* After linking phase, check status of all shaders */
Int i = 1;
for(AbstractShaderProgram& shader: shaders) {
GLint success, logLength;
glGetProgramiv(shader._id, GL_LINK_STATUS, &success);
glGetProgramiv(shader._id, GL_INFO_LOG_LENGTH, &logLength);
glGetProgramiv(_id, GL_LINK_STATUS, &success);
glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &logLength);
/* Error or warning message. The string is returned null-terminated,
strip the \0 at the end afterwards. */
std::string message(logLength, '\n');
if(message.size() > 1)
glGetProgramInfoLog(shader._id, message.size(), nullptr, &message[0]);
glGetProgramInfoLog(_id, message.size(), nullptr, &message[0]);
message.resize(Math::max(logLength, 1)-1);
/* Some drivers are chatty and can't keep shut when there's nothing to
be said, handle that as well. */
Context::current().state().shaderProgram.cleanLogImplementation(message);
/* Usually the driver messages contain a newline at the end. But sometimes
not, such as in case of a program link error due to shaders not being
compiled yet on Mesa; sometimes there's two newlines, sometimes just a
newline and nothing else etc. Because trying do this in driver-specific
workarounds would involve an impossible task of checking all possible
error messages on every possible driver, just trim all whitespace around
the message always and let Debug add its own newline. */
const Containers::StringView messageTrimmed = Containers::StringView{message}.trimmed();
/* Show error log */
if(!success) {
Error out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::AbstractShaderProgram::link(): linking";
if(shaders.size() != 1) out << "of shader" << i;
out << "failed with the following message:" << Debug::newline << message;
Error{} << "GL::AbstractShaderProgram::link(): linking failed with the following message:"
<< Debug::newline << messageTrimmed;
/* Or just warnings, if any */
} else if(!message.empty()) {
Warning out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::AbstractShaderProgram::link(): linking";
if(shaders.size() != 1) out << "of shader" << i;
out << "succeeded with the following message:" << Debug::newline << message;
} else if(messageTrimmed) {
Warning{} << "GL::AbstractShaderProgram::link(): linking succeeded with the following message:"
<< Debug::newline << messageTrimmed;
}
/* Success of all depends on each of them */
allSuccess = allSuccess && success;
++i;
return success;
}
#ifdef MAGNUM_BUILD_DEPRECATED
bool AbstractShaderProgram::link(std::initializer_list<Containers::Reference<AbstractShaderProgram>> shaders) {
for(AbstractShaderProgram& shader: shaders) shader.submitLink();
bool allSuccess = true;
for(AbstractShaderProgram& shader: shaders) allSuccess = allSuccess && shader.checkLink({});
return allSuccess;
}
#endif
bool AbstractShaderProgram::isLinkFinished() {
GLint success;
Context::current().state().shaderProgram.completionStatusImplementation(_id, GL_COMPLETION_STATUS_KHR, &success);
return success == GL_TRUE;
}
void AbstractShaderProgram::cleanLogImplementationNoOp(std::string&) {}
@ -646,6 +669,10 @@ void AbstractShaderProgram::cleanLogImplementationAngle(std::string& message) {
}
#endif
void AbstractShaderProgram::completionStatusImplementationFallback(GLuint, GLenum, GLint* value) {
*value = GL_TRUE;
}
Int AbstractShaderProgram::uniformLocationInternal(const Containers::ArrayView<const char> name) {
const GLint location = glGetUniformLocation(_id, name);
if(location == -1)

166
src/Magnum/GL/AbstractShaderProgram.h

@ -5,6 +5,7 @@
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Copyright © Vladislav Oleshko <vladislav.oleshko@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@ -42,6 +43,9 @@
#endif
#ifdef MAGNUM_BUILD_DEPRECATED
#include <Corrade/Utility/Macros.h>
/* For attachShaders(), which used to take a std::initializer_list<Reference> */
#include <Corrade/Containers/Iterable.h>
/* For label() / setLabel(), which used to be a std::string */
#include <Corrade/Containers/StringStl.h>
#endif
@ -422,6 +426,74 @@ See also @ref Attribute::DataType enum for additional type options.
@ref Magnum::Matrix4x2 "Matrix4x2", @ref Magnum::Matrix3x4 "Matrix3x4" and
@ref Magnum::Matrix4x3 "Matrix4x3") are not available in WebGL 1.0.
@section GL-AbstractShaderProgram-async Asynchronous shader compilation and linking
The workflow described @ref GL-AbstractShaderProgram-subclassing "at the very top"
compiles and links the shader directly in a constructor. While that's fine for
many use cases, with heavier shaders, many shader combinations or on
platforms that translate GLSL to other APIs such as HLSL or MSL, the
compilation and linking can take a significant portion of application startup
time.
To mitigate this problem, nowadays drivers implement *asynchronous compilation*
--- when shader compilation or linking is requested, the driver offloads the
work to separate worker threads, and serializes it back to the application
thread only once the application wants to retrieve the result of the operation.
Which means, the ideal way to spread the operation over more CPU cores is to
first submit compilation & linking of several shaders at once and only then ask
for operation result. That allows the driver to perform compilation/linking of
multiple shaders at once. Furthermore, the
@gl_extension{KHR,parallel_shader_compile} extension adds a possibility to
query whether the operation was finished for a particular shader. That allows
the application to schedule other work in the meantime.
Async compilation and linking can be implemented by using
@ref Shader::submitCompile() and @ref submitLink(), followed by
@ref checkLink() (which optionally delegates to @ref Shader::checkCompile()),
instead of @ref Shader::compile() and @ref link(). Calling the submit functions
will trigger a (potentially async) compilation and linking, calling the check
functions will check the operation result, potentially stalling if the async
operation isn't finished yet.
The @ref Shader::isCompileFinished() and
@ref isLinkFinished() APIs then provide a way to query if the submitted
operation finished. If @gl_extension{KHR,parallel_shader_compile} is not
available, those two implicitly return @cpp true @ce, thus effectively causing
a stall if the operation isn't yet done at the time you call
@ref Shader::checkCompile() / @ref checkLink() --- but compared to the linear
workflow you still get the benefits from submitting multiple operations at
once.
A common way to equip an @ref AbstractShaderProgram subclass with async
creation capability while keeping also the simple constructor is the following:
1. An internal @ref NoInit constructor for the subclass is added, which only
creates the @ref AbstractShaderProgram base but does nothing else.
2. A @cpp CompileState @ce inner class is defined as a subclass of
@cpp MyShader @ce. Besides that it holds all temporary state needed to
finish the construction --- in particular all @ref Shader instances.
3. A @cpp static CompileState compile() @ce function does everything until
and including linking as the original constructor did, except that it calls
@ref Shader::submitCompile() and @ref submitLink() instead of
@ref Shader::compile() and @ref link(), and returns a populated
@cpp CompileState @ce instance.
4. A @cpp MyShader(CompileState&&) @ce constructor then takes over the base
of @cpp CompileState @ce by delegating it into the move constructor. Then
it calls @ref checkLink(), passing all input shaders to it for a complete
context in case of an error, and finally performs any remaining post-link
steps such as uniform setup.
5. The original @cpp MyShader() @ce constructor now only passes the result of
@cpp compile() @ce to @cpp MyShader(CompileState&&) @ce.
@snippet MagnumGL.cpp AbstractShaderProgram-async
Usage-wise, it can look for example like below, with the last line waiting for
linking to finish and making the shader ready to use. On drivers that don't
perform any async compilation this will behave the same as if the construction
was done the usual way.
@snippet MagnumGL.cpp AbstractShaderProgram-async-usage
@section GL-AbstractShaderProgram-performance-optimization Performance optimizations
The engine tracks currently used shader program to avoid unnecessary calls to
@ -1258,21 +1330,40 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
AbstractShaderProgram& dispatchCompute(const Vector3ui& workgroupCount);
#endif
/**
* @brief Whether a @ref submitLink() operation has finished
* @m_since_latest
*
* Has to be called only if @ref submitLink() was called before, and
* before @ref checkLink(). If returns @cpp false @ce, a subsequent
* @ref checkLink() call will block until the linking is finished. If
* @gl_extension{KHR,parallel_shader_compile} is not available, the
* function always returns @cpp true @ce --- i.e., as if the linking
* was done synchronously. See @ref GL-AbstractShaderProgram-async for
* more information.
* @see @ref Shader::isCompileFinished(),
* @fn_gl_keyword{GetProgram} with
* @def_gl_extension{COMPLETION_STATUS,KHR,parallel_shader_compile}
*/
bool isLinkFinished();
protected:
#ifdef MAGNUM_BUILD_DEPRECATED
/**
* @brief Link the shader
* @brief Link multiple shaders simultaenously
* @m_deprecated_since_latest Originally meant to batch multiple link
* operations together in a way that allowed the driver to perform
* the linking in multiple threads. Superseded by @ref submitLink()
* and @ref checkLink(), use either those or the zero-argument
* @ref link() instead. See @ref GL-AbstractShaderProgram-async
* for more information.
*
* Calls @ref submitLink() on all shaders first, then @ref checkLink().
* Returns @cpp false @ce if linking of any shader failed, @cpp true @ce
* if everything succeeded. Linker message (if any) is printed to error
* output. All attached shaders must be compiled with
* @ref Shader::compile() before linking. The operation is batched in a
* way that allows the driver to link multiple shaders simultaneously
* (i.e. in multiple threads).
* @see @fn_gl_keyword{LinkProgram}, @fn_gl_keyword{GetProgram} with
* @def_gl{LINK_STATUS} and @def_gl{INFO_LOG_LENGTH},
* @fn_gl_keyword{GetProgramInfoLog}
* if everything succeeded.
*/
static bool link(std::initializer_list<Containers::Reference<AbstractShaderProgram>> shaders);
static CORRADE_DEPRECATED("use either submitLink() and checkLink() or the zero-argument link() instead") bool link(std::initializer_list<Containers::Reference<AbstractShaderProgram>> shaders);
#endif
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
/**
@ -1325,7 +1416,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
* than one shader at once. Other than that there is no other
* (performance) difference when using this function.
*/
void attachShaders(std::initializer_list<Containers::Reference<Shader>> shaders);
void attachShaders(Containers::Iterable<Shader> shaders);
/**
* @brief Bind an attribute to given location
@ -1446,13 +1537,56 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
/**
* @brief Link the shader
*
* Links single shader. If possible, prefer to link multiple shaders
* at once using @ref link(std::initializer_list<Containers::Reference<AbstractShaderProgram>>)
* for improved performance, see its documentation for more
* information.
* Calls @ref submitLink(), immediately followed by @ref checkLink(),
* passing back its return value. See documentation of those two
* functions for details.
* @see @ref Shader::compile()
*/
bool link();
/**
* @brief Submit the shader for linking
* @m_since_latest
*
* The attached shaders must be at least submitted for compilation
* with @ref Shader::submitCompile() or @ref Shader::compile() before
* linking. Call @ref isLinkFinished() or @ref checkLink() after, see
* @ref GL-AbstractShaderProgram-async for more information.
* @see @fn_gl_keyword{LinkProgram}
*/
void submitLink();
/**
* @brief Check shader linking status and await completion
* @m_since_latest
*
* Has to be called only if @ref submitLink() was called before.
*
* If @p shaders are not empty, first calls @ref Shader::checkCompile()
* on each. If a compilation failure is reached, returns @cpp false @ce
* without even checking link status. To have error messages with full
* context in case of a failed shader compilation or linking, an
* application is encouraged to pass all input @ref Shader instances to
* this function or, if not possible, explicitly call
* @ref Shader::checkCompile() on each.
*
* Then, link status is checked and a message (if any) is printed
* Returns @cpp false @ce if linking failed, @cpp true @ce on success.
* If linking failed, it first goes through @p shaders and calls
* @ref Shader::checkCompile() on each until a failure is reached. If
* no compilation failed, a linker message is printed to error output.
* The function will stall until a (potentially async) linking
* operation finishes, you can use @ref isLinkFinished() to check the
* status instead. See @ref GL-AbstractShaderProgram-async for more
* information.
* @see @ref Shader::checkCompile(), @fn_gl_keyword{GetProgram} with
* @def_gl{LINK_STATUS} and @def_gl{INFO_LOG_LENGTH},
* @fn_gl_keyword{GetProgramInfoLog}
*/
/* No default argument is provided in order to *really* encourage apps
to pass the shaders here */
bool checkLink(Containers::Iterable<Shader> shaders);
/**
* @brief Get uniform location
* @param name Uniform name
@ -1668,6 +1802,8 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
static MAGNUM_GL_LOCAL void cleanLogImplementationAngle(std::string& message);
#endif
MAGNUM_GL_LOCAL static void APIENTRY completionStatusImplementationFallback(GLuint, GLenum, GLint*);
MAGNUM_GL_LOCAL static void use(GLuint id);
void use();

6
src/Magnum/GL/CMakeLists.txt

@ -214,7 +214,7 @@ elseif(MAGNUM_BUILD_STATIC_PIC)
set_target_properties(MagnumGL PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
target_link_libraries(MagnumGL PUBLIC Magnum)
if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)
if(NOT MAGNUM_TARGET_GLES OR (MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_EGL AND NOT CORRADE_TARGET_IOS))
# If the GLVND library (CMake 3.11+) was found, link to the imported
# target. Otherwise (and also on all systems except Linux) link to the
# classic libGL. Can't use OpenGL_OpenGL_FOUND, because that one is set
@ -297,7 +297,7 @@ if(MAGNUM_BUILD_TESTS)
endif()
target_link_libraries(MagnumGLTestLib PUBLIC
Magnum)
if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)
if(NOT MAGNUM_TARGET_GLES OR (MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_EGL AND NOT CORRADE_TARGET_IOS))
# If the GLVND library (CMake 3.11+) was found, link to the imported
# target. Otherwise (and also on all systems except Linux) link to the
# classic libGL. Can't use OpenGL_OpenGL_FOUND, because that one is set
@ -331,7 +331,7 @@ if(MAGNUM_BUILD_TESTS)
# Windows only and elsewhere I just link the same way as with
# MagnumOpenGLTester.
if(CORRADE_TARGET_WINDOWS)
if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES)
if(MAGNUM_TARGET_EGL)
# Otherwise it complains that EGL::EGL does not exist here
find_package(EGL)
endif()

4
src/Magnum/GL/Context.h

@ -1192,7 +1192,7 @@ Example usage:
#define MAGNUM_ASSERT_GL_VERSION_SUPPORTED(version) \
do { \
if(!Magnum::GL::Context::current().isVersionSupported(version)) { \
Corrade::Utility::Error() << "Magnum: required version" << version << "is not supported"; \
Corrade::Utility::Error{} << "Magnum::GL: required version" << version << "is not supported"; \
std::abort(); \
} \
} while(0)
@ -1221,7 +1221,7 @@ Example usage:
#define MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(extension) \
do { \
if(!Magnum::GL::Context::current().isExtensionSupported<extension>()) { \
Corrade::Utility::Error() << "Magnum: required extension" << extension::string() << "is not supported"; \
Corrade::Utility::Error{} << "Magnum::GL: required extension" << extension::string() << "is not supported"; \
std::abort(); \
} \
} while(0)

7
src/Magnum/GL/Implementation/ShaderProgramState.cpp

@ -78,6 +78,13 @@ ShaderProgramState::ShaderProgramState(Context& context, Containers::StaticArray
cleanLogImplementation = &AbstractShaderProgram::cleanLogImplementationNoOp;
}
if(context.isExtensionSupported<Extensions::KHR::parallel_shader_compile>()) {
extensions[Extensions::KHR::parallel_shader_compile::Index] = Extensions::KHR::parallel_shader_compile::string();
completionStatusImplementation = glGetProgramiv;
} else {
completionStatusImplementation = &AbstractShaderProgram::completionStatusImplementationFallback;
}
#ifndef MAGNUM_TARGET_WEBGL
#ifndef MAGNUM_TARGET_GLES2
#ifndef MAGNUM_TARGET_GLES

3
src/Magnum/GL/Implementation/ShaderProgramState.h

@ -47,6 +47,9 @@ struct ShaderProgramState {
void(AbstractShaderProgram::*transformFeedbackVaryingsImplementation)(Containers::ArrayView<const std::string>, AbstractShaderProgram::TransformFeedbackBufferMode);
#endif
void(*cleanLogImplementation)(std::string&);
/* This is a direct pointer to a GL function, so needs a __stdcall on
Windows to compile properly on 32 bits */
void(APIENTRY *completionStatusImplementation)(GLuint, GLenum, GLint* value);
#ifndef MAGNUM_TARGET_WEBGL
void(APIENTRY *uniform1fvImplementation)(GLuint, GLint, GLsizei, const GLfloat*);

12
src/Magnum/GL/Implementation/ShaderState.cpp

@ -31,12 +31,13 @@
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Shader.h"
#include "Magnum/GL/Extensions.h"
namespace Magnum { namespace GL { namespace Implementation {
using namespace Containers::Literals;
ShaderState::ShaderState(Context& context, Containers::StaticArrayView<Implementation::ExtensionCount, const char*>):
ShaderState::ShaderState(Context& context, Containers::StaticArrayView<Implementation::ExtensionCount, const char*> extensions):
maxVertexOutputComponents{}, maxFragmentInputComponents{},
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
maxTessellationControlInputComponents{}, maxTessellationControlOutputComponents{}, maxTessellationControlTotalOutputComponents{}, maxTessellationEvaluationInputComponents{}, maxTessellationEvaluationOutputComponents{}, maxGeometryInputComponents{}, maxGeometryOutputComponents{}, maxGeometryTotalOutputComponents{}, maxAtomicCounterBuffers{}, maxCombinedAtomicCounterBuffers{}, maxAtomicCounters{}, maxCombinedAtomicCounters{}, maxImageUniforms{}, maxCombinedImageUniforms{}, maxShaderStorageBlocks{}, maxCombinedShaderStorageBlocks{},
@ -68,9 +69,12 @@ ShaderState::ShaderState(Context& context, Containers::StaticArrayView<Implement
cleanLogImplementation = &Shader::cleanLogImplementationNoOp;
}
/* Needed only if neither of these ifdefs above hits, but I won't bother
crafting the preprocessor logic for this. */
static_cast<void>(context);
if(context.isExtensionSupported<GL::Extensions::KHR::parallel_shader_compile>()) {
extensions[Extensions::KHR::parallel_shader_compile::Index] = Extensions::KHR::parallel_shader_compile::string();
completionStatusImplementation = glGetShaderiv;
} else {
completionStatusImplementation = &Shader::completionStatusImplementationFallback;
}
}
}}}

3
src/Magnum/GL/Implementation/ShaderState.h

@ -53,6 +53,9 @@ struct ShaderState {
void(Shader::*addSourceImplementation)(std::string);
void(*cleanLogImplementation)(std::string&);
/* This is a direct pointer to a GL function, so needs a __stdcall on
Windows to compile properly on 32 bits */
void(APIENTRY *completionStatusImplementation)(GLuint, GLenum, GLint* value);
GLint maxVertexOutputComponents,
maxFragmentInputComponents;

2
src/Magnum/GL/OpenGL.h

@ -46,7 +46,7 @@
#endif
/* Special case for desktop GLES on Windows (still links to the old opengl32.dll) */
#elif defined(CORRADE_TARGET_WINDOWS) && defined(MAGNUM_TARGET_DESKTOP_GLES)
#elif defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_EGL)
#ifdef MAGNUM_TARGET_GLES2
#include "MagnumExternal/OpenGL/GLES2/flextGLWindowsDesktop.h"
#else

14
src/Magnum/GL/OpenGLTester.h

@ -37,25 +37,17 @@
#include "Magnum/GL/Renderer.h"
#include "Magnum/GL/TimeQuery.h"
#if defined(MAGNUM_TARGET_HEADLESS) || defined(CORRADE_TARGET_EMSCRIPTEN) || defined(CORRADE_TARGET_ANDROID)
#ifdef MAGNUM_TARGET_EGL
#include "Magnum/Platform/WindowlessEglApplication.h"
#elif defined(CORRADE_TARGET_IOS)
#include "Magnum/Platform/WindowlessIosApplication.h"
#elif defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
#elif defined(CORRADE_TARGET_APPLE)
#include "Magnum/Platform/WindowlessCglApplication.h"
#elif defined(CORRADE_TARGET_UNIX)
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_DESKTOP_GLES)
#include "Magnum/Platform/WindowlessEglApplication.h"
#else
#include "Magnum/Platform/WindowlessGlxApplication.h"
#endif
#elif defined(CORRADE_TARGET_WINDOWS)
#if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_DESKTOP_GLES)
#include "Magnum/Platform/WindowlessWglApplication.h"
#else
#include "Magnum/Platform/WindowlessWindowsEglApplication.h"
#endif
#else
#error cannot run OpenGL tests on this platform
#endif
@ -98,7 +90,7 @@ See @ref building, @ref cmake and @ref testsuite for more information.
Implicitly, running the test executables requires presence of a GPU with OpenGL
drivers. In addition, on desktop, unless Magnum is built with
`MAGNUM_TARGET_HEADLESS`, OpenGL context creation requires a graphical desktop
@ref MAGNUM_TARGET_EGL, OpenGL context creation requires a graphical desktop
to be running. On embedded systems (and @ref CORRADE_TARGET_IOS "iOS",
@ref CORRADE_TARGET_ANDROID "Android" in particular) running the tests has no
special requirements. On Emscripten the tests have to be running in a browser,

100
src/Magnum/GL/Shader.cpp

@ -3,6 +3,7 @@
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Copyright © Vladislav Oleshko <vladislav.oleshko@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@ -26,7 +27,9 @@
#include "Shader.h"
#include <Corrade/Containers/Array.h>
#ifdef MAGNUM_BUILD_DEPRECATED
#include <Corrade/Containers/Reference.h>
#endif
#ifndef MAGNUM_TARGET_WEBGL
#include <Corrade/Containers/String.h>
#endif
@ -642,7 +645,7 @@ Int Shader::maxCombinedUniformComponents(const Type type) {
}
#endif
Shader::Shader(const Version version, const Type type): _type(type), _id(0) {
Shader::Shader(const Version version, const Type type): _type{type}, _flags{ObjectFlag::DeleteOnDestruction|ObjectFlag::Created} {
_id = glCreateShader(GLenum(_type));
switch(version) {
@ -675,9 +678,11 @@ Shader::Shader(const Version version, const Type type): _type(type), _id(0) {
CORRADE_ASSERT_UNREACHABLE("GL::Shader::Shader(): unsupported version" << version, );
}
Shader::Shader(const Type type, const GLuint id, ObjectFlags flags) noexcept: _type{type}, _id{id}, _flags{flags} {}
Shader::~Shader() {
/* Moved out, nothing to do */
if(!_id) return;
/* Moved out or not deleting on destruction, nothing to do */
if(!_id || !(_flags & ObjectFlag::DeleteOnDestruction)) return;
glDeleteShader(_id);
}
@ -747,75 +752,84 @@ Shader& Shader::addFile(const std::string& filename) {
return *this;
}
bool Shader::compile() { return compile({*this}); }
bool Shader::compile() {
submitCompile();
return checkCompile();
}
bool Shader::compile(std::initializer_list<Containers::Reference<Shader>> shaders) {
bool allSuccess = true;
void Shader::submitCompile() {
CORRADE_ASSERT(_sources.size() > 1, "GL::Shader::compile(): no files added", );
/* Allocate large enough array for source pointers and sizes (to avoid
reallocating it for each of them) */
std::size_t maxSourceCount = 0;
for(Shader& shader: shaders) {
CORRADE_ASSERT(shader._sources.size() > 1, "GL::Shader::compile(): no files added", false);
maxSourceCount = Math::max(shader._sources.size(), maxSourceCount);
}
/** @todo ArrayTuple/VLAs */
Containers::Array<const GLchar*> pointers(maxSourceCount);
Containers::Array<GLint> sizes(maxSourceCount);
Containers::Array<const GLchar*> pointers(_sources.size());
Containers::Array<GLint> sizes(_sources.size());
/* Upload sources of all shaders */
for(Shader& shader: shaders) {
for(std::size_t i = 0; i != shader._sources.size(); ++i) {
pointers[i] = static_cast<const GLchar*>(shader._sources[i].data());
sizes[i] = shader._sources[i].size();
for(std::size_t i = 0; i != _sources.size(); ++i) {
pointers[i] = static_cast<const GLchar*>(_sources[i].data());
sizes[i] = _sources[i].size();
}
glShaderSource(shader._id, shader._sources.size(), pointers, sizes);
glShaderSource(_id, _sources.size(), pointers, sizes);
glCompileShader(_id);
}
/* Invoke (possibly parallel) compilation on all shaders */
for(Shader& shader: shaders) glCompileShader(shader._id);
/* After compilation phase, check status of all shaders */
Int i = 1;
for(Shader& shader: shaders) {
bool Shader::checkCompile() {
GLint success, logLength;
glGetShaderiv(shader._id, GL_COMPILE_STATUS, &success);
glGetShaderiv(shader._id, GL_INFO_LOG_LENGTH, &logLength);
glGetShaderiv(_id, GL_COMPILE_STATUS, &success);
glGetShaderiv(_id, GL_INFO_LOG_LENGTH, &logLength);
/* Error or warning message. The string is returned null-terminated,
strip the \0 at the end afterwards. */
std::string message(logLength, '\0');
if(message.size() > 1)
glGetShaderInfoLog(shader._id, message.size(), nullptr, &message[0]);
glGetShaderInfoLog(_id, message.size(), nullptr, &message[0]);
message.resize(Math::max(logLength, 1)-1);
/* Some drivers are chatty and can't keep shut when there's nothing to
be said, handle that as well. */
Context::current().state().shader.cleanLogImplementation(message);
/* Usually the driver messages contain a newline at the end. But sometimes
not, such as in case of a program link error due to shaders not being
compiled yet on Mesa; sometimes there's two newlines, sometimes just a
newline and nothing else etc. Because trying do this in driver-specific
workarounds would involve an impossible task of checking all possible
error messages on every possible driver, just trim all whitespace around
the message always and let Debug add its own newline. */
const Containers::StringView messageTrimmed = Containers::StringView{message}.trimmed();
/* Show error log */
if(!success) {
Error out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::Shader::compile(): compilation of" << shaderName(shader._type) << "shader";
if(shaders.size() != 1) out << i;
out << "failed with the following message:" << Debug::newline << message;
Error{} << "GL::Shader::compile(): compilation of" << shaderName(_type)
<< "shader failed with the following message:" << Debug::newline
<< messageTrimmed;
/* Or just warnings, if any */
} else if(!message.empty()) {
Warning out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::Shader::compile(): compilation of" << shaderName(shader._type) << "shader";
if(shaders.size() != 1) out << i;
out << "succeeded with the following message:" << Debug::newline << message;
} else if(messageTrimmed) {
Warning{} << "GL::Shader::compile(): compilation of" << shaderName(_type)
<< "shader succeeded with the following message:" << Debug::newline
<< messageTrimmed;
}
/* Success of all depends on each of them */
allSuccess = allSuccess && success;
++i;
return success;
}
#ifdef MAGNUM_BUILD_DEPRECATED
bool Shader::compile(std::initializer_list<Containers::Reference<Shader>> shaders) {
/* Invoke (possibly parallel) compilation on all shaders */
for(Shader& shader: shaders) shader.submitCompile();
bool allSuccess = true;
for(Shader& shader: shaders) allSuccess = allSuccess && shader.checkCompile();
return allSuccess;
}
#endif
bool Shader::isCompileFinished() {
GLint success;
Context::current().state().shader.completionStatusImplementation(_id, GL_COMPLETION_STATUS_KHR, &success);
return success == GL_TRUE;
}
void Shader::cleanLogImplementationNoOp(std::string&) {}
@ -825,6 +839,10 @@ void Shader::cleanLogImplementationIntelWindows(std::string& message) {
}
#endif
void Shader::completionStatusImplementationFallback(GLuint, GLenum, GLint* value) {
*value = GL_TRUE;
}
#ifndef DOXYGEN_GENERATING_OUTPUT
Debug& operator<<(Debug& debug, const Shader::Type value) {
debug << "GL::Shader::Type" << Debug::nospace;

147
src/Magnum/GL/Shader.h

@ -5,6 +5,7 @@
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Copyright © Vladislav Oleshko <vladislav.oleshko@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@ -38,6 +39,7 @@
#include "Magnum/GL/GL.h"
#ifdef MAGNUM_BUILD_DEPRECATED
#include <Corrade/Utility/Macros.h>
/* For label() / setLabel(), which used to be a std::string. Not ideal for the
return type, but at least something. */
#include <Corrade/Containers/StringStl.h>
@ -507,19 +509,39 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject {
static Int maxCombinedUniformComponents(Type type);
#endif
#ifdef MAGNUM_BUILD_DEPRECATED
/**
* @brief Compile multiple shaders simultaneously
*
* Returns @cpp false @ce if compilation of any shader failed,
* @cpp true @ce if everything succeeded. Compiler messages (if any)
* are printed to error output. The operation is batched in a way that
* allows the driver to perform multiple compilations simultaneously
* (i.e. in multiple threads).
* @see @fn_gl_keyword{ShaderSource}, @fn_gl_keyword{CompileShader},
* @fn_gl_keyword{GetShader} with @def_gl{COMPILE_STATUS} and
* @def_gl{INFO_LOG_LENGTH}, @fn_gl_keyword{GetShaderInfoLog}
*/
static bool compile(std::initializer_list<Containers::Reference<Shader>> shaders);
* @m_deprecated_since_latest Originally meant to batch multiple
* compile operations together in a way that allowed the driver to
* perform the compilation in multiple threads. Superseded by
* @ref submitCompile() and @ref checkCompile(), use either those
* or the zero-argument @ref compile() instead. See
* @ref GL-AbstractShaderProgram-async for more information.
*
* Calls @ref submitCompile() on all shaders first, then
* @ref checkCompile(). Returns @cpp false @ce if compilation of any
* shader failed, @cpp true @ce if everything succeeded.
*/
static CORRADE_DEPRECATED("use either submitCompile() and checkCompile() or the zero-argument compile() instead") bool compile(std::initializer_list<Containers::Reference<Shader>> shaders);
#endif
/**
* @brief Wrap existing OpenGL shader object
* @param type Shader type
* @param id OpenGL shader ID
* @param flags Object creation flags
* @m_since_latest
*
* The @p id is expected to be of an existing OpenGL shader object.
* Unlike a shader created using a constructor, the OpenGL object is by
* default not deleted on destruction, use @p flags for different
* behavior.
* @see @ref release()
*/
static Shader wrap(Type type, GLuint id, ObjectFlags flags = {}) {
return Shader{type, id, flags};
}
/**
* @brief Constructor
@ -530,7 +552,8 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject {
* corresponding to @p version parameter at the beginning. If
* @ref Version::None is specified, (not) adding the @glsl #version @ce
* directive is left to the user.
* @see @fn_gl_keyword{CreateShader}
* @see @ref Shader(NoCreateT), @ref wrap(),
* @fn_gl_keyword{CreateShader}
*/
explicit Shader(Version version, Type type);
@ -559,7 +582,7 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject {
* @brief Destructor
*
* Deletes associated OpenGL shader.
* @see @fn_gl_keyword{DeleteShader}
* @see @ref wrap(), @ref release(), @fn_gl_keyword{DeleteShader}
*/
~Shader();
@ -572,6 +595,18 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject {
/** @brief OpenGL shader ID */
GLuint id() const { return _id; }
/**
* @brief Release the underlying OpenGL object
* @m_since_latest
*
* Releases ownership of the OpenGL shader object and returns its ID so
* it's not deleted on destruction. The internal state is then
* equivalent to a moved-from state.
* @see @ref wrap()
*/
/* MinGW complains loudly if the declaration doesn't also have inline */
inline GLuint release();
#ifndef MAGNUM_TARGET_WEBGL
/**
* @brief Shader label
@ -634,16 +669,80 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject {
Shader& addFile(const std::string& filename);
/**
* @brief Compile shader
* @brief Compile the shader
*
* Compiles single shader. Prefer to compile multiple shaders at once
* using @ref compile(std::initializer_list<Containers::Reference<Shader>>)
* for improved performance, see its documentation for more
* information.
* Calls @ref submitCompile(), immediately followed by
* @ref checkCompile(), passing back its return value. See
* documentation of those two functions for details.
*/
bool compile();
/**
* @brief Submit the shader for compilation
* @m_since_latest
*
* You can call @ref isCompileFinished() or @ref checkCompile() after,
* but it's recommended to instead immediately call
* @ref AbstractShaderProgram::attachShader() and
* @relativeref{AbstractShaderProgram,submitLink()}, then optionally
* continue with @relativeref{AbstractShaderProgram,isLinkFinished()}
* and pass all input shaders to
* @relativeref{AbstractShaderProgram,checkLink()} on the final program
* --- if compilation would fail, subsequent linking will as well, and
* @relativeref{AbstractShaderProgram,checkLink()} will print the
* compilation error if linking failed due to that. See
* @ref GL-AbstractShaderProgram-async for more information.
* @see @fn_gl_keyword{ShaderSource}, @fn_gl_keyword{CompileShader}
*/
void submitCompile();
/**
* @brief Check shader compilation status and await completion
* @m_since_latest
*
* Has to be called only if @ref submitCompile() was called before.
* It's however recommended to instead immediately call
* @ref AbstractShaderProgram::attachShader() and
* @relativeref{AbstractShaderProgram,submitLink()}, then optionally
* continue with @relativeref{AbstractShaderProgram,isLinkFinished()}
* and pass all input shaders to
* @relativeref{AbstractShaderProgram,checkLink()} on the final program
* --- if compilation would fail, subsequent linking will as well, and
* @relativeref{AbstractShaderProgram,checkLink()} will print the
* compilation error if linking failed due to that. See
* @ref GL-AbstractShaderProgram-async for more information.
* @see @fn_gl_keyword{GetShader} with @def_gl{COMPILE_STATUS} and
* @def_gl{INFO_LOG_LENGTH}, @fn_gl_keyword{GetShaderInfoLog}
*/
bool checkCompile();
/**
* @brief Whether a @ref submitCompile() operation has finished
* @m_since_latest
*
* Has to be called only if @ref submitCompile() was called before, and
* before @ref checkCompile(). If returns @cpp false @ce, a subsequent
* @ref checkCompile() call will block until the compilation is
* finished. If @gl_extension{KHR,parallel_shader_compile} is not
* available, the function always returns @cpp true @ce --- i.e., as if
* the compilation was done synchronously.
*
* It's however recommended to wait only for the final link to finish,
* and not for particular compilations --- i.e., right after
* @ref submitCompile() continue with
* @ref AbstractShaderProgram::attachShader() and
* @relativeref{AbstractShaderProgram,submitLink()}, and then check
* with @relativeref{AbstractShaderProgram,isLinkFinished()} on the
* final program. See @ref GL-AbstractShaderProgram-async for more
* information.
* @see @fn_gl_keyword{GetProgram} with
* @def_gl_extension{COMPLETION_STATUS,KHR,parallel_shader_compile}
*/
bool isCompileFinished();
private:
explicit Shader(Type type, GLuint id, ObjectFlags flags) noexcept;
void MAGNUM_GL_LOCAL addSourceImplementationDefault(std::string source);
#if defined(CORRADE_TARGET_EMSCRIPTEN) && defined(__EMSCRIPTEN_PTHREADS__)
void MAGNUM_GL_LOCAL addSourceImplementationEmscriptenPthread(std::string source);
@ -654,8 +753,11 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject {
static MAGNUM_GL_LOCAL void cleanLogImplementationIntelWindows(std::string& message);
#endif
MAGNUM_GL_LOCAL static void APIENTRY completionStatusImplementationFallback(GLuint, GLenum, GLint*);
Type _type;
GLuint _id;
ObjectFlags _flags;
std::vector<std::string> _sources;
};
@ -663,7 +765,7 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject {
/** @debugoperatorclassenum{Shader,Shader::Type} */
MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, Shader::Type value);
inline Shader::Shader(Shader&& other) noexcept: _type(other._type), _id(other._id), _sources(std::move(other._sources)) {
inline Shader::Shader(Shader&& other) noexcept: _type{other._type}, _id{other._id}, _flags{other._flags}, _sources{std::move(other._sources)} {
other._id = 0;
}
@ -671,10 +773,17 @@ inline Shader& Shader::operator=(Shader&& other) noexcept {
using std::swap;
swap(_type, other._type);
swap(_id, other._id);
swap(_flags, other._flags);
swap(_sources, other._sources);
return *this;
}
inline GLuint Shader::release() {
const GLuint id = _id;
_id = 0;
return id;
}
}}
#endif

233
src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp

@ -24,11 +24,14 @@
*/
#include <sstream>
#include <Corrade/Containers/Iterable.h>
#include <Corrade/Containers/Reference.h>
#include <Corrade/Containers/StringStl.h> /** @todo remove when Shader is <string>-free */
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/TestSuite/Compare/String.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/Resource.h>
#include <Corrade/Utility/System.h>
#include "Magnum/Image.h"
#include "Magnum/ImageView.h"
@ -68,8 +71,12 @@ struct AbstractShaderProgramGLTest: OpenGLTester {
#ifndef MAGNUM_TARGET_GLES
void createMultipleOutputsIndexed();
#endif
void createAsync();
void linkFailure();
void linkFailureAsync();
void linkFailureAsyncShaderList();
void uniformNotFound();
void uniform();
@ -103,12 +110,15 @@ AbstractShaderProgramGLTest::AbstractShaderProgramGLTest() {
#endif
&AbstractShaderProgramGLTest::create,
&AbstractShaderProgramGLTest::createAsync,
&AbstractShaderProgramGLTest::createMultipleOutputs,
#ifndef MAGNUM_TARGET_GLES
&AbstractShaderProgramGLTest::createMultipleOutputsIndexed,
#endif
&AbstractShaderProgramGLTest::linkFailure,
&AbstractShaderProgramGLTest::linkFailureAsync,
&AbstractShaderProgramGLTest::linkFailureAsyncShaderList,
&AbstractShaderProgramGLTest::uniformNotFound,
&AbstractShaderProgramGLTest::uniform,
@ -205,6 +215,8 @@ struct MyPublicShader: AbstractShaderProgram {
using AbstractShaderProgram::bindFragmentDataLocation;
#endif
using AbstractShaderProgram::link;
using AbstractShaderProgram::submitLink;
using AbstractShaderProgram::checkLink;
using AbstractShaderProgram::uniformLocation;
#ifndef MAGNUM_TARGET_GLES2
using AbstractShaderProgram::uniformBlockIndex;
@ -257,6 +269,80 @@ void AbstractShaderProgramGLTest::create() {
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_VERIFY(linked);
// Some drivers need a bit of time to update this result
Utility::System::sleep(200);
CORRADE_VERIFY(program.isLinkFinished());
{
#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(valid);
}
const Int matrixUniform = program.uniformLocation("matrix");
const Int multiplierUniform = program.uniformLocation("multiplier");
const Int colorUniform = program.uniformLocation("color");
const Int additionsUniform = program.uniformLocation("additions");
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_VERIFY(matrixUniform >= 0);
CORRADE_VERIFY(multiplierUniform >= 0);
CORRADE_VERIFY(colorUniform >= 0);
CORRADE_VERIFY(additionsUniform >= 0);
}
void AbstractShaderProgramGLTest::createAsync() {
Utility::Resource rs("AbstractShaderProgramGLTest");
Shader vert(
#ifndef MAGNUM_TARGET_GLES
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
#else
Version::GLES200
#endif
, Shader::Type::Vertex);
vert.addSource(rs.getString("MyShader.vert"));
const bool vertCompiled = vert.compile();
Shader frag(
#ifndef MAGNUM_TARGET_GLES
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
#else
Version::GLES200
#endif
, Shader::Type::Fragment);
frag.addSource(rs.getString("MyShader.frag"));
const bool fragCompiled = frag.compile();
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_VERIFY(vertCompiled);
CORRADE_VERIFY(fragCompiled);
MyPublicShader program;
program.attachShaders({vert, frag});
MAGNUM_VERIFY_NO_GL_ERROR();
program.bindAttributeLocation(0, "position");
program.submitLink();
while(!program.isLinkFinished())
Utility::System::sleep(100);
CORRADE_VERIFY(program.checkLink({vert, frag}));
CORRADE_VERIFY(program.isLinkFinished());
const bool valid = program.validate().first;
MAGNUM_VERIFY_NO_GL_ERROR();
{
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
@ -402,6 +488,7 @@ void AbstractShaderProgramGLTest::linkFailure() {
, Shader::Type::Fragment);
shader.addSource("[fu] bleh error #:! stuff\n");
/* The compilation should fail */
{
Error redirectError{nullptr};
CORRADE_VERIFY(!shader.compile());
@ -409,9 +496,127 @@ void AbstractShaderProgramGLTest::linkFailure() {
MyPublicShader program;
program.attachShaders({shader});
/* And thus linking as well, saying something like "error: linking with
uncompiled/unspecialized shader" */
std::ostringstream out;
{
Error redirectError{&out};
CORRADE_VERIFY(!program.link());
}
Utility::System::sleep(200);
CORRADE_VERIFY(program.isLinkFinished());
CORRADE_COMPARE_AS(out.str(), "GL::AbstractShaderProgram::link(): linking failed with the following message:",
TestSuite::Compare::StringHasPrefix);
}
void AbstractShaderProgramGLTest::linkFailureAsync() {
Shader shader(
#ifndef MAGNUM_TARGET_GLES
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
#else
Version::GLES200
#endif
, Shader::Type::Fragment);
shader.addSource("[fu] bleh error #:! stuff\n");
/* The compilation should fail */
{
Error redirectError{nullptr};
CORRADE_VERIFY(!shader.compile());
}
MyPublicShader program;
program.attachShaders({shader});
/* The link submission should not print anything ... */
std::ostringstream out;
{
Error redirectError{&out};
program.submitLink();
}
while(!program.isLinkFinished())
Utility::System::sleep(100);
CORRADE_VERIFY(out.str().empty());
/* ... only the final check should. In this case it's "error: linking with
uncompiled/unspecialized shader" as well, but if the shaders would be
supplied like in linkFailureAsyncShaderList() below, it'd print the
shader failure instead. */
{
Error redirectError{&out};
CORRADE_VERIFY(!program.checkLink({}));
}
CORRADE_VERIFY(program.isLinkFinished());
CORRADE_COMPARE_AS(out.str(), "GL::AbstractShaderProgram::link(): linking failed with the following message:",
TestSuite::Compare::StringHasPrefix);
}
void AbstractShaderProgramGLTest::linkFailureAsyncShaderList() {
Shader vert(
#ifndef MAGNUM_TARGET_GLES
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
#else
Version::GLES200
#endif
, Shader::Type::Vertex);
vert.addSource("void main() {}\n");
Shader frag(
#ifndef MAGNUM_TARGET_GLES
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
#else
Version::GLES200
#endif
, Shader::Type::Fragment);
frag.addSource("[fu] bleh error #:! stuff\n");
vert.submitCompile();
frag.submitCompile();
MyPublicShader program;
program.attachShaders({vert, frag});
/* The link submission should not print anything ... */
{
std::ostringstream out;
Error redirectError{&out};
program.submitLink();
CORRADE_VERIFY(out.str().empty());
}
/* ... only the final check should. Vertex shader should be fine, but
fragment should fail. */
std::ostringstream out;
{
Error redirectError{&out};
CORRADE_VERIFY(!program.checkLink({vert, frag}));
}
CORRADE_COMPARE_AS(out.str(), "GL::Shader::compile(): compilation of fragment shader failed with the following message:",
TestSuite::Compare::StringHasPrefix);
/* The linker error (which would most probably say something like "error:
linking with uncompiled/unspecialized shader") should not be even
printed */
CORRADE_COMPARE_AS(out.str(), "GL::AbstractShaderProgram::link(): linking failed with the following message:",
TestSuite::Compare::StringNotContains);
}
void AbstractShaderProgramGLTest::uniformNotFound() {
MyPublicShader program;
@ -447,7 +652,8 @@ void AbstractShaderProgramGLTest::uniformNotFound() {
#endif
);
CORRADE_VERIFY(Shader::compile({vert, frag}));
CORRADE_VERIFY(vert.compile() && frag.compile());
program.attachShaders({vert, frag});
CORRADE_VERIFY(program.link());
@ -497,10 +703,10 @@ MyShader::MyShader() {
Version::GLES200
#endif
, Shader::Type::Fragment);
vert.addSource(rs.getString("MyShader.vert"));
frag.addSource(rs.getString("MyShader.frag"));
Shader::compile({vert, frag});
vert.addSource(rs.getString("MyShader.vert"))
.compile();
frag.addSource(rs.getString("MyShader.frag"))
.compile();
attachShaders({vert, frag});
@ -581,10 +787,10 @@ MyDoubleShader::MyDoubleShader() {
Shader vert(Version::GL320, Shader::Type::Vertex);
Shader frag(Version::GL320, Shader::Type::Fragment);
vert.addSource(rs.getString("MyDoubleShader.vert"));
frag.addSource(rs.getString("MyDoubleShader.frag"));
Shader::compile({vert, frag});
vert.addSource(rs.getString("MyDoubleShader.vert"))
.compile();
frag.addSource(rs.getString("MyDoubleShader.frag"))
.compile();
attachShaders({vert, frag});
@ -743,8 +949,8 @@ void AbstractShaderProgramGLTest::uniformBlockIndexNotFound() {
vert.addSource("void main() { gl_Position = vec4(0.0); }");
frag.addSource("out lowp vec4 color;\n"
"void main() { color = vec4(1.0); }");
CORRADE_VERIFY(vert.compile() && frag.compile());
CORRADE_VERIFY(Shader::compile({vert, frag}));
program.attachShaders({vert, frag});
CORRADE_VERIFY(program.link());
@ -784,10 +990,11 @@ UniformBlockShader::UniformBlockShader() {
Version::GLES300
#endif
, Shader::Type::Fragment);
vert.addSource(rs.getString("UniformBlockShader.vert"));
frag.addSource(rs.getString("UniformBlockShader.frag"));
vert.addSource(rs.getString("UniformBlockShader.vert"))
.compile();
frag.addSource(rs.getString("UniformBlockShader.frag"))
.compile();
Shader::compile({vert, frag});
attachShaders({vert, frag});
link();

13
src/Magnum/GL/Test/MeshGLTest.cpp

@ -25,6 +25,7 @@
*/
#include <sstream>
#include <Corrade/Containers/Iterable.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/TestSuite/Compare/Numeric.h>
#include <Corrade/Utility/DebugStl.h>
@ -1023,7 +1024,7 @@ FloatShader::FloatShader(const std::string& type, const std::string& conversion)
"#endif\n"
"void main() { result = " + conversion + "; }\n");
CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
attachShaders({vert, frag});
@ -1065,7 +1066,7 @@ IntegerShader::IntegerShader(const std::string& type) {
"out mediump " + type + " result;\n"
"void main() { result = valueInterpolated; }\n");
CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
attachShaders({vert, frag});
@ -1100,7 +1101,7 @@ DoubleShader::DoubleShader(const std::string& type, const std::string& outputTyp
"out " + outputType + " result;\n"
"void main() { result = valueInterpolated; }\n");
CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
attachShaders({vert, frag});
@ -2147,7 +2148,7 @@ MultipleShader::MultipleShader() {
"#endif\n"
"void main() { result = valueInterpolated; }\n");
CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
attachShaders({vert, frag});
@ -3836,7 +3837,7 @@ MultiDrawShader::MultiDrawShader(bool vertexId, bool drawId) {
"#endif\n"
"void main() { result.r = valueInterpolated; }\n");
CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
attachShaders({vert, frag});
@ -4641,7 +4642,7 @@ MultiDrawInstancedShader::MultiDrawInstancedShader(bool vertexId, bool drawId
"#endif\n"
"void main() { result.r = valueInterpolated; }\n");
CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
attachShaders({vert, frag});

4
src/Magnum/GL/Test/RendererGLTest.cpp

@ -23,9 +23,9 @@
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/Containers/Iterable.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/String.h>
#include <Corrade/Containers/Reference.h>
#include <Corrade/PluginManager/Manager.h>
#include <Corrade/Utility/Path.h>
@ -203,7 +203,7 @@ void RendererGLTest::pointCoord() {
color = vec4(gl_PointCoord.x, gl_PointCoord.y, 0.0, 1.0);
})GLSL");
CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
attachShaders({vert, frag});

4
src/Magnum/GL/Test/SampleQueryGLTest.cpp

@ -23,7 +23,7 @@
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/Containers/Reference.h>
#include <Corrade/Containers/Iterable.h>
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Resource.h>
@ -207,7 +207,7 @@ MyShader::MyShader() {
" color = vec4(1.0, 1.0, 1.0, 1.0);\n"
"}\n");
CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
attachShaders({vert, frag});

117
src/Magnum/GL/Test/ShaderGLTest.cpp

@ -23,8 +23,11 @@
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/Containers/StringStl.h> /** @todo remove once Shader is <string>-free */
#include <Corrade/TestSuite/Compare/String.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/System.h>
#include <Corrade/Utility/Path.h>
#include "Magnum/GL/Context.h"
@ -46,6 +49,7 @@ struct ShaderGLTest: OpenGLTester {
void construct();
void constructNoVersion();
void constructMove();
void wrap();
#ifndef MAGNUM_TARGET_WEBGL
void label();
@ -55,6 +59,9 @@ struct ShaderGLTest: OpenGLTester {
void addSourceNoVersion();
void addFile();
void compile();
void compileAsync();
void compileFailure();
void compileFailureAsync();
void compileUtf8();
void compileNoVersion();
};
@ -63,6 +70,7 @@ ShaderGLTest::ShaderGLTest() {
addTests({&ShaderGLTest::construct,
&ShaderGLTest::constructNoVersion,
&ShaderGLTest::constructMove,
&ShaderGLTest::wrap,
#ifndef MAGNUM_TARGET_WEBGL
&ShaderGLTest::label,
@ -72,6 +80,9 @@ ShaderGLTest::ShaderGLTest() {
&ShaderGLTest::addSourceNoVersion,
&ShaderGLTest::addFile,
&ShaderGLTest::compile,
&ShaderGLTest::compileAsync,
&ShaderGLTest::compileFailure,
&ShaderGLTest::compileFailureAsync,
&ShaderGLTest::compileUtf8,
&ShaderGLTest::compileNoVersion});
}
@ -151,6 +162,20 @@ void ShaderGLTest::constructMove() {
CORRADE_VERIFY(std::is_nothrow_move_assignable<Shader>::value);
}
void ShaderGLTest::wrap() {
GLuint id = glCreateShader(GL_FRAGMENT_SHADER);
/* Releasing won't delete anything */
{
auto shader = Shader::wrap(Shader::Type::Fragment, id, ObjectFlag::DeleteOnDestruction);
CORRADE_COMPARE(shader.release(), id);
}
/* ...so we can wrap it again */
Shader::wrap(Shader::Type::Fragment, id);
glDeleteShader(id);
}
#ifndef MAGNUM_TARGET_WEBGL
void ShaderGLTest::label() {
/* No-Op version is tested in AbstractObjectGLTest */
@ -276,11 +301,97 @@ void ShaderGLTest::compile() {
Shader shader(v, Shader::Type::Fragment);
shader.addSource("void main() {}\n");
CORRADE_VERIFY(shader.compile());
CORRADE_VERIFY(shader.isCompileFinished());
}
void ShaderGLTest::compileAsync() {
#ifndef MAGNUM_TARGET_GLES
constexpr Version v =
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
;
#else
constexpr Version v = Version::GLES200;
#endif
Shader shader(v, Shader::Type::Fragment);
shader.addSource("void main() {}\n");
shader.submitCompile();
while(!shader.isCompileFinished())
Utility::System::sleep(100);
CORRADE_VERIFY(shader.checkCompile());
CORRADE_VERIFY(shader.isCompileFinished());
}
void ShaderGLTest::compileFailure() {
#ifndef MAGNUM_TARGET_GLES
constexpr Version v =
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
;
#else
constexpr Version v = Version::GLES200;
#endif
Shader shader(v, Shader::Type::Vertex);
shader.addSource("[fu] bleh error #:! stuff\n");
std::ostringstream out;
{
Error redirectError{&out};
CORRADE_VERIFY(!shader.compile());
}
CORRADE_VERIFY(shader.isCompileFinished());
CORRADE_COMPARE_AS(out.str(), "GL::Shader::compile(): compilation of vertex shader failed with the following message:",
TestSuite::Compare::StringHasPrefix);
}
void ShaderGLTest::compileFailureAsync() {
#ifndef MAGNUM_TARGET_GLES
constexpr Version v =
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
;
#else
constexpr Version v = Version::GLES200;
#endif
Shader shader(v, Shader::Type::Fragment);
shader.addSource("[fu] bleh error #:! stuff\n");
/* The compile submission should not print anything ... */
std::ostringstream out;
{
Error redirectError{&out};
shader.submitCompile();
}
Shader shader2(v, Shader::Type::Fragment);
shader2.addSource("[fu] bleh error #:! stuff\n");
CORRADE_VERIFY(!shader2.compile());
while(!shader.isCompileFinished())
Utility::System::sleep(100);
CORRADE_VERIFY(out.str().empty());
/* ... only the final check should */
{
Error redirectError{&out};
CORRADE_VERIFY(!shader.checkCompile());
}
CORRADE_VERIFY(shader.isCompileFinished());
CORRADE_COMPARE_AS(out.str(), "GL::Shader::compile(): compilation of fragment shader failed with the following message:",
TestSuite::Compare::StringHasPrefix);
}
void ShaderGLTest::compileUtf8() {

7
src/Magnum/GL/Test/TransformFeedbackGLTest.cpp

@ -24,7 +24,7 @@
*/
#include <tuple>
#include <Corrade/Containers/Reference.h>
#include <Corrade/Containers/Iterable.h>
#include "Magnum/Image.h"
#include "Magnum/GL/AbstractShaderProgram.h"
@ -635,7 +635,8 @@ void TransformFeedbackGLTest::draw() {
else geom.addSource(
" EmitVertex();\n");
geom.addSource("}\n");
CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, geom}));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && geom.compile());
attachShaders({vert, geom});
setTransformFeedbackOutputs({"geomOutput"}, TransformFeedbackBufferMode::SeparateAttributes);
CORRADE_INTERNAL_ASSERT_OUTPUT(link());
@ -696,8 +697,8 @@ void TransformFeedbackGLTest::draw() {
"void main() {\n"
" outputData = interleaved.x + 2*interleaved.y;\n"
"}\n");
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag}));
attachShaders({vert, frag});
bindAttributeLocation(Input::Location, "inputData");
CORRADE_INTERNAL_ASSERT_OUTPUT(link());

60
src/Magnum/Magnum.h

@ -131,7 +131,7 @@ Defined if the engine is built with OpenGL interoperability enabled --- extra
APIs in various libraries interacting with the @ref Magnum::GL "GL" library.
Enabled by default in case the @ref Magnum::GL "GL" library is built.
@see @ref MAGNUM_TARGET_GLES2, @ref MAGNUM_TARGET_GLES3,
@ref MAGNUM_TARGET_DESKTOP_GLES, @ref building, @ref cmake
@ref MAGNUM_TARGET_EGL, @ref building, @ref cmake
*/
#define MAGNUM_TARGET_GL
/* (enabled by default) */
@ -141,7 +141,7 @@ Enabled by default in case the @ref Magnum::GL "GL" library is built.
Defined if the engine is built for OpenGL ES 3.0 or OpenGL ES 2.0.
@see @ref MAGNUM_TARGET_GLES2, @ref MAGNUM_TARGET_GLES3,
@ref MAGNUM_TARGET_DESKTOP_GLES, @ref building, @ref cmake
@ref MAGNUM_TARGET_EGL, @ref building, @ref cmake
*/
#define MAGNUM_TARGET_GLES
#undef MAGNUM_TARGET_GLES
@ -151,7 +151,7 @@ Defined if the engine is built for OpenGL ES 3.0 or OpenGL ES 2.0.
Defined if the engine is built for OpenGL ES 2.0. Implies also
@ref MAGNUM_TARGET_GLES.
@see @ref MAGNUM_TARGET_GLES3, @ref MAGNUM_TARGET_DESKTOP_GLES, @ref building,
@see @ref MAGNUM_TARGET_GLES3, @ref MAGNUM_TARGET_EGL, @ref building,
@ref cmake
*/
#define MAGNUM_TARGET_GLES2
@ -162,23 +162,12 @@ Defined if the engine is built for OpenGL ES 2.0. Implies also
Defined if the engine is built for OpenGL ES 3.0. Implies also
@ref MAGNUM_TARGET_GLES.
@see @ref MAGNUM_TARGET_GLES2, @ref MAGNUM_TARGET_DESKTOP_GLES, @ref building,
@see @ref MAGNUM_TARGET_GLES2, @ref MAGNUM_TARGET_EGL, @ref building,
@ref cmake
*/
#define MAGNUM_TARGET_GLES3
#undef MAGNUM_TARGET_GLES3
/**
@brief Desktop emulation of OpenGL ES target
Defined if the engine is built for OpenGL ES 3.0 or OpenGL ES 2.0 emulated
within standard desktop OpenGL. Implies also @ref MAGNUM_TARGET_GLES.
@see @ref MAGNUM_TARGET_GLES2, @ref MAGNUM_TARGET_GLES3, @ref building,
@ref cmake
*/
#define MAGNUM_TARGET_DESKTOP_GLES
#undef MAGNUM_TARGET_DESKTOP_GLES
/**
@brief WebGL target
@ -193,19 +182,48 @@ which you might want to be aware of. Implies also @ref MAGNUM_TARGET_GLES and
#define MAGNUM_TARGET_WEBGL
#undef MAGNUM_TARGET_WEBGL
/**
@brief EGL target
@m_since_latest
Defined if the engine is built for EGL instead of a platform-specific OpenGL
support library like CGL, EAGL, GLX or WGL. When enabled,
@relativeref{Magnum,Platform::Sdl2Application} and
@relativeref{Magnum,Platform::GlfwApplication}
will create the context using EGL, and command-line utilities like
@ref magnum-gl-info "magnum-gl-info" or
@ref magnum-distancefieldconverter "magnum-distancefieldconverter" as well as
the @relativeref{Magnum,GL::OpenGLTester} library will use
@relativeref{Magnum,Platform::WindowlessEglApplication}. Defined implicitly on
@ref CORRADE_TARGET_IOS "iOS", @ref CORRADE_TARGET_ANDROID "Android",
@ref CORRADE_TARGET_EMSCRIPTEN "Emscripten" and
@ref CORRADE_TARGET_WINDOWS_RT "Windows RT".
*/
#define MAGNUM_TARGET_EGL
#undef MAGNUM_TARGET_EGL
#ifdef MAGNUM_BUILD_DEPRECATED
/**
@brief Headless target
@m_deprecated_since_latest Use @ref MAGNUM_TARGET_EGL instead.
Defined if the engine is built for use on a headless machine (without any
graphical desktop environment). Basically it means that EGL with no display
attachment is being used everywhere instead of platform-specific toolkits like
CGL, GLX or WGL. Note that this might not be supported on all platforms, see
@ref Magnum::Platform::WindowlessEglApplication "Platform::WindowlessEglApplication"
for more information.
Alias to @ref MAGNUM_TARGET_EGL, unless on iOS, Android, Emscripten or Windows
RT.
*/
#define MAGNUM_TARGET_HEADLESS
#undef MAGNUM_TARGET_HEADLESS
/**
@brief OpenGL ES target on GLX / WGL
@m_deprecated_since_latest Use @ref MAGNUM_TARGET_EGL instead.
Defined if @ref MAGNUM_TARGET_GLES is set but @ref MAGNUM_TARGET_EGL isn't,
unless on iOS, Android, Emscripten or Windows RT.
*/
#define MAGNUM_TARGET_DESKTOP_GLES
#undef MAGNUM_TARGET_DESKTOP_GLES
#endif
/**
@brief Vulkan interoperability

2
src/Magnum/Math/Complex.h

@ -26,7 +26,7 @@
*/
/** @file
* @brief Class @ref Magnum::Math::Complex, function @ref Magnum::Math::dot(), @ref Magnum::math::angle()
* @brief Class @ref Magnum::Math::Complex, function @ref Magnum::Math::dot(), @ref Magnum::Math::angle()
*/
#include <Corrade/Utility/Assert.h>

6
src/Magnum/Math/FunctionsBatch.h

@ -40,9 +40,9 @@ namespace Implementation {
/** @todo Utility/Algorithms.h has a similar (but different) variant of this,
maybe turn that into some public utility once we have one more use case? */
template<class T, class View = decltype(Corrade::Containers::Implementation::ErasedArrayViewConverter<typename std::remove_reference<T&&>::type>::from(std::declval<T&&>()))> static auto stridedArrayViewTypeFor(T&&) -> typename View::Type;
template<class T> static T stridedArrayViewTypeFor(const Corrade::Containers::ArrayView<T>&);
template<class T> static T stridedArrayViewTypeFor(const Corrade::Containers::StridedArrayView1D<T>&);
template<class T, class View = decltype(Corrade::Containers::Implementation::ErasedArrayViewConverter<typename std::remove_reference<T&&>::type>::from(std::declval<T&&>()))> static auto stridedArrayViewTypeFor(T&&) -> typename std::remove_const<typename View::Type>::type;
template<class T> static typename std::remove_const<T>::type stridedArrayViewTypeFor(const Corrade::Containers::ArrayView<T>&);
template<class T> static typename std::remove_const<T>::type stridedArrayViewTypeFor(const Corrade::Containers::StridedArrayView1D<T>&);
}

21
src/Magnum/Math/Test/FunctionsBatchTest.cpp

@ -24,6 +24,7 @@
*/
#include <vector>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/ArrayViewStl.h>
#include <Corrade/TestSuite/Tester.h>
@ -44,6 +45,8 @@ struct FunctionsBatchTest: Corrade::TestSuite::Tester {
void nanIgnoring();
void nanIgnoringVector();
void constIterable();
};
using namespace Literals;
@ -62,7 +65,9 @@ FunctionsBatchTest::FunctionsBatchTest() {
&FunctionsBatchTest::minmax,
&FunctionsBatchTest::nanIgnoring,
&FunctionsBatchTest::nanIgnoringVector});
&FunctionsBatchTest::nanIgnoringVector,
&FunctionsBatchTest::constIterable});
}
void FunctionsBatchTest::isInf() {
@ -284,6 +289,20 @@ void FunctionsBatchTest::nanIgnoringVector() {
CORRADE_COMPARE(Math::minmax(allNan).second[1], Constants::nan());
}
void FunctionsBatchTest::constIterable() {
const Vector2 data[]{{5, -3}, {-2, 14}, {9, -5}};
/* It shouldn't try to operate with a const type (such as trying to to
assign to `std::pair<std::size_t, const Vector2>`) internally, instead
it should remove the const */
CORRADE_COMPARE(Math::min(Corrade::Containers::arrayView(data)),
(Vector2{-2, -5}));
CORRADE_COMPARE(Math::max(Corrade::Containers::stridedArrayView(data)),
(Vector2{9, 14}));
CORRADE_COMPARE(Math::minmax(Corrade::Containers::Array<const Vector2>{data, 3, [](const Vector2*, std::size_t){}}),
std::make_pair(Vector2{-2, -5}, Vector2{9, 14}));
}
}}}}
CORRADE_TEST_MAIN(Magnum::Math::Test::FunctionsBatchTest)

10
src/Magnum/Mesh.cpp

@ -25,7 +25,7 @@
#include "Mesh.h"
#include <string>
#include <Corrade/Containers/String.h>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h>
@ -105,28 +105,28 @@ UnsignedInt meshIndexTypeSize(const MeshIndexType type) {
namespace Corrade { namespace Utility {
std::string ConfigurationValue<Magnum::MeshPrimitive>::toString(Magnum::MeshPrimitive value, ConfigurationValueFlags) {
Containers::String ConfigurationValue<Magnum::MeshPrimitive>::toString(Magnum::MeshPrimitive value, ConfigurationValueFlags) {
if(Magnum::UnsignedInt(value) - 1 < Containers::arraySize(Magnum::MeshPrimitiveNames))
return Magnum::MeshPrimitiveNames[Magnum::UnsignedInt(value) - 1];
return {};
}
Magnum::MeshPrimitive ConfigurationValue<Magnum::MeshPrimitive>::fromString(const std::string& stringValue, ConfigurationValueFlags) {
Magnum::MeshPrimitive ConfigurationValue<Magnum::MeshPrimitive>::fromString(Containers::StringView stringValue, ConfigurationValueFlags) {
for(std::size_t i = 0; i != Containers::arraySize(Magnum::MeshPrimitiveNames); ++i)
if(stringValue == Magnum::MeshPrimitiveNames[i]) return Magnum::MeshPrimitive(i + 1);
return {};
}
std::string ConfigurationValue<Magnum::MeshIndexType>::toString(Magnum::MeshIndexType value, ConfigurationValueFlags) {
Containers::String ConfigurationValue<Magnum::MeshIndexType>::toString(Magnum::MeshIndexType value, ConfigurationValueFlags) {
if(Magnum::UnsignedInt(value) - 1 < Containers::arraySize(Magnum::MeshIndexTypeNames))
return Magnum::MeshIndexTypeNames[Magnum::UnsignedInt(value) - 1];
return {};
}
Magnum::MeshIndexType ConfigurationValue<Magnum::MeshIndexType>::fromString(const std::string& stringValue, ConfigurationValueFlags) {
Magnum::MeshIndexType ConfigurationValue<Magnum::MeshIndexType>::fromString(Containers::StringView stringValue, ConfigurationValueFlags) {
for(std::size_t i = 0; i != Containers::arraySize(Magnum::MeshIndexTypeNames); ++i)
if(stringValue == Magnum::MeshIndexTypeNames[i]) return Magnum::MeshIndexType(i + 1);

9
src/Magnum/Mesh.h

@ -30,7 +30,6 @@
*/
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/StlForwardString.h>
#include "Magnum/Magnum.h"
#include "Magnum/visibility.h"
@ -365,14 +364,14 @@ template<> struct MAGNUM_EXPORT ConfigurationValue<Magnum::MeshPrimitive> {
*
* If the value is invalid, returns empty string.
*/
static std::string toString(Magnum::MeshPrimitive value, ConfigurationValueFlags);
static Containers::String toString(Magnum::MeshPrimitive value, ConfigurationValueFlags);
/**
* @brief Reads enum value as string
*
* If the value is invalid, returns a zero (invalid) primitive.
*/
static Magnum::MeshPrimitive fromString(const std::string& stringValue, ConfigurationValueFlags);
static Magnum::MeshPrimitive fromString(Containers::StringView stringValue, ConfigurationValueFlags);
};
/** @configurationvalue{Magnum::MeshIndexType} */
@ -384,14 +383,14 @@ template<> struct MAGNUM_EXPORT ConfigurationValue<Magnum::MeshIndexType> {
*
* If the value is invalid, returns empty string.
*/
static std::string toString(Magnum::MeshIndexType value, ConfigurationValueFlags);
static Containers::String toString(Magnum::MeshIndexType value, ConfigurationValueFlags);
/**
* @brief Read enum value as string
*
* If the value is invalid, returns a zero (invalid) type.
*/
static Magnum::MeshIndexType fromString(const std::string& stringValue, ConfigurationValueFlags);
static Magnum::MeshIndexType fromString(Containers::StringView stringValue, ConfigurationValueFlags);
};
}}

47
src/Magnum/MeshTools/Combine.cpp

@ -26,8 +26,7 @@
#include "Combine.h"
#include <numeric>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Containers/Reference.h>
#include <Corrade/Containers/Iterable.h>
#include <Corrade/Utility/Algorithms.h>
#include "Magnum/Math/Functions.h"
@ -44,20 +43,20 @@ Trade::MeshData combineIndexedImplementation(
#ifndef CORRADE_NO_ASSERT
const char* assertPrefix,
#endif
const MeshPrimitive primitive, Containers::Array<char>& combinedIndices, const UnsignedInt indexCount, const UnsignedInt indexStride, const Containers::ArrayView<const Containers::Reference<const Trade::MeshData>> data)
const MeshPrimitive primitive, Containers::Array<char>& combinedIndices, const UnsignedInt indexCount, const UnsignedInt indexStride, const Containers::Iterable<const Trade::MeshData> data)
{
/* Calculate attribute count and vertex stride */
UnsignedInt attributeCount = 0;
UnsignedInt vertexStride = 0;
for(std::size_t i = 0; i != data.size(); ++i) {
attributeCount += data[i]->attributeCount();
for(UnsignedInt j = 0; j != data[i]->attributeCount(); ++j) {
const VertexFormat format = data[i]->attributeFormat(j);
attributeCount += data[i].attributeCount();
for(UnsignedInt j = 0; j != data[i].attributeCount(); ++j) {
const VertexFormat format = data[i].attributeFormat(j);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format),
assertPrefix << "attribute" << j << "of mesh" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(format)),
(Trade::MeshData{MeshPrimitive::Points, 0}));
vertexStride += vertexFormatSize(format)*Math::max(data[i]->attributeArraySize(j), UnsignedShort{1});
vertexStride += vertexFormatSize(format)*Math::max(data[i].attributeArraySize(j), UnsignedShort{1});
}
}
@ -111,7 +110,7 @@ Trade::MeshData combineIndexedImplementation(
}
Trade::MeshData combineIndexedAttributes(const Containers::ArrayView<const Containers::Reference<const Trade::MeshData>> data) {
Trade::MeshData combineIndexedAttributes(const Containers::Iterable<const Trade::MeshData> data) {
CORRADE_ASSERT(!data.isEmpty(),
"MeshTools::combineIndexedAttributes(): no meshes passed",
(Trade::MeshData{MeshPrimitive{}, 0}));
@ -124,21 +123,21 @@ Trade::MeshData combineIndexedAttributes(const Containers::ArrayView<const Conta
UnsignedInt indexCount{};
UnsignedInt indexStride = 0;
for(std::size_t i = 0; i != data.size(); ++i) {
CORRADE_ASSERT(data[i]->isIndexed(),
CORRADE_ASSERT(data[i].isIndexed(),
"MeshTools::combineIndexedAttributes(): data" << i << "is not indexed",
(Trade::MeshData{MeshPrimitive{}, 0}));
const MeshIndexType indexType = data[i]->indexType();
const MeshIndexType indexType = data[i].indexType();
CORRADE_ASSERT(!isMeshIndexTypeImplementationSpecific(indexType),
"MeshTools::combineIndexedAttributes(): data" << i << "has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(indexType)),
(Trade::MeshData{MeshPrimitive{}, 0}));
if(i == 0) {
primitive = data[i]->primitive();
indexCount = data[i]->indexCount();
primitive = data[i].primitive();
indexCount = data[i].indexCount();
} else {
CORRADE_ASSERT(data[i]->primitive() == primitive,
"MeshTools::combineIndexedAttributes(): data" << i << "is" << data[i]->primitive() << "but expected" << primitive, (Trade::MeshData{MeshPrimitive{}, 0}));
CORRADE_ASSERT(data[i]->indexCount() == indexCount,
"MeshTools::combineIndexedAttributes(): data" << i << "has" << data[i]->indexCount() << "indices but expected" << indexCount, (Trade::MeshData{MeshPrimitive{}, 0}));
CORRADE_ASSERT(data[i].primitive() == primitive,
"MeshTools::combineIndexedAttributes(): data" << i << "is" << data[i].primitive() << "but expected" << primitive, (Trade::MeshData{MeshPrimitive{}, 0}));
CORRADE_ASSERT(data[i].indexCount() == indexCount,
"MeshTools::combineIndexedAttributes(): data" << i << "has" << data[i].indexCount() << "indices but expected" << indexCount, (Trade::MeshData{MeshPrimitive{}, 0}));
}
indexStride += meshIndexTypeSize(indexType);
}
@ -171,10 +170,6 @@ Trade::MeshData combineIndexedAttributes(const Containers::ArrayView<const Conta
primitive, combinedIndices, indexCount, indexStride, data);
}
Trade::MeshData combineIndexedAttributes(std::initializer_list<Containers::Reference<const Trade::MeshData>> data) {
return combineIndexedAttributes(Containers::arrayView(data));
}
Trade::MeshData combineFaceAttributes(const Trade::MeshData& mesh, const Trade::MeshData& faceAttributes) {
CORRADE_ASSERT(mesh.isIndexed(),
"MeshTools::combineFaceAttributes(): vertex mesh is not indexed",
@ -194,12 +189,11 @@ Trade::MeshData combineFaceAttributes(const Trade::MeshData& mesh, const Trade::
CORRADE_ASSERT(!isMeshIndexTypeImplementationSpecific(meshIndexType),
"MeshTools::combineFaceAttributes(): vertex mesh has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(meshIndexType)),
(Trade::MeshData{MeshPrimitive{}, 0}));
const UnsignedInt meshIndexSize = meshIndexTypeSize(mesh.indexType());
const UnsignedInt meshIndexSize = meshIndexTypeSize(meshIndexType);
UnsignedInt faceIndexSize;
if(faceAttributes.isIndexed()) {
const MeshIndexType faceIndexType = faceAttributes.indexType();
CORRADE_ASSERT(!isMeshIndexTypeImplementationSpecific(faceIndexType),
"MeshTools::combineFaceAttributes(): face mesh has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(faceIndexType)),
CORRADE_ASSERT(!isMeshIndexTypeImplementationSpecific(faceAttributes.indexType()),
"MeshTools::combineFaceAttributes(): face mesh has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(faceAttributes.indexType())),
(Trade::MeshData{MeshPrimitive{}, 0}));
faceIndexSize = meshIndexTypeSize(faceAttributes.indexType());
} else faceIndexSize = 4;
@ -236,10 +230,9 @@ Trade::MeshData combineFaceAttributes(const Trade::MeshData& mesh, const Trade::
#ifndef CORRADE_NO_ASSERT
"MeshTools::combineFaceAttributes():",
#endif
mesh.primitive(), combinedIndices, meshIndexCount, indexStride,
Containers::arrayView<const Containers::Reference<const Trade::MeshData>>({
mesh.primitive(), combinedIndices, meshIndexCount, indexStride, {
mesh, faceAttributes
}));
});
}
Trade::MeshData combineFaceAttributes(const Trade::MeshData& mesh, Containers::ArrayView<const Trade::MeshAttributeData> faceAttributes) {

15
src/Magnum/MeshTools/Combine.h

@ -35,6 +35,13 @@
#include "Magnum/MeshTools/visibility.h"
#include "Magnum/Trade/Trade.h"
#ifdef MAGNUM_BUILD_DEPRECATED
/* combineIndexedAttributes() used to take an ArrayView<Reference<MeshData>>,
now it's through the Iterable class. Include it explicitly until people
learn to include it themselves. */
#include <Corrade/Containers/Iterable.h>
#endif
namespace Magnum { namespace MeshTools {
/**
@ -88,13 +95,7 @@ implementation-specific format.
@see @ref isMeshIndexTypeImplementationSpecific(),
@ref isVertexFormatImplementationSpecific()
*/
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData combineIndexedAttributes(const Containers::ArrayView<const Containers::Reference<const Trade::MeshData>> data);
/**
* @overload
* @m_since{2020,06}
*/
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData combineIndexedAttributes(std::initializer_list<Containers::Reference<const Trade::MeshData>> data);
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData combineIndexedAttributes(const Containers::Iterable<const Trade::MeshData> data);
/**
@brief Combine per-face attributes into an existing mesh

38
src/Magnum/MeshTools/Concatenate.cpp

@ -33,7 +33,7 @@ namespace Magnum { namespace MeshTools {
namespace Implementation {
std::pair<UnsignedInt, UnsignedInt> concatenateIndexVertexCount(Containers::ArrayView<const Containers::Reference<const Trade::MeshData>> meshes) {
std::pair<UnsignedInt, UnsignedInt> concatenateIndexVertexCount(Containers::Iterable<const Trade::MeshData> meshes) {
UnsignedInt indexCount = 0;
UnsignedInt vertexCount = 0;
for(const Trade::MeshData& mesh: meshes) {
@ -62,7 +62,7 @@ struct MeshAttributeHash: std::hash<typename std::underlying_type<Trade::MeshAtt
}
};
Trade::MeshData concatenate(Containers::Array<char>&& indexData, const UnsignedInt vertexCount, Containers::Array<char>&& vertexData, Containers::Array<Trade::MeshAttributeData>&& attributeData, const Containers::ArrayView<const Containers::Reference<const Trade::MeshData>> meshes, const char* const assertPrefix) {
Trade::MeshData concatenate(Containers::Array<char>&& indexData, const UnsignedInt vertexCount, Containers::Array<char>&& vertexData, Containers::Array<Trade::MeshAttributeData>&& attributeData, const Containers::Iterable<const Trade::MeshData> meshes, const char* const assertPrefix) {
#ifdef CORRADE_NO_ASSERT
static_cast<void>(assertPrefix);
#endif
@ -82,17 +82,17 @@ Trade::MeshData concatenate(Containers::Array<char>&& indexData, const UnsignedI
/** @todo delegate to `indexTriangleStrip()` (`duplicate*()`?) etc when
those are done */
CORRADE_ASSERT(
meshes.front()->primitive() != MeshPrimitive::LineStrip &&
meshes.front()->primitive() != MeshPrimitive::LineLoop &&
meshes.front()->primitive() != MeshPrimitive::TriangleStrip &&
meshes.front()->primitive() != MeshPrimitive::TriangleFan,
assertPrefix << meshes.front()->primitive() << "is not supported, turn it into a plain indexed mesh first",
meshes.front().primitive() != MeshPrimitive::LineStrip &&
meshes.front().primitive() != MeshPrimitive::LineLoop &&
meshes.front().primitive() != MeshPrimitive::TriangleStrip &&
meshes.front().primitive() != MeshPrimitive::TriangleFan,
assertPrefix << meshes.front().primitive() << "is not supported, turn it into a plain indexed mesh first",
(Trade::MeshData{MeshPrimitive{}, 0}));
/* Populate the resulting instance with what we have. It'll be used below
for convenient access to vertex / index data */
auto indices = Containers::arrayCast<UnsignedInt>(indexData);
Trade::MeshData out{meshes.front()->primitive(),
Trade::MeshData out{meshes.front().primitive(),
/* If the index array is empty, we're creating a non-indexed mesh (not
an indexed mesh with zero indices) */
std::move(indexData), indices.isEmpty() ?
@ -189,13 +189,13 @@ Trade::MeshData concatenate(Containers::Array<char>&& indexData, const UnsignedI
}
Trade::MeshData concatenate(const Containers::ArrayView<const Containers::Reference<const Trade::MeshData>> meshes, const InterleaveFlags flags) {
Trade::MeshData concatenate(const Containers::Iterable<const Trade::MeshData> meshes, const InterleaveFlags flags) {
CORRADE_ASSERT(!meshes.isEmpty(),
"MeshTools::concatenate(): expected at least one mesh",
(Trade::MeshData{MeshPrimitive::Points, 0}));
#ifndef CORRADE_NO_ASSERT
for(std::size_t i = 0; i != meshes.front()->attributeCount(); ++i) {
const VertexFormat format = meshes.front()->attributeFormat(i);
for(std::size_t i = 0; i != meshes.front().attributeCount(); ++i) {
const VertexFormat format = meshes.front().attributeFormat(i);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format),
"MeshTools::concatenate(): attribute" << i << "of the first mesh has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(format)),
(Trade::MeshData{MeshPrimitive::Points, 0}));
@ -208,13 +208,13 @@ Trade::MeshData concatenate(const Containers::ArrayView<const Containers::Refere
no attributes in the original array, pass just vertex count ---
otherwise MeshData will assert on that to avoid it getting lost. */
Containers::Array<Trade::MeshAttributeData> attributeData;
if(meshes.front()->attributeCount())
attributeData = Implementation::interleavedLayout(Trade::MeshData{meshes.front()->primitive(),
{}, meshes.front()->vertexData(),
Trade::meshAttributeDataNonOwningArray(meshes.front()->attributeData())}, {}, flags);
if(meshes.front().attributeCount())
attributeData = Implementation::interleavedLayout(Trade::MeshData{meshes.front().primitive(),
{}, meshes.front().vertexData(),
Trade::meshAttributeDataNonOwningArray(meshes.front().attributeData())}, {}, flags);
else attributeData =
Implementation::interleavedLayout(Trade::MeshData{meshes.front()->primitive(),
meshes.front()->vertexCount()}, {}, flags);
Implementation::interleavedLayout(Trade::MeshData{meshes.front().primitive(),
meshes.front().vertexCount()}, {}, flags);
/* Calculate total index/vertex count and allocate the target memory.
Index data are allocated with NoInit as the whole array will be written,
@ -227,8 +227,4 @@ Trade::MeshData concatenate(const Containers::ArrayView<const Containers::Refere
return Implementation::concatenate(std::move(indexData), indexVertexCount.second, std::move(vertexData), std::move(attributeData), meshes, "MeshTools::concatenate():");
}
Trade::MeshData concatenate(const std::initializer_list<Containers::Reference<const Trade::MeshData>> meshes, const InterleaveFlags flags) {
return concatenate(Containers::arrayView(meshes), flags);
}
}}

26
src/Magnum/MeshTools/Concatenate.h

@ -31,7 +31,7 @@
*/
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Reference.h>
#include <Corrade/Containers/Iterable.h>
#include "Magnum/MeshTools/Interleave.h"
#include "Magnum/Trade/MeshData.h"
@ -39,8 +39,8 @@
namespace Magnum { namespace MeshTools {
namespace Implementation {
MAGNUM_MESHTOOLS_EXPORT std::pair<UnsignedInt, UnsignedInt> concatenateIndexVertexCount(Containers::ArrayView<const Containers::Reference<const Trade::MeshData>> meshes);
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData concatenate(Containers::Array<char>&& indexData, UnsignedInt vertexCount, Containers::Array<char>&& vertexData, Containers::Array<Trade::MeshAttributeData>&& attributeData, Containers::ArrayView<const Containers::Reference<const Trade::MeshData>> meshes, const char* assertPrefix);
MAGNUM_MESHTOOLS_EXPORT std::pair<UnsignedInt, UnsignedInt> concatenateIndexVertexCount(Containers::Iterable<const Trade::MeshData> meshes);
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData concatenate(Containers::Array<char>&& indexData, UnsignedInt vertexCount, Containers::Array<char>&& vertexData, Containers::Array<Trade::MeshAttributeData>&& attributeData, Containers::Iterable<const Trade::MeshData> meshes, const char* assertPrefix);
}
/**
@ -82,13 +82,7 @@ to compress it to a smaller type, if desired.
@ref SceneTools::flattenMeshHierarchy2D(),
@ref SceneTools::flattenMeshHierarchy3D()
*/
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData concatenate(Containers::ArrayView<const Containers::Reference<const Trade::MeshData>> meshes, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/**
* @overload
* @m_since{2020,06}
*/
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData concatenate(std::initializer_list<Containers::Reference<const Trade::MeshData>> meshes, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData concatenate(Containers::Iterable<const Trade::MeshData> meshes, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/**
@brief Concatenate a list of meshes into a pre-existing destination, enlarging it if necessary
@ -99,14 +93,14 @@ MAGNUM_MESHTOOLS_EXPORT Trade::MeshData concatenate(std::initializer_list<Contai
@param[in] flags Flags to pass to @ref interleavedLayout()
@m_since{2020,06}
Compared to @ref concatenate(Containers::ArrayView<const Containers::Reference<const Trade::MeshData>>, InterleaveFlags)
Compared to @ref concatenate(Containers::Iterable<const Trade::MeshData>, InterleaveFlags)
this function resizes existing index and vertex buffers in @p destination using
@ref Containers::arrayResize() and given @p allocator, and reuses its
atttribute data array instead of always allocating new ones. Only the attribute
layout from @p destination is used, all vertex/index data are taken from
@p meshes. Expects that @p meshes contains at least one item.
*/
template<template<class> class Allocator = Containers::ArrayAllocator> void concatenateInto(Trade::MeshData& destination, Containers::ArrayView<const Containers::Reference<const Trade::MeshData>> meshes, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes) {
template<template<class> class Allocator = Containers::ArrayAllocator> void concatenateInto(Trade::MeshData& destination, Containers::Iterable<const Trade::MeshData> meshes, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes) {
CORRADE_ASSERT(!meshes.isEmpty(),
"MeshTools::concatenateInto(): no meshes passed", );
#ifndef CORRADE_NO_ASSERT
@ -142,14 +136,6 @@ template<template<class> class Allocator = Containers::ArrayAllocator> void conc
destination = Implementation::concatenate(std::move(indexData), indexVertexCount.second, std::move(vertexData), std::move(attributeData), meshes, "MeshTools::concatenateInto():");
}
/**
* @overload
* @m_since{2020,06}
*/
template<template<class> class Allocator = Containers::ArrayAllocator> void concatenateInto(Trade::MeshData& destination, std::initializer_list<Containers::Reference<const Trade::MeshData>> meshes, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes) {
concatenateInto<Allocator>(destination, Containers::arrayView(meshes), flags);
}
}}
#endif

9
src/Magnum/MeshTools/Interleave.cpp

@ -139,9 +139,8 @@ Containers::Array<Trade::MeshAttributeData> interleavedLayout(Trade::MeshData&&
stride = 0;
minOffset = 0;
for(UnsignedInt i = 0, max = data.attributeCount(); i != max; ++i) {
const VertexFormat format = data.attributeFormat(i);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format),
"MeshTools::interleavedLayout(): attribute" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(format)), {});
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(data.attributeFormat(i)),
"MeshTools::interleavedLayout(): attribute" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(data.attributeFormat(i))), {});
stride += attributeSize(data, i);
}
}
@ -149,12 +148,12 @@ Containers::Array<Trade::MeshAttributeData> interleavedLayout(Trade::MeshData&&
/* Add the extra attributes and explicit padding */
std::size_t extraAttributeCount = 0;
for(std::size_t i = 0; i != extra.size(); ++i) {
if(extra[i].format() == VertexFormat{}) {
const VertexFormat format = extra[i].format();
if(format == VertexFormat{}) {
CORRADE_ASSERT(extra[i].stride() > 0 || stride >= std::size_t(-extra[i].stride()),
"MeshTools::interleavedLayout(): negative padding" << extra[i].stride() << "in extra attribute" << i << "too large for stride" << stride, {});
stride += extra[i].stride();
} else {
const VertexFormat format = extra[i].format();
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format),
"MeshTools::interleavedLayout(): extra attribute" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(format)), {});
stride += attributeSize(extra[i]);

6
src/Magnum/MeshTools/InterleaveFlags.h

@ -43,7 +43,7 @@ namespace Magnum { namespace MeshTools {
@see @ref InterleaveFlags,
@ref interleavedLayout(const Trade::MeshData&, UnsignedInt, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags),
@ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags),
@ref concatenate(Containers::ArrayView<const Containers::Reference<const Trade::MeshData>>, InterleaveFlags)
@ref concatenate(Containers::Iterable<const Trade::MeshData>, InterleaveFlags)
*/
enum class InterleaveFlag: UnsignedInt {
/**
@ -72,7 +72,7 @@ enum class InterleaveFlag: UnsignedInt {
*
* Has no effect when passed to @ref interleavedLayout(const Trade::MeshData&, UnsignedInt, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "interleavedLayout()"
* as that function doesn't preserve the index buffer. Has no effect when
* passed to @ref concatenate(Containers::ArrayView<const Containers::Reference<const Trade::MeshData>>, InterleaveFlags) "concatenate()"
* passed to @ref concatenate(Containers::Iterable<const Trade::MeshData>, InterleaveFlags) "concatenate()"
* as that function allocates a new combined index buffer anyway.
* @see @ref isMeshIndexTypeImplementationSpecific()
*/
@ -85,7 +85,7 @@ enum class InterleaveFlag: UnsignedInt {
@see @ref interleavedLayout(const Trade::MeshData&, UnsignedInt, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags),
@ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags),
@ref concatenate(Containers::ArrayView<const Containers::Reference<const Trade::MeshData>>, InterleaveFlags)
@ref concatenate(Containers::Iterable<const Trade::MeshData>, InterleaveFlags)
*/
typedef Containers::EnumSet<InterleaveFlag> InterleaveFlags;

2
src/Magnum/MeshTools/Test/CombineTest.cpp

@ -24,7 +24,7 @@
*/
#include <sstream>
#include <Corrade/Containers/Reference.h>
#include <Corrade/Containers/Iterable.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>

4
src/Magnum/MeshTools/Test/FullScreenTriangleGLTest.cpp

@ -24,7 +24,7 @@
*/
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/Reference.h>
#include <Corrade/Containers/Iterable.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/Utility/Resource.h>
@ -102,7 +102,7 @@ void main() {
}
)");
CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag}));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
attachShaders({vert, frag});

10
src/Magnum/PixelFormat.cpp

@ -25,7 +25,7 @@
#include "PixelFormat.h"
#include <string>
#include <Corrade/Containers/String.h>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h>
@ -556,14 +556,14 @@ Debug& operator<<(Debug& debug, const CompressedPixelFormat value) {
namespace Corrade { namespace Utility {
std::string ConfigurationValue<Magnum::PixelFormat>::toString(Magnum::PixelFormat value, ConfigurationValueFlags) {
Containers::String ConfigurationValue<Magnum::PixelFormat>::toString(Magnum::PixelFormat value, ConfigurationValueFlags) {
if(Magnum::UnsignedInt(value) - 1 < Containers::arraySize(Magnum::PixelFormatNames))
return Magnum::PixelFormatNames[Magnum::UnsignedInt(value) - 1];
return {};
}
Magnum::PixelFormat ConfigurationValue<Magnum::PixelFormat>::fromString(const std::string& stringValue, ConfigurationValueFlags) {
Magnum::PixelFormat ConfigurationValue<Magnum::PixelFormat>::fromString(Containers::StringView stringValue, ConfigurationValueFlags) {
/** @todo This is extremely slow with >100 values. Do a binary search on a
sorted index list instead (extracted into a common utility) */
for(std::size_t i = 0; i != Containers::arraySize(Magnum::PixelFormatNames); ++i)
@ -572,14 +572,14 @@ Magnum::PixelFormat ConfigurationValue<Magnum::PixelFormat>::fromString(const st
return {};
}
std::string ConfigurationValue<Magnum::CompressedPixelFormat>::toString(Magnum::CompressedPixelFormat value, ConfigurationValueFlags) {
Containers::String ConfigurationValue<Magnum::CompressedPixelFormat>::toString(Magnum::CompressedPixelFormat value, ConfigurationValueFlags) {
if(Magnum::UnsignedInt(value) - 1 < Containers::arraySize(Magnum::CompressedPixelFormatNames))
return Magnum::CompressedPixelFormatNames[Magnum::UnsignedInt(value) - 1];
return {};
}
Magnum::CompressedPixelFormat ConfigurationValue<Magnum::CompressedPixelFormat>::fromString(const std::string& stringValue, ConfigurationValueFlags) {
Magnum::CompressedPixelFormat ConfigurationValue<Magnum::CompressedPixelFormat>::fromString(Containers::StringView stringValue, ConfigurationValueFlags) {
/** @todo This is extremely slow with >100 values. Do a binary search on a
sorted index list instead (extracted into a common utility) */
for(std::size_t i = 0; i != Containers::arraySize(Magnum::CompressedPixelFormatNames); ++i)

9
src/Magnum/PixelFormat.h

@ -30,7 +30,6 @@
*/
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/StlForwardString.h>
#include "Magnum/Magnum.h"
#include "Magnum/visibility.h"
@ -2491,14 +2490,14 @@ template<> struct MAGNUM_EXPORT ConfigurationValue<Magnum::PixelFormat> {
*
* If the value is invalid, returns empty string.
*/
static std::string toString(Magnum::PixelFormat value, ConfigurationValueFlags);
static Containers::String toString(Magnum::PixelFormat value, ConfigurationValueFlags);
/**
* @brief Reads enum value as string
*
* If the value is invalid, returns a zero (invalid) format.
*/
static Magnum::PixelFormat fromString(const std::string& stringValue, ConfigurationValueFlags);
static Magnum::PixelFormat fromString(Containers::StringView stringValue, ConfigurationValueFlags);
};
/**
@ -2513,14 +2512,14 @@ template<> struct MAGNUM_EXPORT ConfigurationValue<Magnum::CompressedPixelFormat
*
* If the value is invalid, returns empty string.
*/
static std::string toString(Magnum::CompressedPixelFormat value, ConfigurationValueFlags);
static Containers::String toString(Magnum::CompressedPixelFormat value, ConfigurationValueFlags);
/**
* @brief Read enum value as string
*
* If the value is invalid, returns a zero (invalid) format.
*/
static Magnum::CompressedPixelFormat fromString(const std::string& stringValue, ConfigurationValueFlags);
static Magnum::CompressedPixelFormat fromString(Containers::StringView stringValue, ConfigurationValueFlags);
};
}}

88
src/Magnum/Platform/CMakeLists.txt

@ -98,24 +98,30 @@ install(FILES ${MagnumPlatform_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR
if(MAGNUM_TARGET_GL)
# Decide about platform-specific context for cross-platform toolkits
if(MAGNUM_WITH_GLFWAPPLICATION OR MAGNUM_WITH_SDL2APPLICATION)
if(CORRADE_TARGET_APPLE AND NOT MAGNUM_TARGET_GLES)
# On WebGL windowless apps don't use anything from EGL, only windowless
# do. On iOS it's EAGL, which isn't really EGL (and thus
# MAGNUM_TARGET_EGL isn't set), but we handle it inside EglContext as
# well. TODO make EaglContext for iOS to prepare for true EGL (such as
# with ANGLE or Zink)
if((MAGNUM_TARGET_EGL AND NOT MAGNUM_TARGET_WEBGL) OR CORRADE_TARGET_IOS)
set(NEED_EGLCONTEXT 1)
set(MagnumSomeContext_OBJECTS $<TARGET_OBJECTS:MagnumEglContextObjects>)
find_package(EGL REQUIRED)
set(MagnumSomeContext_LIBRARY EGL::EGL)
elseif(CORRADE_TARGET_APPLE)
set(NEED_CGLCONTEXT 1)
set(MagnumSomeContext_OBJECTS $<TARGET_OBJECTS:MagnumCglContextObjects>)
elseif(CORRADE_TARGET_WINDOWS AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES))
elseif(CORRADE_TARGET_WINDOWS)
set(NEED_WGLCONTEXT 1)
set(MagnumSomeContext_OBJECTS $<TARGET_OBJECTS:MagnumWglContextObjects>)
elseif(CORRADE_TARGET_UNIX AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES))
elseif(CORRADE_TARGET_UNIX)
set(NEED_GLXCONTEXT 1)
set(MagnumSomeContext_OBJECTS $<TARGET_OBJECTS:MagnumGlxContextObjects>)
elseif(MAGNUM_TARGET_GLES AND NOT CORRADE_TARGET_EMSCRIPTEN)
set(NEED_EGLCONTEXT 1)
set(MagnumSomeContext_OBJECTS $<TARGET_OBJECTS:MagnumEglContextObjects>)
# We're linking to EGL explicitly, no need to bother with GLVND there
endif()
endif()
# This is needed also by [Windowless]GlxApplication
if((MAGNUM_WITH_GLXAPPLICATION OR MAGNUM_WITH_WINDOWLESSGLXAPPLICATION OR MAGNUM_WITH_GLFWAPPLICATION OR MAGNUM_WITH_SDL2APPLICATION) AND CORRADE_TARGET_UNIX AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES))
if((MAGNUM_WITH_GLXAPPLICATION OR MAGNUM_WITH_WINDOWLESSGLXAPPLICATION OR MAGNUM_WITH_GLFWAPPLICATION OR MAGNUM_WITH_SDL2APPLICATION) AND CORRADE_TARGET_UNIX AND NOT MAGNUM_TARGET_EGL)
# If the GLVND library (CMake 3.11+) was found and linked to, we need
# to link to GLX explicitly. Otherwise (and also on all systems except
# Linux) the transitive dependency to classic GL lib from MagnumGL is
@ -255,12 +261,6 @@ if(MAGNUM_WITH_GLFWAPPLICATION)
${MagnumSomeContext_LIBRARY})
endif()
# Link also EGL library, if on ES (and not on WebGL)
if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES AND NOT MAGNUM_TARGET_WEBGL)
find_package(EGL REQUIRED)
target_link_libraries(MagnumGlfwApplication PUBLIC EGL::EGL)
endif()
install(FILES ${MagnumGlfwApplication_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform)
install(TARGETS MagnumGlfwApplication
RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR}
@ -327,12 +327,6 @@ if(MAGNUM_WITH_SDL2APPLICATION)
${MagnumSomeContext_LIBRARY})
endif()
# Link also EGL library, if on ES (and not on WebGL)
if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES AND NOT MAGNUM_TARGET_WEBGL)
find_package(EGL REQUIRED)
target_link_libraries(MagnumSdl2Application PUBLIC EGL::EGL)
endif()
install(FILES ${MagnumSdl2Application_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform)
install(TARGETS MagnumSdl2Application
RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR}
@ -623,44 +617,6 @@ if(MAGNUM_WITH_WINDOWLESSWGLAPPLICATION)
add_library(Magnum::WindowlessWglApplication ALIAS MagnumWindowlessWglApplication)
endif()
# Windowless Windows/EGL application
if(MAGNUM_WITH_WINDOWLESSWINDOWSEGLAPPLICATION)
if(NOT MAGNUM_TARGET_GL)
message(SEND_ERROR "WindowlessWindowsEglApplication is available only if MAGNUM_TARGET_GL is enabled")
endif()
set(NEED_EGLCONTEXT 1)
set(MagnumWindowlessWindowsEglApplication_SRCS
WindowlessWindowsEglApplication.cpp
Implementation/Egl.cpp
$<TARGET_OBJECTS:MagnumEglContextObjects>)
set(MagnumWindowlessWindowsEglApplication_HEADERS
WindowlessWindowsEglApplication.h)
set(MagnumWindowlessWindowsEglApplication_PRIVATE_HEADERS
Implementation/Egl.h)
add_library(MagnumWindowlessWindowsEglApplication STATIC
${MagnumWindowlessWindowsEglApplication_SRCS}
${MagnumWindowlessWindowsEglApplication_HEADERS}
${MagnumWindowlessWindowsEglApplication_PRIVATE_HEADERS})
set_target_properties(MagnumWindowlessWindowsEglApplication PROPERTIES
DEBUG_POSTFIX "-d")
if(NOT MAGNUM_BUILD_STATIC OR MAGNUM_BUILD_STATIC_PIC)
set_target_properties(MagnumWindowlessWindowsEglApplication PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
target_link_libraries(MagnumWindowlessWindowsEglApplication PUBLIC MagnumGL EGL::EGL)
install(FILES ${MagnumWindowlessWindowsEglApplication_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform)
install(TARGETS MagnumWindowlessWindowsEglApplication
RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR}
LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}
ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR})
# Magnum WindowlessWindowsEglApplication target alias for superprojects
add_library(Magnum::WindowlessWindowsEglApplication ALIAS MagnumWindowlessWindowsEglApplication)
endif()
# Windowless CGL application
if(MAGNUM_WITH_WINDOWLESSCGLAPPLICATION)
if(NOT MAGNUM_TARGET_GL)
@ -769,7 +725,7 @@ endif()
if(NOT MAGNUM_TARGET_GLES)
list(APPEND MagnumContext_SRCS ../../MagnumExternal/OpenGL/GL/flextGLPlatform.cpp)
elseif(MAGNUM_TARGET_GLES AND MAGNUM_TARGET_GLES2)
if(CORRADE_TARGET_WINDOWS AND MAGNUM_TARGET_DESKTOP_GLES)
if(CORRADE_TARGET_WINDOWS AND NOT MAGNUM_TARGET_EGL)
list(APPEND MagnumContext_SRCS ../../MagnumExternal/OpenGL/GLES2/flextGLPlatformWindowsDesktop.cpp)
elseif(CORRADE_TARGET_IOS)
list(APPEND MagnumContext_SRCS ../../MagnumExternal/OpenGL/GLES2/flextGLPlatformIOS.cpp)
@ -777,7 +733,7 @@ elseif(MAGNUM_TARGET_GLES AND MAGNUM_TARGET_GLES2)
list(APPEND MagnumContext_SRCS ../../MagnumExternal/OpenGL/GLES2/flextGLPlatform.cpp)
endif()
elseif(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_GLES2)
if(CORRADE_TARGET_WINDOWS AND MAGNUM_TARGET_DESKTOP_GLES)
if(CORRADE_TARGET_WINDOWS AND NOT MAGNUM_TARGET_EGL)
list(APPEND MagnumContext_SRCS ../../MagnumExternal/OpenGL/GLES3/flextGLPlatformWindowsDesktop.cpp)
elseif(CORRADE_TARGET_IOS)
list(APPEND MagnumContext_SRCS ../../MagnumExternal/OpenGL/GLES3/flextGLPlatformIOS.cpp)
@ -941,24 +897,16 @@ if(MAGNUM_WITH_GL_INFO)
add_executable(magnum-gl-info gl-info.cpp)
target_link_libraries(magnum-gl-info PRIVATE MagnumGL)
if(MAGNUM_TARGET_HEADLESS OR CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID)
if(MAGNUM_TARGET_EGL)
target_link_libraries(magnum-gl-info PRIVATE MagnumWindowlessEglApplication)
elseif(CORRADE_TARGET_IOS)
target_link_libraries(magnum-gl-info PRIVATE MagnumWindowlessIosApplication)
elseif(CORRADE_TARGET_APPLE AND NOT MAGNUM_TARGET_GLES)
elseif(CORRADE_TARGET_APPLE)
target_link_libraries(magnum-gl-info PRIVATE MagnumWindowlessCglApplication)
elseif(CORRADE_TARGET_UNIX)
if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES)
target_link_libraries(magnum-gl-info PRIVATE MagnumWindowlessEglApplication)
else()
target_link_libraries(magnum-gl-info PRIVATE MagnumWindowlessGlxApplication)
endif()
elseif(CORRADE_TARGET_WINDOWS)
if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES)
target_link_libraries(magnum-gl-info PRIVATE MagnumWindowlessWindowsEglApplication)
else()
target_link_libraries(magnum-gl-info PRIVATE MagnumWindowlessWglApplication)
endif()
else()
message(FATAL_ERROR "magnum-gl-info is not available on this platform. Set MAGNUM_WITH_GL_INFO to OFF to skip building it.")
endif()

10
src/Magnum/Platform/GlfwApplication.cpp

@ -457,10 +457,7 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf
}
#else
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
/* Force EGL on Windows and non-desktop GLES -- needed by ANGLE:
https://stackoverflow.com/a/58904181/4084782 . Might be useful on
other platforms as well (Mac?), not tested yet. */
#if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_DESKTOP_GLES)
#ifdef MAGNUM_TARGET_EGL /* Force EGL if desired */
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
#endif
#endif
@ -487,10 +484,7 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf
#endif
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
/* Force EGL on Windows and non-desktop GLES -- needed by ANGLE:
https://stackoverflow.com/a/58904181/4084782 . Might be useful on
other platforms as well (Mac?), not tested yet. */
#if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_DESKTOP_GLES)
#ifdef MAGNUM_TARGET_EGL /* Force EGL if desired */
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
#endif
#endif

3
src/Magnum/Platform/GlxApplication.h

@ -43,8 +43,7 @@ namespace Magnum { namespace Platform {
@m_keywords{Application}
Application using pure X11 and GLX. Supports keyboard and mouse handling.
Available on desktop OpenGL and
@ref MAGNUM_TARGET_DESKTOP_GLES "OpenGL ES emulation on desktop" on Linux.
Available on desktop OpenGL and OpenGL ES using GLX on Linux.
@section Platform-GlxApplication-bootstrap Bootstrap application

11
src/Magnum/Platform/Sdl2Application.cpp

@ -139,10 +139,9 @@ Sdl2Application::Sdl2Application(const Arguments& arguments, NoCreateT):
#ifdef SDL_HINT_NO_SIGNAL_HANDLERS
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
#endif
/* Available since 2.0.6, uses dedicated OpenGL ES drivers by default and a
desktop GLES context only if MAGNUM_TARGET_DESKTOP_GLES is defined as
well. */
#if !defined(MAGNUM_TARGET_DESKTOP_GLES) && defined(SDL_HINT_OPENGL_ES_DRIVER)
/* Available since 2.0.6, uses dedicated OpenGL ES drivers if EGL is used,
and desktop GLES context otherwise. */
#if defined(MAGNUM_TARGET_GLES) && defined(MAGNUM_TARGET_EGL) && defined(SDL_HINT_OPENGL_ES_DRIVER)
SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, "1");
#endif
/* Available since 2.0.8, disables compositor bypass on X11, which causes
@ -150,6 +149,10 @@ Sdl2Application::Sdl2Application(const Arguments& arguments, NoCreateT):
#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
#endif
/* Available since 2.0.12, use EGL if MAGNUM_TARGET_HEADLESS is enabled */
#if defined(MAGNUM_TARGET_HEADLESS) && defined(SDL_HINT_VIDEO_X11_FORCE_EGL)
SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");
#endif
if(SDL_Init(SDL_INIT_VIDEO) < 0) {
Error() << "Cannot initialize SDL:" << SDL_GetError();

11
src/Magnum/Platform/Test/CMakeLists.txt

@ -85,6 +85,9 @@ if(MAGNUM_WITH_GLFWAPPLICATION)
# HiDPi.manifest not needed, as GLFW sets that on its own
target_link_libraries(PlatformGlfwApplicationTest PRIVATE MagnumGlfwApplication Corrade::Main)
# Window icon loading
if(NOT MAGNUM_WITH_TRADE)
message(FATAL_ERROR "GlfwApplication tests need the Trade library enabled")
endif()
target_sources(PlatformGlfwApplicationTest PRIVATE ${Platform_RESOURCES})
target_link_libraries(PlatformGlfwApplicationTest PRIVATE MagnumTrade)
if(CORRADE_TARGET_APPLE)
@ -112,6 +115,9 @@ if(MAGNUM_WITH_SDL2APPLICATION)
endif()
target_link_libraries(PlatformSdl2ApplicationTest PRIVATE MagnumSdl2Application Corrade::Main)
# Window icon loading
if(NOT MAGNUM_WITH_TRADE)
message(FATAL_ERROR "GlfwApplication tests need the Trade library enabled")
endif()
if(NOT CORRADE_TARGET_EMSCRIPTEN)
target_sources(PlatformSdl2ApplicationTest PRIVATE ${Platform_RESOURCES})
target_link_libraries(PlatformSdl2ApplicationTest PRIVATE MagnumTrade)
@ -176,8 +182,3 @@ if(MAGNUM_WITH_WINDOWLESSWGLAPPLICATION)
add_executable(PlatformWindowlessWglApplicationTest WindowlessWglApplicationTest.cpp)
target_link_libraries(PlatformWindowlessWglApplicationTest PRIVATE MagnumWindowlessWglApplication)
endif()
if(MAGNUM_WITH_WINDOWLESSWINDOWSEGLAPPLICATION)
add_executable(PlatformWindowlessWindowsEglApplicationTest WindowlessWindowsEglApplicationTest.cpp)
target_link_libraries(PlatformWindowlessWindowsEglApplicationTest PRIVATE MagnumWindowlessWindowsEglApplication)
endif()

55
src/Magnum/Platform/Test/WindowlessWindowsEglApplicationTest.cpp

@ -1,55 +0,0 @@
/*
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 <Corrade/Utility/Arguments.h>
#include "Magnum/Platform/WindowlessWindowsEglApplication.h"
namespace Magnum { namespace Platform { namespace Test { namespace {
struct WindowlessWindowsEglApplicationTest: Platform::WindowlessApplication {
explicit WindowlessWindowsEglApplicationTest(const Arguments& arguments);
int exec() override { return 0; }
};
WindowlessWindowsEglApplicationTest::WindowlessWindowsEglApplicationTest(const Arguments& arguments): Platform::WindowlessApplication{arguments, NoCreate} {
Utility::Arguments args;
args.addSkippedPrefix("magnum", "engine-specific options")
.addBooleanOption("quiet").setHelp("quiet", "like --magnum-log quiet, but specified via a Context::Configuration instead")
.addBooleanOption("gpu-validation").setHelp("gpu-validation", "like --magnum-gpu-validation, but specified via a Context::Configuration instead")
.parse(arguments.argc, arguments.argv);
Configuration conf;
if(args.isSet("quiet"))
conf.addFlags(Configuration::Flag::QuietLog);
/* No verbose logs in this app */
if(args.isSet("gpu-validation"))
conf.addFlags(Configuration::Flag::GpuValidation);
createContext(conf);
}
}}}}
MAGNUM_WINDOWLESSAPPLICATION_MAIN(Magnum::Platform::Test::WindowlessWindowsEglApplicationTest)

75
src/Magnum/Platform/WindowlessEglApplication.cpp

@ -39,6 +39,14 @@
#include "Implementation/Egl.h"
/* ANGLE's EGL on Windows needs an actual window */
/** @todo investigate if this is still needed */
#ifdef CORRADE_TARGET_WINDOWS
#define WIN32_LEAN_AND_MEAN 1
#define VC_EXTRALEAN
#include <windows.h>
#endif
/* None of this is in the Emscripten emulation layer, so no need to include
that there */
#ifndef MAGNUM_TARGET_WEBGL
@ -252,10 +260,44 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, G
}
#endif
/* ANGLE's EGL on Windows needs to get a display from an actual
window. Elsewhere EGL_DEFAULT_DISPLAY is fine. */
/** @todo investigate if this is still needed */
#ifdef CORRADE_TARGET_WINDOWS
/* Register the window class (if not yet done) */
WNDCLASSW wc;
if(!GetClassInfoW(GetModuleHandleW(nullptr), L"Magnum Windowless Application", &wc)) {
wc = WNDCLASSW{
0,
DefWindowProcW,
0,
0,
GetModuleHandleW(nullptr),
nullptr,
nullptr,
HBRUSH(COLOR_BACKGROUND),
nullptr,
L"Magnum Windowless Application"
};
if(!RegisterClassW(&wc)) {
Error() << "Platform::WindowlessWglContext: cannot create window class:" << GetLastError();
return;
}
}
/* Create the window */
_window = CreateWindowW(wc.lpszClassName, L"Magnum Windowless Application",
WS_OVERLAPPEDWINDOW, 0, 0, 32, 32, 0, 0, wc.hInstance, 0);
/* Initialize */
_display = eglGetDisplay(GetDC(_window));
#else
if(!(_display = eglGetDisplay(EGL_DEFAULT_DISPLAY))) {
Error{} << "Platform::WindowlessEglApplication::tryCreateContext(): cannot get default EGL display:" << Implementation::eglErrorString(eglGetError());
return;
}
#endif
}
if(!eglInitialize(_display, nullptr, nullptr)) {
@ -489,7 +531,14 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, G
return;
}
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
#ifdef CORRADE_TARGET_WINDOWS
/* ANGLE's EGL on Windows needs has an actual window, and so it also needs
a surface */
/** @todo investigate if this is still needed */
if(!(_surface = eglCreateWindowSurface(_display, config, _window, nullptr)))
Error() << "Platform::WindowlessEglContext: cannot create window surface:" << Implementation::eglErrorString(eglGetError());
#elif defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
/* Android Emulator can run with a SwiftShader GPU and thus needs some of
the SwiftShader context creation workarounds. However, it's impossible
to detect, as EGL_VERSION is always "1.4 Android META-EGL" and
@ -525,13 +574,25 @@ WindowlessEglContext::WindowlessEglContext(WindowlessEglContext&& other) noexcep
#ifndef MAGNUM_TARGET_WEBGL
_sharedContext{other._sharedContext},
#endif
#ifdef CORRADE_TARGET_WINDOWS
_window{other._window},
#endif
_display{other._display}, _context{other._context}
#if defined(CORRADE_TARGET_WINDOWS) || (defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL))
, _surface{other._surface}
#endif
{
#ifndef MAGNUM_TARGET_WEBGL
other._sharedContext = false;
#endif
#ifdef CORRADE_TARGET_WINDOWS
other._window = {};
#endif
other._display = {};
other._context = {};
#if defined(CORRADE_TARGET_WINDOWS) || (defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL))
other._surface = {};
#endif
}
WindowlessEglContext::~WindowlessEglContext() {
@ -548,7 +609,7 @@ WindowlessEglContext::~WindowlessEglContext() {
eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(_display, _context);
}
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
#if defined(CORRADE_TARGET_WINDOWS) || (defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL))
if(_surface) eglDestroySurface(_display, _surface);
#endif
@ -561,6 +622,10 @@ WindowlessEglContext::~WindowlessEglContext() {
!_sharedContext &&
#endif
_display) eglTerminate(_display);
#ifdef CORRADE_TARGET_WINDOWS
if(_window) DestroyWindow(_window);
#endif
}
WindowlessEglContext& WindowlessEglContext::operator=(WindowlessEglContext&& other) noexcept {
@ -570,6 +635,12 @@ WindowlessEglContext& WindowlessEglContext::operator=(WindowlessEglContext&& oth
#endif
swap(other._display, _display);
swap(other._context, _context);
#if defined(CORRADE_TARGET_WINDOWS) || (defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL))
swap(other._surface, _surface);
#endif
#ifdef CORRADE_TARGET_WINDOWS
swap(other._window, _window);
#endif
return *this;
}

7
src/Magnum/Platform/WindowlessEglApplication.h

@ -162,9 +162,14 @@ class WindowlessEglContext {
#ifndef MAGNUM_TARGET_WEBGL
bool _sharedContext = false;
#endif
#ifdef CORRADE_TARGET_WINDOWS
/* It's a HWND, which is HANDLE, which is PVOID, which is void*. FFS
Windows you're really mad with the typedefs. */
void* _window{};
#endif
EGLDisplay _display{};
EGLContext _context{};
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
#if defined(CORRADE_TARGET_WINDOWS) || (defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL))
/* Needed only by SwiftShader, using EGL_NO_SURFACE everywhere else */
EGLSurface _surface = EGL_NO_SURFACE;
#endif

5
src/Magnum/Platform/WindowlessGlxApplication.h

@ -344,9 +344,8 @@ CORRADE_ENUMSET_OPERATORS(WindowlessGlxContext::Configuration::Flags)
@m_keywords{WindowlessApplication GLX}
Application for offscreen rendering using @ref WindowlessGlxContext. This
application library is available on desktop OpenGL and
@ref MAGNUM_TARGET_DESKTOP_GLES "OpenGL ES emulation on desktop" on Linux.
Application for offscreen rendering using @ref WindowlessGlxContext. Available
on desktop OpenGL and OpenGL ES using GLX on Linux.
@section Platform-WindowlessGlxApplication-bootstrap Bootstrap application

238
src/Magnum/Platform/WindowlessWindowsEglApplication.cpp

@ -1,238 +0,0 @@
/*
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 "WindowlessWindowsEglApplication.h"
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h>
#include "Magnum/GL/Version.h"
#include "Implementation/Egl.h"
#ifndef EGL_KHR_create_context_no_error
#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31B3
#endif
namespace Magnum { namespace Platform {
WindowlessWindowsEglContext::WindowlessWindowsEglContext(const Configuration& configuration, GLContext* const magnumContext) {
/** @todo device selection and skipping of eglInitialize()/Terminate() the
same way as with WindowlessEglContext */
/* Register the window class (if not yet done) */
WNDCLASSW wc;
if(!GetClassInfoW(GetModuleHandleW(nullptr), L"Magnum Windowless Application", &wc)) {
wc = WNDCLASSW{
0,
DefWindowProcW,
0,
0,
GetModuleHandleW(nullptr),
nullptr,
nullptr,
HBRUSH(COLOR_BACKGROUND),
nullptr,
L"Magnum Windowless Application"
};
if(!RegisterClassW(&wc)) {
Error() << "Platform::WindowlessWglContext: cannot create window class:" << GetLastError();
return;
}
}
/* Create the window */
_window = CreateWindowW(wc.lpszClassName, L"Magnum Windowless Application",
WS_OVERLAPPEDWINDOW, 0, 0, 32, 32, 0, 0, wc.hInstance, 0);
/* Initialize */
_display = eglGetDisplay(GetDC(_window));
if(!eglInitialize(_display, nullptr, nullptr)) {
Error() << "Platform::WindowlessWindowsEglApplication::tryCreateContext(): cannot initialize EGL:" << Implementation::eglErrorString(eglGetError());
return;
}
const EGLenum api =
#ifndef MAGNUM_TARGET_GLES
EGL_OPENGL_API
#else
EGL_OPENGL_ES_API
#endif
;
if(!eglBindAPI(api)) {
Error() << "Platform::WindowlessWindowsEglApplication::tryCreateContext(): cannot bind EGL API:" << Implementation::eglErrorString(eglGetError());
return;
}
/* Choose EGL config */
static const EGLint attribs[] = {
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_DEPTH_SIZE, 1,
#ifndef MAGNUM_TARGET_GLES
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
#elif defined(MAGNUM_TARGET_GLES3)
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
#elif defined(MAGNUM_TARGET_GLES2)
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
#else
#error unsupported OpenGL edition
#endif
EGL_NONE
};
EGLint configCount;
EGLConfig config;
if(!eglChooseConfig(_display, attribs, &config, 1, &configCount)) {
Error() << "Platform::WindowlessWindowsEglApplication::tryCreateContext(): cannot get EGL visual config:" << Implementation::eglErrorString(eglGetError());
return;
}
if(!configCount) {
Error() << "Platform::WindowlessWindowsEglApplication::tryCreateContext(): no matching EGL visual config available";
return;
}
/* Request debug context if GpuValidation is enabled either via the
configuration or via command-line */
Configuration::Flags flags = configuration.flags();
if((flags & Configuration::Flag::GpuValidation) || (magnumContext && magnumContext->configurationFlags() & GL::Context::Configuration::Flag::GpuValidation))
flags |= Configuration::Flag::Debug;
else if((flags & Configuration::Flag::GpuValidationNoError) || (magnumContext && magnumContext->configurationFlags() & GL::Context::Configuration::Flag::GpuValidationNoError))
flags |= Configuration::Flag::NoError;
/** @todo needs a growable DynamicArray with disabled alloc or somesuch */
EGLint attributes[7] = {
#ifdef MAGNUM_TARGET_GLES
EGL_CONTEXT_CLIENT_VERSION,
#ifdef MAGNUM_TARGET_GLES3
3,
#elif defined(MAGNUM_TARGET_GLES2)
2,
#else
#error unsupported OpenGL ES version
#endif
#endif
/* Mask out the upper 32bits used for other flags */
EGL_CONTEXT_FLAGS_KHR, EGLint(UnsignedLong(flags) & 0xffffffffu),
/* The rest is added optionally */
EGL_NONE, EGL_NONE, /* EGL_CONTEXT_OPENGL_NO_ERROR_KHR */
EGL_NONE
};
std::size_t nextAttribute = 4;
CORRADE_INTERNAL_ASSERT(attributes[nextAttribute] == EGL_NONE);
if(flags & Configuration::Flag::NoError) {
attributes[nextAttribute++] = EGL_CONTEXT_OPENGL_NO_ERROR_KHR;
attributes[nextAttribute++] = true;
}
CORRADE_INTERNAL_ASSERT(nextAttribute < Containers::arraySize(attributes));
if(!(_context = eglCreateContext(_display, config, configuration.sharedContext(), attributes))) {
Error() << "Platform::WindowlessWindowsEglContext: cannot create EGL context:" << Implementation::eglErrorString(eglGetError());
return;
}
if(!(_surface = eglCreateWindowSurface(_display, config, _window, nullptr)))
Error() << "Platform::WindowlessWindowsEglContext: cannot create window surface:" << Implementation::eglErrorString(eglGetError());
}
WindowlessWindowsEglContext::WindowlessWindowsEglContext(WindowlessWindowsEglContext&& other) noexcept: _window{other._window}, _display{other._display}, _surface{other._surface}, _context{other._context} {
other._window = {};
other._display = {};
other._surface = {};
other._context = {};
}
WindowlessWindowsEglContext::~WindowlessWindowsEglContext() {
if(_context) eglDestroyContext(_display, _context);
if(_surface) eglDestroySurface(_display, _surface);
if(_display) eglTerminate(_display);
if(_window) DestroyWindow(_window);
}
WindowlessWindowsEglContext& WindowlessWindowsEglContext::operator=(WindowlessWindowsEglContext&& other) noexcept {
using std::swap;
swap(other._window, _window);
swap(other._display, _display);
swap(other._surface, _surface);
swap(other._context, _context);
return *this;
}
bool WindowlessWindowsEglContext::makeCurrent() {
if(eglMakeCurrent(_display, _surface, _surface, _context))
return true;
Error() << "Platform::WindowlessWindowsEglContext::makeCurrent(): cannot make context current:" << GetLastError();
return false;
}
bool WindowlessWindowsEglContext::release() {
if(eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
return true;
Error() << "Platform::WindowlessWindowsEglApplication::release(): cannot release current context:" << GetLastError();
return false;
}
WindowlessWindowsEglContext::Configuration::Configuration() {
GL::Context::Configuration::addFlags(GL::Context::Configuration::Flag::Windowless);
}
#ifndef DOXYGEN_GENERATING_OUTPUT
WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments& arguments): WindowlessWindowsEglApplication{arguments, Configuration{}} {}
#endif
WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessWindowsEglApplication{arguments, NoCreate} {
createContext(configuration);
}
WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments& arguments, NoCreateT): _glContext{NoCreate}, _context{NoCreate, arguments.argc, arguments.argv} {}
void WindowlessWindowsEglApplication::createContext() { createContext({}); }
void WindowlessWindowsEglApplication::createContext(const Configuration& configuration) {
if(!tryCreateContext(configuration)) std::exit(1);
}
bool WindowlessWindowsEglApplication::tryCreateContext(const Configuration& configuration) {
CORRADE_ASSERT(_context.version() == Version::None, "Platform::WindowlessWindowsEglApplication::tryCreateContext(): context already created", false);
WindowlessWindowsEglContext glContext{configuration, &_context};
if(!glContext.isCreated() || !glContext.makeCurrent() || !_context.tryCreate(configuration))
return false;
_glContext = std::move(glContext);
return true;
}
WindowlessWindowsEglApplication::~WindowlessWindowsEglApplication() = default;
}}

538
src/Magnum/Platform/WindowlessWindowsEglApplication.h

@ -1,538 +0,0 @@
#ifndef Magnum_Platform_WindowlessWindowsEglApplication_h
#define Magnum_Platform_WindowlessWindowsEglApplication_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 Class @ref Magnum::Platform::WindowlessWindowsEglApplication, @ref Magnum::Platform::WindowlessWindowsEglContext, macro @ref MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN()
*/
#include "Magnum/configure.h"
#ifdef MAGNUM_TARGET_GL
#ifndef DOXYGEN_GENERATING_OUTPUT
#define WIN32_LEAN_AND_MEAN 1
#define VC_EXTRALEAN
#endif
#include <windows.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <Corrade/Containers/EnumSet.h>
#include "Magnum/Magnum.h"
#include "Magnum/Tags.h"
#include "Magnum/Platform/GLContext.h"
namespace Magnum { namespace Platform {
/**
@brief Windowless Windows/EGL context
@m_keywords{WindowlessGLContext EGL}
GL context using pure WINAPI and EGL, used in @ref WindowlessWindowsEglApplication.
Meant to be used when there is a need to manage (multiple) GL contexts
manually. See @ref platform-windowless-contexts for more information. If no
other application header is included, this class is also aliased to
@cpp Platform::WindowlessGLContext @ce.
@note This class is available only if Magnum is compiled with
@ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features
for more information.
*/
class WindowlessWindowsEglContext {
public:
class Configuration;
/**
* @brief Constructor
* @param configuration Context configuration
* @param context Optional Magnum context instance constructed
* using @ref NoCreate to manage driver workarounds
*
* Once the context is created, make it current using
* @ref makeCurrent() and create @ref Platform::GLContext instance to
* be able to use Magnum.
* @see @ref isCreated()
*/
explicit WindowlessWindowsEglContext(const Configuration& configuration, GLContext* context = nullptr);
/**
* @brief Construct without creating the context
*
* Move a instance with created context over to make it usable.
*/
explicit WindowlessWindowsEglContext(NoCreateT) {}
/** @brief Copying is not allowed */
WindowlessWindowsEglContext(const WindowlessWindowsEglContext&) = delete;
/** @brief Move constructor */
WindowlessWindowsEglContext(WindowlessWindowsEglContext&& other) noexcept;
/** @brief Copying is not allowed */
WindowlessWindowsEglContext& operator=(const WindowlessWindowsEglContext&) = delete;
/** @brief Move assignment */
WindowlessWindowsEglContext& operator=(WindowlessWindowsEglContext&& other) noexcept;
/**
* @brief Destructor
*
* Destroys the context, if any.
*/
~WindowlessWindowsEglContext();
/** @brief Whether the context is created */
bool isCreated() const { return _context; }
/**
* @brief Make the context current
*
* Prints error message and returns @cpp false @ce on failure,
* otherwise returns @cpp true @ce. If the context is current on
* another thread, you have to @ref release() it there first --- an
* OpenGL context can't be current in multiple threads at the same
* time.
*/
bool makeCurrent();
/**
* @brief Release current context
* @m_since_latest
*
* Releases a context previously made current using @ref makeCurrent().
* Prints error message and returns @cpp false @ce on failure,
* otherwise returns @cpp true @ce.
*/
bool release();
/**
* @brief Underlying OpenGL context
* @m_since{2020,06}
*
* Use in case you need to call EGL functionality directly or in order
* to create a shared context. Returns @cpp nullptr @ce in case the
* context was not created yet.
* @see @ref Configuration::setSharedContext()
*/
EGLContext glContext() { return _context; }
private:
HWND _window{};
EGLDisplay _display{};
EGLSurface _surface{};
EGLContext _context{};
};
/**
@brief Configuration
@see @ref WindowlessWindowsEglContext(),
@ref WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(),
@ref WindowlessWindowsEglApplication::createContext(),
@ref WindowlessWindowsEglApplication::tryCreateContext()
*/
class WindowlessWindowsEglContext::Configuration: public GL::Context::Configuration {
public:
/**
* @brief Context flag
*
* Includes also everything from @ref GL::Context::Configuration::Flag
* except for @relativeref{GL::Context::Configuration,Flag::Windowless},
* which is enabled implicitly by default.
* @see @ref Flags, @ref setFlags(), @ref GL::Context::Flag
*/
enum class Flag: UnsignedLong {
/**
* Debug context. Enabled automatically if supported by the driver
* and the @ref Flag::GpuValidation flag is set or if the
* `--magnum-gpu-validation` @ref GL-Context-usage-command-line "command-line option"
* is set to `on`.
*/
Debug = EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
/**
* Context without error reporting. Might result in better
* performance, but situations that would have generated errors
* instead cause undefined behavior. Enabled automatically if
* supported by the driver and the @ref Flag::GpuValidationNoError
* flag is set or if the `--magnum-gpu-validation` @ref GL-Context-usage-command-line "command-line option"
* is set to `no-error`.
* @m_since_latest
*/
/* Treated as a separate attribute and not a flag in EGL, thus
handling manually. */
NoError = 1ull << 32,
/**
* @copydoc GL::Context::Configuration::Flag::QuietLog
* @m_since_latest
*/
QuietLog = UnsignedLong(GL::Context::Configuration::Flag::QuietLog),
/**
* @copydoc GL::Context::Configuration::Flag::VerboseLog
* @m_since_latest
*/
VerboseLog = UnsignedLong(GL::Context::Configuration::Flag::VerboseLog),
/**
* @copydoc GL::Context::Configuration::Flag::GpuValidation
* @m_since_latest
*/
GpuValidation = UnsignedLong(GL::Context::Configuration::Flag::GpuValidation),
/**
* @copydoc GL::Context::Configuration::Flag::GpuValidationNoError
* @m_since_latest
*/
GpuValidationNoError = UnsignedLong(GL::Context::Configuration::Flag::GpuValidationNoError)
};
/**
* @brief Context flags
*
* @see @ref setFlags(), @ref Context::Flags
*/
typedef Containers::EnumSet<Flag> Flags;
/*implicit*/ Configuration();
/** @brief Context flags */
Flags flags() const {
return Flag(UnsignedLong(GL::Context::Configuration::flags()));
}
/**
* @brief Set context flags
* @return Reference to self (for method chaining)
*
* Default is no flag. To avoid clearing default flags by accident,
* prefer to use @ref addFlags() and @ref clearFlags() instead.
* @see @ref GL::Context::flags()
*/
Configuration& setFlags(Flags flags) {
GL::Context::Configuration::setFlags(GL::Context::Configuration::Flag(UnsignedLong(flags)));
return *this;
}
/**
* @brief Add context flags
* @return Reference to self (for method chaining)
*
* Unlike @ref setFlags(), ORs the flags with existing instead of
* replacing them. Useful for preserving the defaults.
* @see @ref clearFlags()
*/
Configuration& addFlags(Flags flags) {
GL::Context::Configuration::addFlags(GL::Context::Configuration::Flag(UnsignedLong(flags)));
return *this;
}
/**
* @brief Clear context flags
* @return Reference to self (for method chaining)
*
* Unlike @ref setFlags(), ANDs the inverse of @p flags with existing
* instead of replacing them. Useful for removing default flags.
* @see @ref addFlags()
*/
Configuration& clearFlags(Flags flags) {
GL::Context::Configuration::clearFlags(GL::Context::Configuration::Flag(UnsignedLong(flags)));
return *this;
}
/**
* @brief Create a shared context
* @return Reference to self (for method chaining)
* @m_since{2020,06}
*
* When set, the created context will share a subset of OpenGL objects
* with @p context, instead of being independent. Many caveats and
* limitations apply to shared OpenGL contexts, please consult the
* OpenGL specification for details. Default is `EGL_NO_CONTEXT`, i.e.
* no sharing.
* @see @ref WindowlessWindowsEglContext::glContext(),
* @ref WindowlessWindowsEglApplication::glContext()
*/
Configuration& setSharedContext(EGLContext context) {
_sharedContext = context;
return *this;
}
/**
* @brief Shared context
* @m_since{2020,06}
*/
EGLContext sharedContext() const { return _sharedContext; }
/* Overloads to remove WTF-factor from method chaining order */
#ifndef DOXYGEN_GENERATING_OUTPUT
MAGNUM_GL_CONTEXT_CONFIGURATION_SUBCLASS_IMPLEMENTATION(Configuration)
#endif
private:
EGLContext _sharedContext = EGL_NO_CONTEXT;
};
CORRADE_ENUMSET_OPERATORS(WindowlessWindowsEglContext::Configuration::Flags)
/**
@brief Windowless Windows/EGL application
@m_keywords{WindowlessApplication EGL}
Application for offscreen rendering using @ref WindowlessWindowsEglContext.
This application library is available on OpenGL ES (also ANGLE) on Windows.
@section Platform-WindowlessWindowsEglApplication-bootstrap Bootstrap application
Fully contained windowless application using @ref WindowlessWindowsEglApplication
along with CMake setup is available in `windowless` branch of
[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository,
download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/windowless.tar.gz)
or [zip](https://github.com/mosra/magnum-bootstrap/archive/windowless.zip)
file. After extracting the downloaded archive you can build and run the
application with these four commands:
@code{.sh}
mkdir build && cd build
cmake ..
cmake --build .
./src/MyApplication # or ./src/Debug/MyApplication
@endcode
See @ref cmake for more information.
@section Platform-WindowlessWindowsEglApplication-usage General usage
This application library is built if `MAGNUM_WITH_WINDOWLESSWINDOWSEGLAPPLICATION`
is enabled when building Magnum. To use this library from CMake, put
[FindEGL.cmake](https://github.com/mosra/magnum/blob/master/modules/FindEGL.cmake)
into your `modules/` directory, request the `WindowlessWindowsEglApplication`
component of the `Magnum` package and link to the
`Magnum::WindowlessWindowsEglApplication` target:
@code{.cmake}
find_package(Magnum REQUIRED)
if(CORRADE_TARGET_WINDOWS)
find_package(Magnum REQUIRED WindowlessWindowsEglApplication)
endif()
# ...
if(CORRADE_TARGET_WINDOWS)
target_link_libraries(your-app PRIVATE Magnum::WindowlessWindowsEglApplication)
endif()
@endcode
Additionally, if you're using Magnum as a CMake subproject, do the following
* *before* calling @cmake find_package() @ce to ensure it's enabled, as the
library is not built by default:
@code{.cmake}
set(MAGNUM_WITH_WINDOWLESSWINDOWSEGLAPPLICATION ON CACHE BOOL "" FORCE)
add_subdirectory(magnum EXCLUDE_FROM_ALL)
@endcode
If no other application is requested, you can also use the generic
`Magnum::WindowlessApplication` alias to simplify porting. Again, see
@ref building and @ref cmake for more information.
Place your code into @ref exec(). The subclass can be then used in main
function using @ref MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN() macro. See
@ref platform for more information.
@code{.cpp}
class MyApplication: public Platform::WindowlessWindowsEglApplication {
// implement required methods...
};
MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN(MyApplication)
@endcode
If no other application header is included, this class is also aliased to
@cpp Platform::WindowlessApplication @ce and the macro is aliased to
@cpp MAGNUM_WINDOWLESSAPPLICATION_MAIN() @ce to simplify porting.
@note This class is available only if Magnum is compiled with
@ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features
for more information.
*/
class WindowlessWindowsEglApplication {
public:
/** @brief Application arguments */
struct Arguments {
/** @brief Constructor */
/*implicit*/ constexpr Arguments(int& argc, char** argv) noexcept: argc{argc}, argv{argv} {}
int& argc; /**< @brief Argument count */
char** argv; /**< @brief Argument values */
};
/**
* @brief Configuration
*
* @see @ref WindowlessWindowsEglApplication(), @ref createContext(),
* @ref tryCreateContext()
*/
typedef WindowlessWindowsEglContext::Configuration Configuration;
/**
* @brief Default constructor
* @param arguments Application arguments
* @param configuration Configuration
*
* Creates application with default or user-specified configuration.
* See @ref Configuration for more information. The program exits if
* the context cannot be created, see @ref tryCreateContext() for an
* alternative.
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
explicit WindowlessWindowsEglApplication(const Arguments& arguments, const Configuration& configuration = Configuration());
#else
/* To avoid "invalid use of incomplete type" */
explicit WindowlessWindowsEglApplication(const Arguments& arguments, const Configuration& configuration);
explicit WindowlessWindowsEglApplication(const Arguments& arguments);
#endif
/**
* @brief Constructor
* @param arguments Application arguments
*
* Unlike above, the context is not created and must be created later
* with @ref createContext() or @ref tryCreateContext().
*/
explicit WindowlessWindowsEglApplication(const Arguments& arguments, NoCreateT);
/** @brief Copying is not allowed */
WindowlessWindowsEglApplication(const WindowlessWindowsEglApplication&) = delete;
/** @brief Moving is not allowed */
WindowlessWindowsEglApplication(WindowlessWindowsEglApplication&&) = delete;
/** @brief Copying is not allowed */
WindowlessWindowsEglApplication& operator=(const WindowlessWindowsEglApplication&) = delete;
/** @brief Moving is not allowed */
WindowlessWindowsEglApplication& operator=(WindowlessWindowsEglApplication&&) = delete;
/**
* @brief Execute application
* @return Value for returning from @cpp main() @ce
*
* See @ref MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN() for usage
* information.
*/
virtual int exec() = 0;
/**
* @brief Underlying OpenGL context
* @m_since{2020,06}
*
* Use in case you need to call EGL functionality directly or in order
* to create a shared context. Returns @cpp nullptr @ce in case the
* context was not created yet.
* @see @ref Configuration::setSharedContext()
*/
EGLContext glContext() { return _glContext.glContext(); }
protected:
/* Nobody will need to have (and delete) WindowlessWindowsEglApplication*,
thus this is faster than public pure virtual destructor */
~WindowlessWindowsEglApplication();
/**
* @brief Create context with given configuration
*
* Must be called if and only if the context wasn't created by the
* constructor itself. Error message is printed and the program exits
* if the context cannot be created, see @ref tryCreateContext() for an
* alternative.
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
void createContext(const Configuration& configuration = Configuration());
#else
/* To avoid "invalid use of incomplete type" */
void createContext(const Configuration& configuration);
void createContext();
#endif
/**
* @brief Try to create context with given configuration
*
* Unlike @ref createContext() returns @cpp false @ce if the context
* cannot be created, @cpp true @ce otherwise.
*/
bool tryCreateContext(const Configuration& configuration);
private:
WindowlessWindowsEglContext _glContext;
Platform::GLContext _context;
};
/** @hideinitializer
@brief Entry point for windowless Windows/EGL application
@param className Class name
@m_keywords{MAGNUM_WINDOWLESSAPPLICATION_MAIN()}
See @ref Magnum::Platform::WindowlessWindowsEglApplication "Platform::WindowlessWindowsEglApplication"
for usage information.This macro abstracts out platform-specific entry point
code and is equivalent to the following, see @ref portability-applications for
more information.
@code{.cpp}
int main(int argc, char** argv) {
className app({argc, argv});
return app.exec();
}
@endcode
When no other windowless application header is included this macro is also
aliased to @cpp MAGNUM_WINDOWLESSAPPLICATION_MAIN() @ce.
*/
#define MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN(className) \
int main(int argc, char** argv) { \
className app({argc, argv}); \
return app.exec(); \
}
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_WINDOWLESSAPPLICATION_MAIN
typedef WindowlessWindowsEglApplication WindowlessApplication;
typedef WindowlessWindowsEglContext WindowlessGLContext;
#define MAGNUM_WINDOWLESSAPPLICATION_MAIN(className) MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN(className)
#else
#undef MAGNUM_WINDOWLESSAPPLICATION_MAIN
#endif
#endif
}}
#else
#error this header is available only in the OpenGL build
#endif
#endif

21
src/Magnum/Platform/gl-info.cpp

@ -58,24 +58,16 @@
#include "Magnum/GL/TransformFeedback.h"
#endif
#if defined(MAGNUM_TARGET_HEADLESS) || defined(CORRADE_TARGET_EMSCRIPTEN) || defined(CORRADE_TARGET_ANDROID)
#ifdef MAGNUM_TARGET_EGL
#include "Magnum/Platform/WindowlessEglApplication.h"
#elif defined(CORRADE_TARGET_IOS)
#include "Magnum/Platform/WindowlessIosApplication.h"
#elif defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
#elif defined(CORRADE_TARGET_APPLE)
#include "Magnum/Platform/WindowlessCglApplication.h"
#elif defined(CORRADE_TARGET_UNIX)
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_DESKTOP_GLES)
#include "Magnum/Platform/WindowlessEglApplication.h"
#else
#include "Magnum/Platform/WindowlessGlxApplication.h"
#endif
#elif defined(CORRADE_TARGET_WINDOWS)
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_DESKTOP_GLES)
#include "Magnum/Platform/WindowlessWindowsEglApplication.h"
#else
#include "Magnum/Platform/WindowlessWglApplication.h"
#endif
#else
#error no windowless application available on this platform
#endif
@ -241,8 +233,6 @@ MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplicat
Debug{} << "Used application: Platform::WindowlessGlxApplication";
#elif defined(MAGNUM_WINDOWLESSWGLAPPLICATION_MAIN)
Debug{} << "Used application: Platform::WindowlessWglApplication";
#elif defined(MAGNUM_WINDOWLESSWINDOWSEGLAPPLICATION_MAIN)
Debug{} << "Used application: Platform::WindowlessWindowsEglApplication";
#else
#error no windowless application available on this platform
#endif
@ -349,14 +339,11 @@ MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplicat
#ifdef MAGNUM_TARGET_GLES2
Debug{} << " MAGNUM_TARGET_GLES2";
#endif
#ifdef MAGNUM_TARGET_DESKTOP_GLES
Debug{} << " MAGNUM_TARGET_DESKTOP_GLES";
#endif
#ifdef MAGNUM_TARGET_WEBGL
Debug{} << " MAGNUM_TARGET_WEBGL";
#endif
#ifdef MAGNUM_TARGET_HEADLESS
Debug{} << " MAGNUM_TARGET_HEADLESS";
#ifdef MAGNUM_TARGET_EGL
Debug{} << " MAGNUM_TARGET_EGL";
#endif
Debug{} << "Compiled CPU features:";
Debug{} << " " << Debug::packed << Cpu::compiledFeatures();

17
src/Magnum/SceneTools/CMakeLists.txt

@ -27,6 +27,17 @@
# property that would have to be set on each target separately.
set(CMAKE_FOLDER "Magnum/SceneTools")
# Somehow, due to MagnumTradeObjects having target_include_directories() with
# $<TARGET_PROPERTY:Corrade::PluginManager,INTERFACE_INCLUDE_DIRECTORIES>,
# if MAGNUM_SCENECONVERTER_STATIC_PLUGINS is non-empty then CMake fails with
#
# Target "Corrade::PluginManager" not found.
#
# unless the find_package() is here. Not sure why, probably some bug in CMake
# dependency handling? Changing target_include_directories() to PRIVATE doesn't
# help, removing it altogether helps.
find_package(Corrade REQUIRED PluginManager)
# Files shared between main library and unit test library
set(MagnumSceneTools_SRCS )
@ -44,7 +55,8 @@ set(MagnumSceneTools_HEADERS
set(MagnumSceneTools_PRIVATE_HEADERS
Implementation/combine.h
Implementation/convertToSingleFunctionObjects.h)
Implementation/convertToSingleFunctionObjects.h
Implementation/sceneConverterUtilities.h)
## Objects shared between main and test library
#add_library(MagnumSceneToolsObjects OBJECT
@ -87,7 +99,8 @@ if(MAGNUM_WITH_SCENECONVERTER)
Magnum
MagnumMeshTools
MagnumSceneTools
MagnumTrade)
MagnumTrade
${MAGNUM_SCENECONVERTER_STATIC_PLUGINS})
install(TARGETS magnum-sceneconverter DESTINATION ${MAGNUM_BINARY_INSTALL_DIR})

1035
src/Magnum/SceneTools/Implementation/sceneConverterUtilities.h

File diff suppressed because it is too large Load Diff

58
src/Magnum/SceneTools/Test/CMakeLists.txt

@ -27,7 +27,65 @@
# property that would have to be set on each target separately.
set(CMAKE_FOLDER "Magnum/SceneTools/Test")
if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID)
set(SCENETOOLS_TEST_DIR ".")
set(SCENETOOLS_TEST_OUTPUT_DIR "write")
else()
set(SCENETOOLS_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(SCENETOOLS_TEST_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
endif()
# Executable testing is implemented on Unix platforms only at the moment, so
# don't even provide the filename elsewhere.
if(MAGNUM_WITH_SCENECONVERTER AND CORRADE_TARGET_UNIX)
set(SCENECONVERTER_EXECUTABLE_FILENAME $<TARGET_FILE:magnum-sceneconverter>)
endif()
# First replace ${} variables, then $<> generator expressions
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/configure.h.in)
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/configure.h
INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in)
corrade_add_test(SceneToolsCombineTest CombineTest.cpp LIBRARIES MagnumTrade)
corrade_add_test(SceneToolsConvertToSingleFun___Test ConvertToSingleFunctionObjectsTest.cpp LIBRARIES MagnumTrade)
corrade_add_test(SceneToolsFlattenMeshHierarchyTest FlattenMeshHierarchyTest.cpp LIBRARIES MagnumSceneToolsTestLib)
corrade_add_test(SceneToolsOrderClusterParentsTest OrderClusterParentsTest.cpp LIBRARIES MagnumSceneToolsTestLib)
corrade_add_test(SceneToolsSceneConverterTest SceneConverterTest.cpp
LIBRARIES
MagnumSceneTools
# Link the same static plugins as for the magnum-sceneconverter
# executable so plugin existence checks are consistent between the two
${MAGNUM_SCENECONVERTER_STATIC_PLUGINS}
FILES
SceneConverterTestFiles/broken-mesh.obj
SceneConverterTestFiles/broken-scene.gltf
SceneConverterTestFiles/empty.gltf
SceneConverterTestFiles/info-animations.txt
SceneConverterTestFiles/info-cameras.txt
SceneConverterTestFiles/info-images.txt
SceneConverterTestFiles/info-lights.txt
SceneConverterTestFiles/info-materials.txt
SceneConverterTestFiles/info-meshes-bounds.txt
SceneConverterTestFiles/info-meshes.txt
SceneConverterTestFiles/info-objects.txt
SceneConverterTestFiles/info-references.txt
SceneConverterTestFiles/info-scenes-objects.txt
SceneConverterTestFiles/info-scenes.txt
SceneConverterTestFiles/info-skins.txt
SceneConverterTestFiles/info-textures.txt
SceneConverterTestFiles/point.obj
SceneConverterTestFiles/quad-duplicates-fuzzy.obj
SceneConverterTestFiles/quad-duplicates.obj
SceneConverterTestFiles/quad-duplicates.ply
SceneConverterTestFiles/quad-normals-texcoords.obj
SceneConverterTestFiles/quad.obj
SceneConverterTestFiles/quad.ply
SceneConverterTestFiles/two-triangles-transformed.bin
SceneConverterTestFiles/two-triangles-transformed.gltf
SceneConverterTestFiles/two-triangles.obj)
target_include_directories(SceneToolsSceneConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)
if(MAGNUM_WITH_SCENECONVERTER AND CORRADE_TARGET_UNIX)
add_dependencies(SceneToolsSceneConverterTest magnum-sceneconverter)
endif()

1686
src/Magnum/SceneTools/Test/SceneConverterTest.cpp

File diff suppressed because it is too large Load Diff

3
src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-mesh.obj

@ -0,0 +1,3 @@
# A point with two indices
v 1 2 3
p 5 5

22
src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-scene.gltf

@ -0,0 +1,22 @@
{
"asset": {
"version": "2.0"
},
"scenes": [
{
"nodes": [0]
}
],
"meshes": [
{
"primitives": [
{}
]
}
],
"nodes": [
{
"mesh": 1
}
]
}

5
src/Magnum/SceneTools/Test/SceneConverterTestFiles/empty.gltf

@ -0,0 +1,5 @@
{
"asset": {
"version": "2.0"
}
}

12
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-animations.txt

@ -0,0 +1,12 @@
Animation 0:
Duration: {0.5, 1.25} (0.1 kB)
Track 0: Translation2D @ Vector2, 3 keyframes
Interpolation: Linear, DefaultConstructed, Constant
Track 1: Rotation2D @ CubicHermite2D -> Vector2, 3 keyframes
Interpolation: Constant, Extrapolated, Extrapolated
Animation 1: Custom track duration and interpolator function
Duration: {0.1, 1.3} (0.1 kB, ExternallyOwned)
Track 0: Scaling3D @ Vector3, 5 keyframes
Duration: {0.75, 1.25}
Interpolation: Custom, DefaultConstructed, Constant
Total animation data size: 0.2 kB

12
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-cameras.txt

@ -0,0 +1,12 @@
Camera 0: Orthographic 2D
Type: Orthographic2D
Size: {5, 6}
Aspect ratio: 0.833333
Camera 1:
Type: Orthographic3D
Size: {2, 3}, -1 - 0.5
Aspect ratio: 0.666667
Camera 2:
Type: Perspective3D
FoV: 35°, 0.01 - 100
Aspect ratio: 1.33333

6
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-ignored-output.txt

@ -0,0 +1,6 @@
Ignoring output file for --info: whatever.ply
Mesh 0:
Level 0: 1 vertices @ Points (0.0 kB)
Position @ Vector3, offset 0, stride 12
1 indices @ UnsignedInt, offset 0, stride 4 (0.0 kB)
Total mesh data size: 0.0 kB

3
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-images.txt

@ -0,0 +1,3 @@
1D image 0:
Level 0: {1024} @ R32F (4.0 kB)
Total image data size: 4.0 kB

8
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-lights.txt

@ -0,0 +1,8 @@
Light 0:
Type: Spot, 55° - 85°
Color: {0.203922, 0.341176, 1} * 15
Attenuation: {1.2, 0.3, 0.04}
Range: 100
Light 1: Directional light with always-implicit attenuation and range
Type: Directional
Color: {1, 0.341176, 0.203922} * 5

28
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-materials.txt

@ -0,0 +1,28 @@
Material 0:
Type: PbrMetallicRoughness
Base layer:
BaseColor @ Vector4: {0.231373, 0.823529, 0.403922, 0.6}
DoubleSided @ Bool: true
EmissiveColor @ Vector3: {0.054902, 0.619608, 0.792157}
RoughnessTexture @ UnsignedInt: 67
RoughnessTextureMatrix @ Matrix3x3: {1, 0, 0.25,
0, 1, 0.75,
0, 0, 1}
RoughnessTextureSwizzle @ TextureSwizzle: B
deadBeef @ Pointer: 0xdeadbeef
notAColour3 @ Vector3: {0.2, 0.3, 0.4}
notAColour4 @ Vector4: {0.1, 0.2, 0.3, 0.4}
reflectionAngle @ Deg: 35
undeadBeef @ MutablePointer: 0xbeefbeef
Material 1: Lots o' laierz
Type: Phong|PbrClearCoat
Base layer:
DiffuseColor @ Vector4: {0.780392, 0.811765, 0.184314, 0.6}
Layer 1: ClearCoat
LayerFactor @ Float: 0.5
LayerFactorTexture @ UnsignedInt: 3
Layer 2: anEmptyLayer
Layer 3:
LayerFactor @ Float: 0.25
LayerFactorTexture @ UnsignedInt: 2
yes @ String: a string

21
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-meshes-bounds.txt

@ -0,0 +1,21 @@
Mesh 0:
Level 0: 2 vertices @ Lines (0.2 kB, {})
Position @ Vector3, offset 0, stride 12
Bounds: ({0.1, -0.1, -0.2}, {0.2, 0, 0.2})
Tangent @ Vector3, offset 24, stride 12
Bounds: ({0.2, -0.2, 0.2}, {0.3, 0.8, 0.8})
Bitangent @ Vector3, offset 48, stride 12
Bounds: ({0.3, 0.2, 0}, {0.4, 0.9, 1})
ObjectId @ UnsignedShort, offset 72, stride 2
Bounds: (12, 155)
Normal @ Vector3, offset 76, stride 12
Bounds: ({0, 0, 0}, {1, 1, 1})
TextureCoordinates @ Vector2, offset 100, stride 8
Bounds: ({0.5, 0.5}, {1.5, 0.5})
Color @ Vector4, offset 116, stride 16
Bounds: ({0.6, 0.2, 0.2, 0}, {1, 0.4, 0.4, 0.2})
ObjectId @ UnsignedInt, offset 148, stride 4
Bounds: (15, 337)
3 indices @ UnsignedByte, offset 0, stride 1 (0.0 kB, {})
Bounds: (3, 176)
Total mesh data size: 0.2 kB

16
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-meshes.txt

@ -0,0 +1,16 @@
Mesh 0:
Level 0: 50 vertices @ Points (0.6 kB, ExternallyOwned|Mutable)
Position @ Vector3, offset 0, stride 12
70 indices @ UnsignedShort, offset 0, stride 2 (0.1 kB, ExternallyOwned)
Mesh 1: LODs? No, meshets.
Level 0: 250 vertices @ Triangles (6.8 kB)
Position @ Vector3, offset 0, stride 12
Tangent @ Vector4, offset 3000, stride 16
Level 1: 135 vertices @ Meshlets (83.8 kB)
Custom(25:vertices) @ UnsignedInt[64], offset 0, stride 256
Custom(26:triangles) @ Vector3ub[126], offset 34560, stride 378
Custom(37:) @ UnsignedByte, offset 85590, stride 1
Custom(116:vertexCount) @ UnsignedByte, offset 85725, stride 1
Mesh 2:
Level 0: 15 vertices @ Instances (0.0 kB)
Total mesh data size: 91.4 kB

5
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-objects.txt

@ -0,0 +1,5 @@
Object 0: Parent-less mesh
Object 2: Two meshes, shared among two scenes
Object 4: Two custom arrays
Object 6: Only in the second scene, but no fields, thus same as unreferenced
Object 8: Not in any scene

111
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-references.txt

@ -0,0 +1,111 @@
Scene 0:
Bound: 2 objects @ UnsignedInt (0.1 kB, {})
Fields:
Transformation @ Matrix4x4, 0 entries
Mesh @ UnsignedInt, 4 entries
MeshMaterial @ Int, 4 entries
Light @ UnsignedInt, 4 entries
Camera @ UnsignedInt, 4 entries
Skin @ UnsignedInt, 4 entries
Scene 1:
Bound: 4 objects @ UnsignedInt (0.0 kB, {})
Fields:
Transformation @ Matrix3x3, 0 entries
Mesh @ UnsignedInt, 3 entries
Skin @ UnsignedInt, 3 entries
Total scene data size: 0.1 kB
Object 0 (referenced by 1 scenes):
Fields: Mesh, MeshMaterial, Light, Camera, Skin
Object 1 (referenced by 2 scenes):
Fields: Mesh[2], MeshMaterial[2], Light[2], Camera[2], Skin[2], Mesh, Skin
Object 2 (referenced by 0 scenes): Not referenced
Object 3 (referenced by 1 scenes):
Fields: Mesh, Skin
2D skin 0 (referenced by 1 objects):
2 joints
2D skin 1 (referenced by 1 objects):
3 joints
2D skin 2 (referenced by 0 objects): Not referenced
1 joints
3D skin 0 (referenced by 0 objects): Not referenced
2 joints
3D skin 1 (referenced by 2 objects):
1 joints
3D skin 2 (referenced by 1 objects):
3 joints
Light 0 (referenced by 2 objects):
Type: Directional
Color: {0.341176, 1, 0.203922} * 5
Light 1 (referenced by 0 objects): Not referenced
Type: Ambient
Color: {1, 0.341176, 0.203922} * 0.1
Light 2 (referenced by 1 objects):
Type: Directional
Color: {0.203922, 0.341176, 1}
Camera 0 (referenced by 0 objects): Not referenced
Type: Orthographic3D
Size: {2, 3}, -1 - 0.5
Aspect ratio: 0.666667
Camera 1 (referenced by 2 objects):
Type: Orthographic3D
Size: {2, 2}, 0 - 1
Aspect ratio: 1
Camera 2 (referenced by 1 objects):
Type: Orthographic2D
Size: {2, 2}
Aspect ratio: 1
Material 0 (referenced by 2 objects):
Type: {}
Base layer:
BaseColorTexture @ UnsignedInt: 2
DiffuseTexture @ UnsignedInt: 2
Material 1 (referenced by 1 objects):
Type: {}
Base layer:
EmissiveTexture @ UnsignedInt: 4
NormalTexture @ UnsignedInt: 17
lookupTexture @ UnsignedInt: 0
volumeTexture @ UnsignedInt: 3
Material 2 (referenced by 0 objects): Not referenced
Type: {}
Base layer:
Mesh 0 (referenced by 2 objects):
Level 0: 5 vertices @ Points (0.0 kB)
Mesh 1 (referenced by 0 objects): Not referenced
Level 0: 4 vertices @ Lines (0.0 kB)
Mesh 2 (referenced by 3 objects):
Level 0: 4 vertices @ TriangleFan (0.0 kB)
Total mesh data size: 0.0 kB
Texture 0 (referenced by 1 material attributes):
Type: Texture1D, image 1
Minification, mipmap and magnification: Nearest, Nearest, Linear
Wrapping: {Repeat, Repeat, Repeat}
Texture 1 (referenced by 0 material attributes): Not referenced
Type: Texture1DArray, image 225
Minification, mipmap and magnification: Nearest, Nearest, Linear
Wrapping: {Repeat, Repeat, Repeat}
Texture 2 (referenced by 2 material attributes):
Type: Texture2D, image 0
Minification, mipmap and magnification: Nearest, Nearest, Linear
Wrapping: {Repeat, Repeat, Repeat}
Texture 3 (referenced by 1 material attributes):
Type: Texture3D, image 1
Minification, mipmap and magnification: Nearest, Nearest, Linear
Wrapping: {Repeat, Repeat, Repeat}
Texture 4 (referenced by 1 material attributes):
Type: Texture2D, image 0
Minification, mipmap and magnification: Nearest, Nearest, Linear
Wrapping: {Repeat, Repeat, Repeat}
1D image 0 (referenced by 0 textures): Not referenced
Level 0: {1} @ RGBA8I (0.0 kB)
1D image 1 (referenced by 1 textures):
Level 0: {4} @ R8I (0.0 kB)
2D image 0 (referenced by 2 textures):
Level 0: {1, 2} @ RGBA8I (0.0 kB)
2D image 1 (referenced by 0 textures): Not referenced
Level 0: {4, 1} @ R8I (0.0 kB)
3D image 0 (referenced by 0 textures): Not referenced
Level 0: {1, 2, 1} @ RGBA8I (0.0 kB)
3D image 1 (referenced by 1 textures):
Level 0: {4, 1, 1} @ R8I (0.0 kB)
Total image data size: 0.0 kB

25
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-scenes-objects.txt

@ -0,0 +1,25 @@
Scene 0: A simple scene
Bound: 4 objects @ UnsignedInt (0.1 kB)
Fields:
Parent @ Int, 3 entries
Mesh @ UnsignedInt, OrderedMapping, 4 entries
Scene 1:
Bound: 8 objects @ UnsignedByte (0.0 kB, ExternallyOwned|Mutable)
Fields:
Custom(42:) @ Double, 2 entries
Custom(1337:DirectionVector) @ Short[3], 3 entries
Total scene data size: 0.1 kB
Object 0 (referenced by 1 scenes): Parent-less mesh
Fields: Mesh
Object 1 (referenced by 1 scenes):
Fields: Parent, Mesh
Object 2 (referenced by 2 scenes): Two meshes, shared among two scenes
Fields: Parent, Mesh[2], Custom(1337:DirectionVector)
Object 3 (referenced by 2 scenes):
Fields: Parent, Custom(42:)
Object 4 (referenced by 1 scenes): Two custom arrays
Fields: Custom(1337:DirectionVector)[2]
Object 6 (referenced by 0 scenes): Only in the second scene, but no fields, thus same as unreferenced
Object 7 (referenced by 1 scenes):
Fields: Custom(42:)
Object 8 (referenced by 0 scenes): Not in any scene

11
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-scenes.txt

@ -0,0 +1,11 @@
Scene 0: A simple scene
Bound: 4 objects @ UnsignedInt (0.1 kB)
Fields:
Parent @ Int, 3 entries
Mesh @ UnsignedInt, OrderedMapping, 4 entries
Scene 1:
Bound: 8 objects @ UnsignedByte (0.0 kB, ExternallyOwned|Mutable)
Fields:
Custom(42:) @ Double, 2 entries
Custom(1337:DirectionVector) @ Short[3], 3 entries
Total scene data size: 0.1 kB

10
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-skins.txt

@ -0,0 +1,10 @@
2D skin 0:
5 joints
2D skin 1: Second 2D skin, external data
15 joints
3D skin 0: First 3D skin, external data
12 joints
3D skin 1:
2 joints
3D skin 2:
1 joints

8
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-textures.txt

@ -0,0 +1,8 @@
Texture 0:
Type: Texture1D, image 666
Minification, mipmap and magnification: Nearest, Nearest, Linear
Wrapping: {Repeat, Repeat, Repeat}
Texture 1: Name!
Type: Texture2DArray, image 3
Minification, mipmap and magnification: Linear, Linear, Nearest
Wrapping: {MirroredRepeat, ClampToEdge, ClampToEdge}

5
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info.txt

@ -0,0 +1,5 @@
Mesh 0:
Level 0: 1 vertices @ Points (0.0 kB)
Position @ Vector3, offset 0, stride 12
1 indices @ UnsignedInt, offset 0, stride 4 (0.0 kB)
Total mesh data size: 0.0 kB

2
src/Magnum/SceneTools/Test/SceneConverterTestFiles/point.obj

@ -0,0 +1,2 @@
v 1 2 3
p 1

12
src/Magnum/SceneTools/Test/SceneConverterTestFiles/quad-duplicates-fuzzy.obj

@ -0,0 +1,12 @@
# 1 4--6
# |\ \ |
# | \ \|
# 2--3 5
v -1 1 0
v -1 -1 0
v 1 -1 0
f 1 2 3
v -0.9 0.9 0
v 0.9 -0.9 0
v 1 1 0
f 4 5 6

12
src/Magnum/SceneTools/Test/SceneConverterTestFiles/quad-duplicates.obj

@ -0,0 +1,12 @@
# 1 4--6
# |\ \ |
# | \ \|
# 2--3 5
v -1 1 0
v -1 -1 0
v 1 -1 0
f 1 2 3
v -1 1 0
v 1 -1 0
v 1 1 0
f 4 5 6

BIN
src/Magnum/SceneTools/Test/SceneConverterTestFiles/quad-duplicates.ply

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save