Browse Source

GL: implement time queries for WebGL 2, and workarounds, and ...

There's a new firefox-fake-disjoint-timer-query-webgl2 workaround and a
half-page of text listing various caveats and issues you might run into.
Also exposing them in the OpenGLTester (although quite shitty at this
point).
pull/388/head
Vladimír Vondruš 7 years ago
parent
commit
60c18f93a1
  1. 6
      doc/changelog.dox
  2. 22
      doc/platforms-html5.dox
  3. 10
      src/Magnum/GL/AbstractQuery.cpp
  4. 9
      src/Magnum/GL/AbstractQuery.h
  5. 17
      src/Magnum/GL/CMakeLists.txt
  6. 3
      src/Magnum/GL/Context.cpp
  7. 9
      src/Magnum/GL/Extensions.h
  8. 2
      src/Magnum/GL/GL.h
  9. 4
      src/Magnum/GL/Implementation/QueryState.h
  10. 4
      src/Magnum/GL/Implementation/State.cpp
  11. 4
      src/Magnum/GL/Implementation/State.h
  12. 23
      src/Magnum/GL/Implementation/driverSpecific.cpp
  13. 2
      src/Magnum/GL/OpenGLTester.cpp
  14. 64
      src/Magnum/GL/OpenGLTester.h
  15. 11
      src/Magnum/GL/Test/CMakeLists.txt
  16. 14
      src/Magnum/GL/Test/TimeQueryGLTest.cpp
  17. 13
      src/Magnum/GL/TimeQuery.h
  18. 1
      src/MagnumExternal/OpenGL/GLES3/Emscripten/extensions.txt

6
doc/changelog.dox

@ -194,6 +194,12 @@ See also:
shell (as opposed to an APK) because using it causes the shell (as opposed to an APK) because using it causes the
@ref GL::Renderer::Error::OutOfMemory error. See @ref opengl-workarounds @ref GL::Renderer::Error::OutOfMemory error. See @ref opengl-workarounds
for more information. for more information.
- Implemented @webgl_extension{EXT,disjoint_timer_query} and
@webgl_extension{EXT,disjoint_timer_query_webgl2} for WebGL, together with
a new @cpp "firefox-fake-disjoint-timer-query-webgl2" @ce workaround. See
@ref opengl-workarounds, @ref platforms-html5-webgl-timer-queries and
@ref platforms-html5-webgl-queries-zero for more information about various
caveats.
- New `--magnum-gpu-validation` @ref GL-Context-command-line "command-line option" - New `--magnum-gpu-validation` @ref GL-Context-command-line "command-line option"
and a corresponding environment variable to conveniently enable and a corresponding environment variable to conveniently enable
@gl_extension{KHR,debug} debug output. This flag also causes @gl_extension{KHR,debug} debug output. This flag also causes

22
doc/platforms-html5.dox

@ -438,6 +438,28 @@ WebGL specification provide even more detail:
- [Differences Between WebGL and OpenGL ES 2.0](http://www.khronos.org/registry/webgl/specs/latest/1.0/#6) - [Differences Between WebGL and OpenGL ES 2.0](http://www.khronos.org/registry/webgl/specs/latest/1.0/#6)
- [Differences Between WebGL and OpenGL ES 3.0](https://www.khronos.org/registry/webgl/specs/latest/2.0/#5) - [Differences Between WebGL and OpenGL ES 3.0](https://www.khronos.org/registry/webgl/specs/latest/2.0/#5)
@subsection platforms-html5-webgl-timer-queries Timer queries not available in the browser
Similarly to [Rowhammer](https://en.wikipedia.org/wiki/Row_hammer), GPU timer
queries could be abused to do a bit-flip attack, which is the reason why
@webgl_extension{EXT,disjoint_timer_query} / @webgl_extension{EXT,disjoint_timer_query_webgl2} might not be available in
your browser. Recent versions of Chrome [expose it again](https://bugs.chromium.org/p/chromium/issues/detail?id=823863),
and Firefox exposes it if you enable `webgl.enable-privileged-extensions` in
`about:config` ([source](https://www.khronos.org/webgl/public-mailing-list/public_webgl/1803/msg00017.php)).
[More details about the GLitch vulnerability.](https://www.vusec.net/wp-content/uploads/2018/05/glitch.pdf)
@subsection platforms-html5-webgl-queries-zero Queries always report zero results
Compared to OpenGL ES, WebGL [has an additional restriction](https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.12)
where it's not guaranteed for a @ref GL::PrimitiveQuery, @ref GL::SampleQuery
or @ref GL::TimeQuery to return a result in the same frame, which in practice
means that the queries will return zero. This is particularly problematic in
headless tests / benchmarks and currently there's no builtin way in Magnum to
circumvent this limitation.
If you're on Firefox, you can enable the `webgl.allow-immediate-queries` option
in `about:flags`, which will make these working.
@section platforms-html5-code-size Compilation time / code size tradeoffs @section platforms-html5-code-size Compilation time / code size tradeoffs
By default, the toolchain configures the compiler and linker to use `-O3` in By default, the toolchain configures the compiler and linker to use `-O3` in

10
src/Magnum/GL/AbstractQuery.cpp

@ -46,10 +46,8 @@ AbstractQuery::~AbstractQuery() {
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
glDeleteQueries(1, &_id); glDeleteQueries(1, &_id);
#elif !defined(CORRADE_TARGET_EMSCRIPTEN)
glDeleteQueriesEXT(1, &_id);
#else #else
CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ glDeleteQueriesEXT(1, &_id);
#endif #endif
_flags |= ObjectFlag::Created; _flags |= ObjectFlag::Created;
} }
@ -57,10 +55,8 @@ AbstractQuery::~AbstractQuery() {
void AbstractQuery::createImplementationDefault() { void AbstractQuery::createImplementationDefault() {
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
glGenQueries(1, &_id); glGenQueries(1, &_id);
#elif !defined(CORRADE_TARGET_EMSCRIPTEN)
glGenQueriesEXT(1, &_id);
#else #else
CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ glGenQueriesEXT(1, &_id);
#endif #endif
} }
@ -112,7 +108,6 @@ template<> UnsignedInt AbstractQuery::result<UnsignedInt>() {
template<> bool AbstractQuery::result<bool>() { return result<UnsignedInt>() != 0; } template<> bool AbstractQuery::result<bool>() { return result<UnsignedInt>() != 0; }
#ifndef MAGNUM_TARGET_WEBGL
template<> Int AbstractQuery::result<Int>() { template<> Int AbstractQuery::result<Int>() {
Int result; Int result;
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
@ -143,7 +138,6 @@ template<> Long AbstractQuery::result<Long>() {
return result; return result;
} }
#endif #endif
#endif
void AbstractQuery::begin() { void AbstractQuery::begin() {
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2

9
src/Magnum/GL/AbstractQuery.h

@ -25,11 +25,9 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
/** @file /** @file
* @brief Class @ref Magnum::GL::AbstractQuery * @brief Class @ref Magnum::GL::AbstractQuery
*/ */
#endif
#include <Corrade/Containers/ArrayView.h> #include <Corrade/Containers/ArrayView.h>
#include <Corrade/Utility/Assert.h> #include <Corrade/Utility/Assert.h>
@ -39,7 +37,6 @@
#include "Magnum/configure.h" #include "Magnum/configure.h"
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
namespace Magnum { namespace GL { namespace Magnum { namespace GL {
namespace Implementation { struct QueryState; } namespace Implementation { struct QueryState; }
@ -49,7 +46,6 @@ namespace Implementation { struct QueryState; }
See @ref PrimitiveQuery, @ref SampleQuery and @ref TimeQuery documentation for See @ref PrimitiveQuery, @ref SampleQuery and @ref TimeQuery documentation for
more information. more information.
@requires_webgl20 Queries are not available in WebGL 1.0.
@todo `QUERY_COUNTER_BITS` (not sure since when this is supported) @todo `QUERY_COUNTER_BITS` (not sure since when this is supported)
*/ */
class MAGNUM_GL_EXPORT AbstractQuery: public AbstractObject { class MAGNUM_GL_EXPORT AbstractQuery: public AbstractObject {
@ -203,11 +199,9 @@ class MAGNUM_GL_EXPORT AbstractQuery: public AbstractObject {
template<> bool MAGNUM_GL_EXPORT AbstractQuery::result<bool>(); template<> bool MAGNUM_GL_EXPORT AbstractQuery::result<bool>();
template<> UnsignedInt MAGNUM_GL_EXPORT AbstractQuery::result<UnsignedInt>(); template<> UnsignedInt MAGNUM_GL_EXPORT AbstractQuery::result<UnsignedInt>();
template<> Int MAGNUM_GL_EXPORT AbstractQuery::result<Int>(); template<> Int MAGNUM_GL_EXPORT AbstractQuery::result<Int>();
#ifndef MAGNUM_TARGET_WEBGL
template<> UnsignedLong MAGNUM_GL_EXPORT AbstractQuery::result<UnsignedLong>(); template<> UnsignedLong MAGNUM_GL_EXPORT AbstractQuery::result<UnsignedLong>();
template<> Long MAGNUM_GL_EXPORT AbstractQuery::result<Long>(); template<> Long MAGNUM_GL_EXPORT AbstractQuery::result<Long>();
#endif #endif
#endif
inline AbstractQuery::AbstractQuery(AbstractQuery&& other) noexcept: _id(other._id), _target(other._target) { inline AbstractQuery::AbstractQuery(AbstractQuery&& other) noexcept: _id(other._id), _target(other._target) {
other._id = 0; other._id = 0;
@ -227,8 +221,5 @@ inline GLuint AbstractQuery::release() {
} }
}} }}
#else
#error this header is not available in WebGL 1.0 build
#endif
#endif #endif

17
src/Magnum/GL/CMakeLists.txt

@ -25,6 +25,7 @@
set(MagnumGL_SRCS set(MagnumGL_SRCS
AbstractObject.cpp AbstractObject.cpp
AbstractQuery.cpp
AbstractShaderProgram.cpp AbstractShaderProgram.cpp
Attribute.cpp Attribute.cpp
Buffer.cpp Buffer.cpp
@ -43,6 +44,7 @@ set(MagnumGL_SRCS
Implementation/ContextState.cpp Implementation/ContextState.cpp
Implementation/FramebufferState.cpp Implementation/FramebufferState.cpp
Implementation/MeshState.cpp Implementation/MeshState.cpp
Implementation/QueryState.cpp
Implementation/RendererState.cpp Implementation/RendererState.cpp
Implementation/ShaderProgramState.cpp Implementation/ShaderProgramState.cpp
Implementation/ShaderState.cpp Implementation/ShaderState.cpp
@ -63,6 +65,7 @@ set(MagnumGL_GracefulAssert_SRCS
set(MagnumGL_HEADERS set(MagnumGL_HEADERS
AbstractFramebuffer.h AbstractFramebuffer.h
AbstractObject.h AbstractObject.h
AbstractQuery.h
AbstractShaderProgram.h AbstractShaderProgram.h
AbstractTexture.h AbstractTexture.h
Attribute.h Attribute.h
@ -84,6 +87,7 @@ set(MagnumGL_HEADERS
Shader.h Shader.h
Texture.h Texture.h
TextureFormat.h TextureFormat.h
TimeQuery.h
Version.h Version.h
visibility.h) visibility.h)
@ -95,6 +99,7 @@ set(MagnumGL_PRIVATE_HEADERS
Implementation/FramebufferState.h Implementation/FramebufferState.h
Implementation/maxTextureSize.h Implementation/maxTextureSize.h
Implementation/MeshState.h Implementation/MeshState.h
Implementation/QueryState.h
Implementation/RendererState.h Implementation/RendererState.h
Implementation/ShaderProgramState.h Implementation/ShaderProgramState.h
Implementation/ShaderState.h Implementation/ShaderState.h
@ -137,8 +142,7 @@ if(NOT TARGET_WEBGL)
Implementation/DebugState.cpp) Implementation/DebugState.cpp)
list(APPEND MagnumGL_HEADERS list(APPEND MagnumGL_HEADERS
DebugOutput.h DebugOutput.h)
TimeQuery.h)
list(APPEND MagnumGL_PRIVATE_HEADERS list(APPEND MagnumGL_PRIVATE_HEADERS
Implementation/DebugState.h) Implementation/DebugState.h)
@ -160,17 +164,8 @@ endif()
# Desktop, OpenGL ES and WebGL 2.0 stuff that is not available in WebGL 1.0 # Desktop, OpenGL ES and WebGL 2.0 stuff that is not available in WebGL 1.0
if(NOT (TARGET_WEBGL AND TARGET_GLES2)) if(NOT (TARGET_WEBGL AND TARGET_GLES2))
list(APPEND MagnumGL_SRCS
AbstractQuery.cpp
Implementation/QueryState.cpp)
list(APPEND MagnumGL_HEADERS list(APPEND MagnumGL_HEADERS
AbstractQuery.h
SampleQuery.h) SampleQuery.h)
list(APPEND MagnumGL_PRIVATE_HEADERS
Implementation/QueryState.h)
endif() endif()
# Objects shared between main and test library # Objects shared between main and test library

3
src/Magnum/GL/Context.cpp

@ -247,9 +247,12 @@ constexpr Extension ExtensionList460[]{
#elif defined(MAGNUM_TARGET_WEBGL) #elif defined(MAGNUM_TARGET_WEBGL)
constexpr Extension ExtensionList[]{ constexpr Extension ExtensionList[]{
_extension(EXT,texture_filter_anisotropic), _extension(EXT,texture_filter_anisotropic),
#ifdef MAGNUM_TARGET_GLES2
_extension(EXT,disjoint_timer_query), _extension(EXT,disjoint_timer_query),
#endif
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
_extension(EXT,color_buffer_float), _extension(EXT,color_buffer_float),
_extension(EXT,disjoint_timer_query_webgl2),
#endif #endif
_extension(EXT,texture_compression_rgtc), _extension(EXT,texture_compression_rgtc),
_extension(EXT,texture_compression_bptc), _extension(EXT,texture_compression_bptc),

9
src/Magnum/GL/Extensions.h

@ -274,16 +274,21 @@ namespace ANGLE {
_extension( 4,EXT,sRGB, GLES200, GLES300) // #17 _extension( 4,EXT,sRGB, GLES200, GLES300) // #17
_extension( 5,EXT,blend_minmax, GLES200, GLES300) // #25 _extension( 5,EXT,blend_minmax, GLES200, GLES300) // #25
#endif #endif
#ifdef MAGNUM_TARGET_GLES2
/* Replaced by EXT_disjoint_timer_query_webgl2 in WebGL 2 */
_extension( 6,EXT,disjoint_timer_query, GLES200, None) // #26 _extension( 6,EXT,disjoint_timer_query, GLES200, None) // #26
#endif
#ifdef MAGNUM_TARGET_GLES2 #ifdef MAGNUM_TARGET_GLES2
_extension( 7,EXT,shader_texture_lod, GLES200, GLES300) // #27 _extension( 7,EXT,shader_texture_lod, GLES200, GLES300) // #27
#endif #endif
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
/* Replaces WEBGL_color_buffer_float from WebGL 1 */ /* Replaces WEBGL_color_buffer_float from WebGL 1 */
_extension( 8,EXT,color_buffer_float, GLES300, None) // #31 _extension( 8,EXT,color_buffer_float, GLES300, None) // #31
/* Replaces WEBGL_disjoint_timer_query from WebGL 1 */
_extension( 9,EXT,disjoint_timer_query_webgl2, GLES300, None) // #33
#endif #endif
_extension( 9,EXT,texture_compression_rgtc, GLES200, None) // #38 _extension(10,EXT,texture_compression_rgtc, GLES200, None) // #38
_extension(10,EXT,texture_compression_bptc, GLES200, None) // #39 _extension(11,EXT,texture_compression_bptc, GLES200, None) // #39
} namespace OES { } namespace OES {
#ifdef MAGNUM_TARGET_GLES2 #ifdef MAGNUM_TARGET_GLES2
_extension(15,OES,texture_float, GLES200, GLES300) // #1 _extension(15,OES,texture_float, GLES200, GLES300) // #1

2
src/Magnum/GL/GL.h

@ -44,9 +44,7 @@ namespace Magnum { namespace GL {
FramebufferTarget enums used only directly with framebuffer instance */ FramebufferTarget enums used only directly with framebuffer instance */
class AbstractFramebuffer; class AbstractFramebuffer;
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
class AbstractQuery; class AbstractQuery;
#endif
class AbstractShaderProgram; class AbstractShaderProgram;
class AbstractTexture; class AbstractTexture;

4
src/Magnum/GL/Implementation/QueryState.h

@ -37,10 +37,6 @@
#include "Magnum/GL/AbstractQuery.h" #include "Magnum/GL/AbstractQuery.h"
#endif #endif
#if defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)
#error this header is not available in WebGL 1.0 build
#endif
namespace Magnum { namespace GL { namespace Implementation { namespace Magnum { namespace GL { namespace Implementation {
struct QueryState { struct QueryState {

4
src/Magnum/GL/Implementation/State.cpp

@ -37,9 +37,7 @@
#endif #endif
#include "Magnum/GL/Implementation/FramebufferState.h" #include "Magnum/GL/Implementation/FramebufferState.h"
#include "Magnum/GL/Implementation/MeshState.h" #include "Magnum/GL/Implementation/MeshState.h"
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
#include "Magnum/GL/Implementation/QueryState.h" #include "Magnum/GL/Implementation/QueryState.h"
#endif
#include "Magnum/GL/Implementation/RendererState.h" #include "Magnum/GL/Implementation/RendererState.h"
#include "Magnum/GL/Implementation/ShaderState.h" #include "Magnum/GL/Implementation/ShaderState.h"
#include "Magnum/GL/Implementation/ShaderProgramState.h" #include "Magnum/GL/Implementation/ShaderProgramState.h"
@ -67,9 +65,7 @@ State::State(Context& context, std::ostream* const out) {
#endif #endif
framebuffer.reset(new FramebufferState{context, extensions}); framebuffer.reset(new FramebufferState{context, extensions});
mesh.reset(new MeshState{context, *this->context, extensions}); mesh.reset(new MeshState{context, *this->context, extensions});
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
query.reset(new QueryState{context, extensions}); query.reset(new QueryState{context, extensions});
#endif
renderer.reset(new RendererState{context, extensions}); renderer.reset(new RendererState{context, extensions});
shader.reset(new ShaderState(context, extensions)); shader.reset(new ShaderState(context, extensions));
shaderProgram.reset(new ShaderProgramState{context, extensions}); shaderProgram.reset(new ShaderProgramState{context, extensions});

4
src/Magnum/GL/Implementation/State.h

@ -39,9 +39,7 @@ struct DebugState;
#endif #endif
struct FramebufferState; struct FramebufferState;
struct MeshState; struct MeshState;
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
struct QueryState; struct QueryState;
#endif
struct RendererState; struct RendererState;
struct ShaderState; struct ShaderState;
struct ShaderProgramState; struct ShaderProgramState;
@ -65,9 +63,7 @@ struct State {
#endif #endif
Containers::Pointer<FramebufferState> framebuffer; Containers::Pointer<FramebufferState> framebuffer;
Containers::Pointer<MeshState> mesh; Containers::Pointer<MeshState> mesh;
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
Containers::Pointer<QueryState> query; Containers::Pointer<QueryState> query;
#endif
Containers::Pointer<RendererState> renderer; Containers::Pointer<RendererState> renderer;
Containers::Pointer<ShaderState> shader; Containers::Pointer<ShaderState> shader;
Containers::Pointer<ShaderProgramState> shaderProgram; Containers::Pointer<ShaderProgramState> shaderProgram;

23
src/Magnum/GL/Implementation/driverSpecific.cpp

@ -280,6 +280,19 @@ namespace {
framebuffer size, otherwise it assumes it's zero-sized. */ framebuffer size, otherwise it assumes it's zero-sized. */
"apitrace-zero-initial-viewport", "apitrace-zero-initial-viewport",
#endif #endif
#if defined(MAGNUM_TARGET_WEBGL) && !defined(MAGNUM_TARGET_GLES2)
/* While the EXT_disjoint_timer_query extension should be only on WebGL 1 and
EXT_disjoint_timer_query_webgl2 only on WebGL 2, Firefox reports
EXT_disjoint_timer_query on both. The entry points work correctly however,
so this workaround makes Magnum pretend EXT_disjoint_timer_query_webgl2 is
available when it detects EXT_disjoint_timer_query on WebGL 2 builds on
Firefox. See also https://bugzilla.mozilla.org/show_bug.cgi?id=1328882,
https://www.khronos.org/webgl/public-mailing-list/public_webgl/1705/msg00015.php
and https://github.com/emscripten-core/emscripten/pull/9652 for the
Emscripten-side part of this workaround. */
"firefox-fake-disjoint-timer-query-webgl2",
#endif
/* [workarounds] */ /* [workarounds] */
}; };
} }
@ -470,6 +483,16 @@ void Context::setupDriverWorkarounds() {
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
} }
#endif #endif
#if defined(MAGNUM_TARGET_WEBGL) && !defined(MAGNUM_TARGET_GLES2)
if(rendererString() == "Mozilla") {
for(const auto& extension: extensionStrings()) {
if(extension == "GL_EXT_disjoint_timer_query" && !isDriverWorkaroundDisabled("firefox-fake-disjoint-timer-query-webgl2")) {
_extensionStatus.set(Extensions::EXT::disjoint_timer_query_webgl2::Index, true);
}
}
}
#endif
} }
}} }}

2
src/Magnum/GL/OpenGLTester.cpp

@ -39,7 +39,6 @@ OpenGLTester::OpenGLTester(): TestSuite::Tester{TestSuite::Tester::TesterConfigu
OpenGLTester::~OpenGLTester() = default; OpenGLTester::~OpenGLTester() = default;
#ifndef MAGNUM_TARGET_WEBGL
void OpenGLTester::gpuTimeBenchmarkBegin() { void OpenGLTester::gpuTimeBenchmarkBegin() {
setBenchmarkName("GPU time"); setBenchmarkName("GPU time");
@ -53,6 +52,5 @@ std::uint64_t OpenGLTester::gpuTimeBenchmarkEnd() {
_gpuTimeQuery.end(); _gpuTimeQuery.end();
return _gpuTimeQuery.result<UnsignedLong>(); return _gpuTimeQuery.result<UnsignedLong>();
} }
#endif
}} }}

64
src/Magnum/GL/OpenGLTester.h

@ -35,6 +35,7 @@
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include "Magnum/GL/Renderer.h" #include "Magnum/GL/Renderer.h"
#include "Magnum/GL/TimeQuery.h"
#if defined(MAGNUM_TARGET_HEADLESS) || defined(CORRADE_TARGET_EMSCRIPTEN) || defined(CORRADE_TARGET_ANDROID) #if defined(MAGNUM_TARGET_HEADLESS) || defined(CORRADE_TARGET_EMSCRIPTEN) || defined(CORRADE_TARGET_ANDROID)
#include "Magnum/Platform/WindowlessEglApplication.h" #include "Magnum/Platform/WindowlessEglApplication.h"
@ -58,10 +59,6 @@
#error cannot run OpenGL tests on this platform #error cannot run OpenGL tests on this platform
#endif #endif
#ifndef MAGNUM_TARGET_WEBGL
#include "Magnum/GL/TimeQuery.h"
#endif
namespace Magnum { namespace GL { namespace Magnum { namespace GL {
/** /**
@ -134,7 +131,12 @@ which is supported here as well as in all other application implementations.
This class adds @ref BenchmarkType::GpuTime to the benchmark type enum, This class adds @ref BenchmarkType::GpuTime to the benchmark type enum,
allowing you to measure time spent on GPU as opposed to CPU or wall clock time. allowing you to measure time spent on GPU as opposed to CPU or wall clock time.
@requires_gles GPU time benchmarking is not available on WebGL. If @gl_extension{ARB,timer_query} desktop extension (part of OpenGL 3.3),
@gl_extension{EXT,disjoint_timer_query} OpenGL ES extension,
@webgl_extension{EXT,disjoint_timer_query} WebGL 1 extension or
@gl_extension{EXT,disjoint_timer_query_webgl2} WebGL 2 extension is not
available, GPU time benchmarks will get automatically skipped, producing a
@cb{.ansi} SKIP @ce message on the output.
@note This class is available only if Magnum is compiled with @note This class is available only if Magnum is compiled with
@ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features @ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features
@ -142,7 +144,6 @@ allowing you to measure time spent on GPU as opposed to CPU or wall clock time.
*/ */
class OpenGLTester: public TestSuite::Tester { class OpenGLTester: public TestSuite::Tester {
public: public:
#ifndef MAGNUM_TARGET_WEBGL
/** /**
* @brief Benchmark type * @brief Benchmark type
* *
@ -178,14 +179,19 @@ class OpenGLTester: public TestSuite::Tester {
/** /**
* GPU time, measured using @ref TimeQuery::Target::TimeElapsed. * GPU time, measured using @ref TimeQuery::Target::TimeElapsed.
* Note that the result of the query is retrieved synchronously and * Note that the result of the query is retrieved synchronously and
* thus may cause pipeline bubble. Increase number of iterations * thus may cause a pipeline bubble. Increase number of iterations
* passed to @ref CORRADE_BENCHMARK() to amortize the measurement * passed to @ref CORRADE_BENCHMARK() to amortize the measurement
* error. * error.
* @requires_gles GPU time benchmarking is not available on WebGL. *
* If @gl_extension{ARB,timer_query} desktop extension (part of
* OpenGL 3.3), @gl_extension{EXT,disjoint_timer_query} OpenGL ES
* extension, @webgl_extension{EXT,disjoint_timer_query} WebGL 1
* extension or @gl_extension{EXT,disjoint_timer_query_webgl2}
* WebGL 2 extension is not available, GPU time benchmarks will get
* automatically skipped.
*/ */
GpuTime = 32 GpuTime = 32
}; };
#endif
/** /**
* @brief Constructor * @brief Constructor
@ -196,13 +202,18 @@ class OpenGLTester: public TestSuite::Tester {
~OpenGLTester(); ~OpenGLTester();
#ifndef MAGNUM_TARGET_WEBGL
/** /**
* @brief Add benchmarks * @brief Add benchmarks
* *
* Extends @ref Corrade::TestSuite::Tester::addBenchmarks(std::initializer_list<void(Derived::*)()>, std::size_t, BenchmarkType) with support * Extends @ref Corrade::TestSuite::Tester::addBenchmarks(std::initializer_list<void(Derived::*)()>, std::size_t, BenchmarkType) with support
* for GPU benchmark types. * for GPU benchmark types.
* @requires_gles GPU time benchmarking is not available on WebGL. *
* If @gl_extension{ARB,timer_query} desktop extension (part of OpenGL
* 3.3), @gl_extension{EXT,disjoint_timer_query} OpenGL ES extension,
* @webgl_extension{EXT,disjoint_timer_query} WebGL 1 extension or
* @gl_extension{EXT,disjoint_timer_query_webgl2} WebGL 2 extension is
* not available, @ref BenchmarkType::GpuTime benchmarks will get
* automatically skipped.
*/ */
template<class Derived> void addBenchmarks(std::initializer_list<void(Derived::*)()> benchmarks, std::size_t batchCount, BenchmarkType benchmarkType = BenchmarkType::Default) { template<class Derived> void addBenchmarks(std::initializer_list<void(Derived::*)()> benchmarks, std::size_t batchCount, BenchmarkType benchmarkType = BenchmarkType::Default) {
if(benchmarkType == BenchmarkType::GpuTime) if(benchmarkType == BenchmarkType::GpuTime)
@ -216,7 +227,13 @@ class OpenGLTester: public TestSuite::Tester {
* *
* Extends @ref Corrade::TestSuite::Tester::addBenchmarks(std::initializer_list<void(Derived::*)()>, std::size_t, void(Derived::*)(), void(Derived::*)(), BenchmarkType) with support * Extends @ref Corrade::TestSuite::Tester::addBenchmarks(std::initializer_list<void(Derived::*)()>, std::size_t, void(Derived::*)(), void(Derived::*)(), BenchmarkType) with support
* for GPU benchmark types. * for GPU benchmark types.
* @requires_gles GPU time benchmarking is not available on WebGL. *
* If @gl_extension{ARB,timer_query} desktop extension (part of OpenGL
* 3.3), @gl_extension{EXT,disjoint_timer_query} OpenGL ES extension,
* @webgl_extension{EXT,disjoint_timer_query} WebGL 1 extension or
* @gl_extension{EXT,disjoint_timer_query_webgl2} WebGL 2 extension is
* not available, @ref BenchmarkType::GpuTime benchmarks will get
* automatically skipped.
*/ */
template<class Derived> void addBenchmarks(std::initializer_list<void(Derived::*)()> benchmarks, std::size_t batchCount, void(Derived::*setup)(), void(Derived::*teardown)(), BenchmarkType benchmarkType = BenchmarkType::Default) { template<class Derived> void addBenchmarks(std::initializer_list<void(Derived::*)()> benchmarks, std::size_t batchCount, void(Derived::*setup)(), void(Derived::*teardown)(), BenchmarkType benchmarkType = BenchmarkType::Default) {
if(benchmarkType == BenchmarkType::GpuTime) if(benchmarkType == BenchmarkType::GpuTime)
@ -230,7 +247,13 @@ class OpenGLTester: public TestSuite::Tester {
* *
* Extends @ref Corrade::TestSuite::Tester::addInstancedBenchmarks(std::initializer_list<void(Derived::*)()>, std::size_t, std::size_t, BenchmarkType) with support for GPU * Extends @ref Corrade::TestSuite::Tester::addInstancedBenchmarks(std::initializer_list<void(Derived::*)()>, std::size_t, std::size_t, BenchmarkType) with support for GPU
* benchmark types. * benchmark types.
* @requires_gles GPU time benchmarking is not available on WebGL. *
* If @gl_extension{ARB,timer_query} desktop extension (part of OpenGL
* 3.3), @gl_extension{EXT,disjoint_timer_query} OpenGL ES extension,
* @webgl_extension{EXT,disjoint_timer_query} WebGL 1 extension or
* @gl_extension{EXT,disjoint_timer_query_webgl2} WebGL 2 extension is
* not available, @ref BenchmarkType::GpuTime benchmarks will get
* automatically skipped.
*/ */
template<class Derived> void addInstancedBenchmarks(std::initializer_list<void(Derived::*)()> benchmarks, std::size_t batchCount, std::size_t instanceCount, BenchmarkType benchmarkType = BenchmarkType::Default) { template<class Derived> void addInstancedBenchmarks(std::initializer_list<void(Derived::*)()> benchmarks, std::size_t batchCount, std::size_t instanceCount, BenchmarkType benchmarkType = BenchmarkType::Default) {
if(benchmarkType == BenchmarkType::GpuTime) if(benchmarkType == BenchmarkType::GpuTime)
@ -244,30 +267,31 @@ class OpenGLTester: public TestSuite::Tester {
* *
* Extends @ref Corrade::TestSuite::Tester::addInstancedBenchmarks(std::initializer_list<void(Derived::*)()>, std::size_t, std::size_t, void(Derived::*)(), void(Derived::*)(), BenchmarkType) * Extends @ref Corrade::TestSuite::Tester::addInstancedBenchmarks(std::initializer_list<void(Derived::*)()>, std::size_t, std::size_t, void(Derived::*)(), void(Derived::*)(), BenchmarkType)
* with support for GPU benchmark types. * with support for GPU benchmark types.
* @requires_gles GPU time benchmarking is not available on WebGL. *
* If @gl_extension{ARB,timer_query} desktop extension (part of OpenGL
* 3.3), @gl_extension{EXT,disjoint_timer_query} OpenGL ES extension,
* @webgl_extension{EXT,disjoint_timer_query} WebGL 1 extension or
* @gl_extension{EXT,disjoint_timer_query_webgl2} WebGL 2 extension is
* not available, @ref BenchmarkType::GpuTime benchmarks will get
* automatically skipped.
*/ */
template<class Derived> void addInstancedBenchmarks(std::initializer_list<void(Derived::*)()> benchmarks, std::size_t batchCount, std::size_t instanceCount, void(Derived::*setup)(), void(Derived::*teardown)(), BenchmarkType benchmarkType = BenchmarkType::Default) { template<class Derived> void addInstancedBenchmarks(std::initializer_list<void(Derived::*)()> benchmarks, std::size_t batchCount, std::size_t instanceCount, void(Derived::*setup)(), void(Derived::*teardown)(), BenchmarkType benchmarkType = BenchmarkType::Default) {
if(benchmarkType == BenchmarkType::GpuTime) if(benchmarkType == BenchmarkType::GpuTime)
addCustomInstancedBenchmarks<Derived>(benchmarks, batchCount, instanceCount, &OpenGLTester::gpuTimeBenchmarkBegin, &OpenGLTester::gpuTimeBenchmarkEnd, setup, teardown, BenchmarkUnits::Nanoseconds); addCustomInstancedBenchmarks<Derived>(benchmarks, batchCount, instanceCount, setup, teardown, &OpenGLTester::gpuTimeBenchmarkBegin, &OpenGLTester::gpuTimeBenchmarkEnd, BenchmarkUnits::Nanoseconds);
else else
Tester::addInstancedBenchmarks(benchmarks, batchCount, instanceCount, setup, teardown, Tester::BenchmarkType(Int(benchmarkType))); Tester::addInstancedBenchmarks(benchmarks, batchCount, instanceCount, setup, teardown, Tester::BenchmarkType(Int(benchmarkType)));
} }
#endif
private: private:
#ifndef MAGNUM_TARGET_WEBGL
void gpuTimeBenchmarkBegin(); void gpuTimeBenchmarkBegin();
std::uint64_t gpuTimeBenchmarkEnd(); std::uint64_t gpuTimeBenchmarkEnd();
#endif
struct WindowlessApplication: Platform::WindowlessApplication { struct WindowlessApplication: Platform::WindowlessApplication {
explicit WindowlessApplication(const Arguments& arguments): Platform::WindowlessApplication{arguments} {} explicit WindowlessApplication(const Arguments& arguments): Platform::WindowlessApplication{arguments} {}
int exec() override final { return 0; } int exec() override final { return 0; }
} _windowlessApplication; } _windowlessApplication;
#ifndef MAGNUM_TARGET_WEBGL
TimeQuery _gpuTimeQuery{NoCreate}; TimeQuery _gpuTimeQuery{NoCreate};
#endif
}; };
/** @hideinitializer /** @hideinitializer

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

@ -37,6 +37,7 @@ corrade_add_test(GLRenderbufferTest RenderbufferTest.cpp LIBRARIES MagnumGL)
corrade_add_test(GLSamplerTest SamplerTest.cpp LIBRARIES MagnumGLTestLib) corrade_add_test(GLSamplerTest SamplerTest.cpp LIBRARIES MagnumGLTestLib)
corrade_add_test(GLShaderTest ShaderTest.cpp LIBRARIES MagnumGL) corrade_add_test(GLShaderTest ShaderTest.cpp LIBRARIES MagnumGL)
corrade_add_test(GLTextureTest TextureTest.cpp LIBRARIES MagnumGL) corrade_add_test(GLTextureTest TextureTest.cpp LIBRARIES MagnumGL)
corrade_add_test(GLTimeQueryTest TimeQueryTest.cpp LIBRARIES MagnumGL)
corrade_add_test(GLVersionTest VersionTest.cpp LIBRARIES MagnumGL) corrade_add_test(GLVersionTest VersionTest.cpp LIBRARIES MagnumGL)
set_target_properties( set_target_properties(
@ -54,6 +55,7 @@ set_target_properties(
GLSamplerTest GLSamplerTest
GLShaderTest GLShaderTest
GLTextureTest GLTextureTest
GLTimeQueryTest
GLVersionTest GLVersionTest
PROPERTIES FOLDER "Magnum/GL/Test") PROPERTIES FOLDER "Magnum/GL/Test")
@ -93,11 +95,6 @@ if(NOT (MAGNUM_TARGET_WEBGL AND MAGNUM_TARGET_GLES2))
set_target_properties(GLSampleQueryTest PROPERTIES FOLDER "Magnum/GL/Test") set_target_properties(GLSampleQueryTest PROPERTIES FOLDER "Magnum/GL/Test")
endif() endif()
if(NOT MAGNUM_TARGET_WEBGL)
corrade_add_test(GLTimeQueryTest TimeQueryTest.cpp LIBRARIES MagnumGL)
set_target_properties(GLTimeQueryTest PROPERTIES FOLDER "Magnum/GL/Test")
endif()
if(NOT MAGNUM_TARGET_GLES) if(NOT MAGNUM_TARGET_GLES)
corrade_add_test(GLRectangleTextureTest RectangleTextureTest.cpp LIBRARIES MagnumGL) corrade_add_test(GLRectangleTextureTest RectangleTextureTest.cpp LIBRARIES MagnumGL)
set_target_properties(GLRectangleTextureTest PROPERTIES FOLDER "Magnum/GL/Test") set_target_properties(GLRectangleTextureTest PROPERTIES FOLDER "Magnum/GL/Test")
@ -112,6 +109,7 @@ if(BUILD_GL_TESTS)
corrade_add_test(GLRendererGLTest RendererGLTest.cpp LIBRARIES MagnumOpenGLTester) corrade_add_test(GLRendererGLTest RendererGLTest.cpp LIBRARIES MagnumOpenGLTester)
corrade_add_test(GLRenderbufferGLTest RenderbufferGLTest.cpp LIBRARIES MagnumOpenGLTester) corrade_add_test(GLRenderbufferGLTest RenderbufferGLTest.cpp LIBRARIES MagnumOpenGLTester)
corrade_add_test(GLTextureGLTest TextureGLTest.cpp LIBRARIES MagnumOpenGLTesterTestLib) corrade_add_test(GLTextureGLTest TextureGLTest.cpp LIBRARIES MagnumOpenGLTesterTestLib)
corrade_add_test(GLTimeQueryGLTest TimeQueryGLTest.cpp LIBRARIES MagnumOpenGLTester)
corrade_add_resource(GLAbstractShaderProgramGLTest_RES AbstractShaderProgramGLTestFiles/resources.conf) corrade_add_resource(GLAbstractShaderProgramGLTest_RES AbstractShaderProgramGLTestFiles/resources.conf)
corrade_add_test(GLAbstractShaderProgramGLTest corrade_add_test(GLAbstractShaderProgramGLTest
@ -147,6 +145,7 @@ if(BUILD_GL_TESTS)
GLMeshGLTest GLMeshGLTest
GLRenderbufferGLTest GLRenderbufferGLTest
GLTextureGLTest GLTextureGLTest
GLTimeQueryGLTest
GLAbstractShaderProgramGLTest GLAbstractShaderProgramGLTest
GLAbstractShaderProgramGLTest_RES-dependencies GLAbstractShaderProgramGLTest_RES-dependencies
@ -157,12 +156,10 @@ if(BUILD_GL_TESTS)
if(NOT MAGNUM_TARGET_WEBGL) if(NOT MAGNUM_TARGET_WEBGL)
corrade_add_test(GLAbstractObjectGLTest AbstractObjectGLTest.cpp LIBRARIES MagnumOpenGLTester) corrade_add_test(GLAbstractObjectGLTest AbstractObjectGLTest.cpp LIBRARIES MagnumOpenGLTester)
corrade_add_test(GLDebugOutputGLTest DebugOutputGLTest.cpp LIBRARIES MagnumOpenGLTester) corrade_add_test(GLDebugOutputGLTest DebugOutputGLTest.cpp LIBRARIES MagnumOpenGLTester)
corrade_add_test(GLTimeQueryGLTest TimeQueryGLTest.cpp LIBRARIES MagnumOpenGLTester)
set_target_properties( set_target_properties(
GLAbstractObjectGLTest GLAbstractObjectGLTest
GLDebugOutputGLTest GLDebugOutputGLTest
GLTimeQueryGLTest
PROPERTIES FOLDER "Magnum/GL/Test") PROPERTIES FOLDER "Magnum/GL/Test")
endif() endif()

14
src/Magnum/GL/Test/TimeQueryGLTest.cpp

@ -52,6 +52,9 @@ void TimeQueryGLTest::wrap() {
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
if(!Context::current().isExtensionSupported<Extensions::ARB::timer_query>()) if(!Context::current().isExtensionSupported<Extensions::ARB::timer_query>())
CORRADE_SKIP(Extensions::ARB::timer_query::string() + std::string(" is not available")); CORRADE_SKIP(Extensions::ARB::timer_query::string() + std::string(" is not available"));
#elif defined(MAGNUM_TARGET_WEBGL) && !defined(MAGNUM_TARGET_GLES2)
if(!Context::current().isExtensionSupported<Extensions::EXT::disjoint_timer_query_webgl2>())
CORRADE_SKIP(Extensions::EXT::disjoint_timer_query_webgl2::string() + std::string(" is not available"));
#else #else
if(!Context::current().isExtensionSupported<Extensions::EXT::disjoint_timer_query>()) if(!Context::current().isExtensionSupported<Extensions::EXT::disjoint_timer_query>())
CORRADE_SKIP(Extensions::EXT::disjoint_timer_query::string() + std::string(" is not available")); CORRADE_SKIP(Extensions::EXT::disjoint_timer_query::string() + std::string(" is not available"));
@ -83,6 +86,9 @@ void TimeQueryGLTest::queryTime() {
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
if(!Context::current().isExtensionSupported<Extensions::ARB::timer_query>()) if(!Context::current().isExtensionSupported<Extensions::ARB::timer_query>())
CORRADE_SKIP(Extensions::ARB::timer_query::string() + std::string(" is not available")); CORRADE_SKIP(Extensions::ARB::timer_query::string() + std::string(" is not available"));
#elif defined(MAGNUM_TARGET_WEBGL) && !defined(MAGNUM_TARGET_GLES2)
if(!Context::current().isExtensionSupported<Extensions::EXT::disjoint_timer_query_webgl2>())
CORRADE_SKIP(Extensions::EXT::disjoint_timer_query_webgl2::string() + std::string(" is not available"));
#else #else
if(!Context::current().isExtensionSupported<Extensions::EXT::disjoint_timer_query>()) if(!Context::current().isExtensionSupported<Extensions::EXT::disjoint_timer_query>())
CORRADE_SKIP(Extensions::EXT::disjoint_timer_query::string() + std::string(" is not available")); CORRADE_SKIP(Extensions::EXT::disjoint_timer_query::string() + std::string(" is not available"));
@ -107,7 +113,13 @@ void TimeQueryGLTest::queryTime() {
} }
void TimeQueryGLTest::queryTimestamp() { void TimeQueryGLTest::queryTimestamp() {
#ifdef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
if(!Context::current().isExtensionSupported<Extensions::ARB::timer_query>())
CORRADE_SKIP(Extensions::ARB::timer_query::string() + std::string(" is not available"));
#elif defined(MAGNUM_TARGET_WEBGL) && !defined(MAGNUM_TARGET_GLES2)
if(!Context::current().isExtensionSupported<Extensions::EXT::disjoint_timer_query_webgl2>())
CORRADE_SKIP(Extensions::EXT::disjoint_timer_query_webgl2::string() + std::string(" is not available"));
#else
if(!Context::current().isExtensionSupported<Extensions::EXT::disjoint_timer_query>()) if(!Context::current().isExtensionSupported<Extensions::EXT::disjoint_timer_query>())
CORRADE_SKIP(Extensions::EXT::disjoint_timer_query::string() + std::string(" is not available")); CORRADE_SKIP(Extensions::EXT::disjoint_timer_query::string() + std::string(" is not available"));
#endif #endif

13
src/Magnum/GL/TimeQuery.h

@ -25,15 +25,12 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#ifndef MAGNUM_TARGET_WEBGL
/** @file /** @file
* @brief Class @ref Magnum::GL::TimeQuery * @brief Class @ref Magnum::GL::TimeQuery
*/ */
#endif
#include "Magnum/GL/AbstractQuery.h" #include "Magnum/GL/AbstractQuery.h"
#ifndef MAGNUM_TARGET_WEBGL
namespace Magnum { namespace GL { namespace Magnum { namespace GL {
/** /**
@ -52,7 +49,8 @@ times are reported in nanoseconds.
@requires_gl33 Extension @gl_extension{ARB,timer_query} @requires_gl33 Extension @gl_extension{ARB,timer_query}
@requires_es_extension Extension @gl_extension{EXT,disjoint_timer_query} @requires_es_extension Extension @gl_extension{EXT,disjoint_timer_query}
@requires_gles Time query is not available in WebGL. @requires_webgl_extension Extension @webgl_extension{EXT,disjoint_timer_query}
on WebGL 1, @gl_extension{EXT,disjoint_timer_query_webgl2} on WebGL 2
@see @ref PrimitiveQuery, @ref SampleQuery @see @ref PrimitiveQuery, @ref SampleQuery
@todo timestamp with glGet + example usage @todo timestamp with glGet + example usage
@ -163,10 +161,8 @@ class TimeQuery: public AbstractQuery {
void timestamp() { void timestamp() {
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
glQueryCounter(id(), GL_TIMESTAMP); glQueryCounter(id(), GL_TIMESTAMP);
#elif !defined(CORRADE_TARGET_EMSCRIPTEN)
glQueryCounterEXT(id(), GL_TIMESTAMP_EXT);
#else #else
CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ glQueryCounterEXT(id(), GL_TIMESTAMP_EXT);
#endif #endif
} }
@ -175,8 +171,5 @@ class TimeQuery: public AbstractQuery {
}; };
}} }}
#else
#error this header is not available in WebGL build
#endif
#endif #endif

1
src/MagnumExternal/OpenGL/GLES3/Emscripten/extensions.txt vendored

@ -5,6 +5,7 @@
version 3.0 es version 3.0 es
extension EXT_texture_filter_anisotropic optional extension EXT_texture_filter_anisotropic optional
# It's actually EXT_disjoint_timer_query_webgl2, but that's not known to gl.xml
extension EXT_disjoint_timer_query optional extension EXT_disjoint_timer_query optional
extension EXT_color_buffer_float optional extension EXT_color_buffer_float optional
extension EXT_texture_compression_rgtc optional extension EXT_texture_compression_rgtc optional

Loading…
Cancel
Save