From 3df692f29ad447581ed0b20e1bde57515c259900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 3 Aug 2018 01:17:00 +0200 Subject: [PATCH] Animation: added Player::addRawCallback(). Provides some further optimization opportunities. --- doc/snippets/MagnumAnimation.cpp | 24 +++++++++ src/Magnum/Animation/Player.h | 63 ++++++++++++++++++++++-- src/Magnum/Animation/Test/Benchmark.cpp | 34 ++++++++++++- src/Magnum/Animation/Test/PlayerTest.cpp | 32 +++++++++++- 4 files changed, 146 insertions(+), 7 deletions(-) diff --git a/doc/snippets/MagnumAnimation.cpp b/doc/snippets/MagnumAnimation.cpp index b778a22df..7169d1687 100644 --- a/doc/snippets/MagnumAnimation.cpp +++ b/doc/snippets/MagnumAnimation.cpp @@ -161,6 +161,30 @@ timer.advance(timeline.previousFrameTime()); /* [Player-higher-order-animated-time] */ } +{ +/* [Player-addRawCallback] */ +Animation::Track track; + +Int result; +std::vector data; +auto callback = [](std::vector& data, Int value) { + data.push_back(value); +}; + +Animation::Player player; +player.addRawCallback(track, + [](const Animation::TrackViewStorage& track, Float key, + std::size_t& hint, void* destination, void(*callback)(), void* userData) { + Int value = static_cast&>(track) + .atStrict(key, hint); + if(value == *static_cast(destination)) return; + *static_cast(destination) = value; + reinterpret_cast&, Int)>(callback) + (*static_cast*>(userData), value); + }, &result, reinterpret_cast(+callback), &data); +/* [Player-addRawCallback] */ +} + { /* [Track-usage] */ const Animation::Track jump{{ diff --git a/src/Magnum/Animation/Player.h b/src/Magnum/Animation/Player.h index 64165c5b6..3462eac04 100644 --- a/src/Magnum/Animation/Player.h +++ b/src/Magnum/Animation/Player.h @@ -108,7 +108,9 @@ implementation: The @ref addWithCallbackOnChange() variant will fire the callback only if the interpolated value changes, which is useful for triggering other events. See -@ref Animation-Player-higher-order "below" for an example. +@ref Animation-Player-higher-order "below" for an example. Lastly, there is +@ref addRawCallback() that allows for greater control and further performance +optimizations. See its documentation for a usage example code snippet. By default, the @ref duration() of an animation is calculated implicitly from all added tracks. You can use @ref setDuration() to specify a custom duration @@ -323,7 +325,7 @@ template Player& addWithCallback(const TrackView& track, void(*callback)(const K&, const R&, void*), void* userData = nullptr); @@ -404,7 +407,8 @@ template Player& addWithCallback(const TrackView& track, void(*callback)(const K&, const R&, U&), U& userData); @@ -439,6 +443,7 @@ template Player& addWithCallbackOnChange(const TrackView& track, void(*callback)(const K&, const R&, void*), R& destination, void* userData = nullptr); @@ -466,7 +471,8 @@ template Player& addWithCallbackOnChange(const TrackView& track, void(*callback)(const K&, const R&, void*), R& destination, U& userData); @@ -488,6 +494,48 @@ template Player& addRawCallback(const TrackView& track, void(*callback)(const TrackViewStorage&, K, std::size_t&, void*, void(*)(), void*), void* destination, void(*userCallback)(), void* userData); + #else + template Player& addRawCallback(const TrackView& track, Callback callback, void* destination, void(*userCallback)(), void* userData); + #endif + + /** @overload + * + * Note that the track ownership is *not* transferred to the + * @ref Player and you have to ensure that it's kept in scope for the + * whole lifetime of the @ref Player instance. + */ + #ifdef DOXYGEN_GENERATING_OUTPUT + template Player& addRawCallback(const Track& track, void(*callback)(const TrackViewStorage&, K, std::size_t&, void*, void(*)(), void*), void* destination, void(*userCallback)(), void* userData); + #else + template Player& addRawCallback(const Track& track, Callback callback, void* destination, void(*userCallback)(), void* userData) { + return addRawCallback(TrackView{track}, callback, destination, userCallback, userData); + } + #endif + /** * @brief State * @@ -641,6 +689,11 @@ template template P *static_cast(destination) = result; }, &destination, reinterpret_cast(callbackPtr), &userData); } + +template template Player& Player::addRawCallback(const TrackView& track, Callback callback, void* destination, void(*userCallback)(), void* userData) { + auto callbackPtr = static_cast&, K, std::size_t&, void*, void(*)(), void*)>(callback); + return addInternal(track, callbackPtr, destination, userCallback, userData); +} #endif #if defined(CORRADE_TARGET_WINDOWS) && !defined(__MINGW32__) diff --git a/src/Magnum/Animation/Test/Benchmark.cpp b/src/Magnum/Animation/Test/Benchmark.cpp index 6ef9180ed..07dde5ab5 100644 --- a/src/Magnum/Animation/Test/Benchmark.cpp +++ b/src/Magnum/Animation/Test/Benchmark.cpp @@ -47,6 +47,8 @@ struct Benchmark: TestSuite::Tester { void playerAdvanceEmptyTrack(); void playerAdvance(); void playerAdvanceCallback(); + void playerAdvanceRawCallback(); + void playerAdvanceRawCallbackDirectInterpolator(); Containers::Array _keys; Containers::Array _values; @@ -76,7 +78,9 @@ Benchmark::Benchmark() { &Benchmark::playerAdvanceEmpty, &Benchmark::playerAdvanceEmptyTrack, &Benchmark::playerAdvance, - &Benchmark::playerAdvanceCallback}, 10); + &Benchmark::playerAdvanceCallback, + &Benchmark::playerAdvanceRawCallback, + &Benchmark::playerAdvanceRawCallbackDirectInterpolator}, 10); _keys = Containers::Array{DataSize}; _values = Containers::Array{Containers::DirectInit, DataSize, 1}; @@ -228,6 +232,34 @@ void Benchmark::playerAdvanceCallback() { CORRADE_COMPARE(result, 125000); } +void Benchmark::playerAdvanceRawCallback() { + Int result{}; + Player player; + player.addRawCallback(_track, [](const TrackViewStorage& track, Float key, std::size_t& hint, void* destination, void(*)(), void*) { + *static_cast(destination) += static_cast&>(track).atStrict(key, hint); + }, &result, nullptr, nullptr) + .play({}); + CORRADE_BENCHMARK(250) { + for(Float i = 0.0f; i < 500.0f; i += 1.0f) + player.advance(i); + } + CORRADE_COMPARE(result, 125000); +} + +void Benchmark::playerAdvanceRawCallbackDirectInterpolator() { + Int result{}; + Player player; + player.addRawCallback(_track, [](const TrackViewStorage& track, Float key, std::size_t& hint, void* destination, void(*)(), void*) { + *static_cast(destination) += static_cast&>(track).atStrict(Math::select, key, hint); + }, &result, nullptr, nullptr) + .play({}); + CORRADE_BENCHMARK(250) { + for(Float i = 0.0f; i < 500.0f; i += 1.0f) + player.advance(i); + } + CORRADE_COMPARE(result, 125000); +} + }}} CORRADE_TEST_MAIN(Magnum::Animation::Test::Benchmark) diff --git a/src/Magnum/Animation/Test/PlayerTest.cpp b/src/Magnum/Animation/Test/PlayerTest.cpp index e90b16f5f..e61bf49d5 100644 --- a/src/Magnum/Animation/Test/PlayerTest.cpp +++ b/src/Magnum/Animation/Test/PlayerTest.cpp @@ -62,6 +62,7 @@ struct PlayerTest: TestSuite::Tester { void addWithCallbackTemplate(); void addWithCallbackOnChange(); void addWithCallbackOnChangeTemplate(); + void addRawCallback(); void runFor100YearsFloat(); void runFor100YearsChrono(); @@ -114,7 +115,8 @@ PlayerTest::PlayerTest() { &PlayerTest::addWithCallback, &PlayerTest::addWithCallbackTemplate, &PlayerTest::addWithCallbackOnChange, - &PlayerTest::addWithCallbackOnChangeTemplate}); + &PlayerTest::addWithCallbackOnChangeTemplate, + &PlayerTest::addRawCallback}); addInstancedTests({ &PlayerTest::runFor100YearsFloat, @@ -704,6 +706,34 @@ void PlayerTest::addWithCallbackOnChangeTemplate() { CORRADE_COMPARE(data.called, 2); } +void PlayerTest::addRawCallback() { + Animation::Track track; + + Int result = -1; + std::vector data; + auto callback = [](std::vector& data, Int value) { + data.push_back(value); + }; + + Animation::Player player; + player.addRawCallback(track, + [](const Animation::TrackViewStorage& track, Float key, + std::size_t& hint, void* destination, void(*callback)(), void* userData) { + Int value = static_cast&>(track).at(key, hint); + if(value == *static_cast(destination)) return; + *static_cast(destination) = value; + reinterpret_cast&, Int)>(callback)(*static_cast*>(userData), value); + }, &result, reinterpret_cast(+callback), &data) + .play({}); + + /* Should add the default-constructed value into the vector, but only once */ + CORRADE_COMPARE(data, std::vector{}); + player.advance({}); + CORRADE_COMPARE(data, std::vector{0}); + player.advance(1.0f); + CORRADE_COMPARE(data, std::vector{0}); +} + void PlayerTest::runFor100YearsFloat() { auto&& data = RunFor100YearsData[testCaseInstanceId()]; setTestCaseDescription(data.name);