Browse Source

Animation: added Player::addRawCallback().

Provides some further optimization opportunities.
pull/191/head
Vladimír Vondruš 8 years ago
parent
commit
3df692f29a
  1. 24
      doc/snippets/MagnumAnimation.cpp
  2. 63
      src/Magnum/Animation/Player.h
  3. 34
      src/Magnum/Animation/Test/Benchmark.cpp
  4. 32
      src/Magnum/Animation/Test/PlayerTest.cpp

24
doc/snippets/MagnumAnimation.cpp

@ -161,6 +161,30 @@ timer.advance(timeline.previousFrameTime());
/* [Player-higher-order-animated-time] */ /* [Player-higher-order-animated-time] */
} }
{
/* [Player-addRawCallback] */
Animation::Track<Float, Int> track;
Int result;
std::vector<Int> data;
auto callback = [](std::vector<Int>& data, Int value) {
data.push_back(value);
};
Animation::Player<Float> player;
player.addRawCallback(track,
[](const Animation::TrackViewStorage<Float>& track, Float key,
std::size_t& hint, void* destination, void(*callback)(), void* userData) {
Int value = static_cast<const Animation::TrackView<Float, Int>&>(track)
.atStrict(key, hint);
if(value == *static_cast<Int*>(destination)) return;
*static_cast<Int*>(destination) = value;
reinterpret_cast<void(*)(std::vector<Int>&, Int)>(callback)
(*static_cast<std::vector<Int>*>(userData), value);
}, &result, reinterpret_cast<void(*)()>(+callback), &data);
/* [Player-addRawCallback] */
}
{ {
/* [Track-usage] */ /* [Track-usage] */
const Animation::Track<Float, Vector2> jump{{ const Animation::Track<Float, Vector2> jump{{

63
src/Magnum/Animation/Player.h

@ -108,7 +108,9 @@ implementation:
The @ref addWithCallbackOnChange() variant will fire the callback only if the The @ref addWithCallbackOnChange() variant will fire the callback only if the
interpolated value changes, which is useful for triggering other events. See 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 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 all added tracks. You can use @ref setDuration() to specify a custom duration
@ -323,7 +325,7 @@ template<class T, class K
* @brief Whether the player is empty * @brief Whether the player is empty
* *
* @see @ref size(), @ref add(), @ref addWithCallback(), * @see @ref size(), @ref add(), @ref addWithCallback(),
* @ref addWithCallbackOnChange() * @ref addWithCallbackOnChange(), @ref addRawCallback()
*/ */
bool isEmpty() const; bool isEmpty() const;
@ -331,7 +333,7 @@ template<class T, class K
* @brief Count of tracks managed by this player * @brief Count of tracks managed by this player
* *
* @see @ref isEmpty(), @ref add(), @ref addWithCallback(), * @see @ref isEmpty(), @ref add(), @ref addWithCallback(),
* @ref addWithCallbackOnChange() * @ref addWithCallbackOnChange(), @ref addRawCallback()
*/ */
std::size_t size() const; std::size_t size() const;
@ -372,6 +374,7 @@ template<class T, class K
* *
* See the overload below for a more convenient type-safe way to pass * See the overload below for a more convenient type-safe way to pass
* user data. * user data.
* @see @ref addRawCallback()
*/ */
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class V, class R> Player<T, K>& addWithCallback(const TrackView<K, V, R>& track, void(*callback)(const K&, const R&, void*), void* userData = nullptr); template<class V, class R> Player<T, K>& addWithCallback(const TrackView<K, V, R>& track, void(*callback)(const K&, const R&, void*), void* userData = nullptr);
@ -404,7 +407,8 @@ template<class T, class K
* Equivalent to calling the above with a lambda wrapper that casts * Equivalent to calling the above with a lambda wrapper that casts
* @cpp void* @ce back to @cpp T* @ce and dereferences it in order to * @cpp void* @ce back to @cpp T* @ce and dereferences it in order to
* pass it to @p callback. There is no additional overhead compared to * pass it to @p callback. There is no additional overhead compared to
* the overload taking the @cpp void* @ce pointer. * the overload taking the @cpp void* @ce pointer, however see
* @ref addRawCallback() for optimization possibilities.
*/ */
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class V, class R, class U> Player<T, K>& addWithCallback(const TrackView<K, V, R>& track, void(*callback)(const K&, const R&, U&), U& userData); template<class V, class R, class U> Player<T, K>& addWithCallback(const TrackView<K, V, R>& track, void(*callback)(const K&, const R&, U&), U& userData);
@ -439,6 +443,7 @@ template<class T, class K
* *
* See the overload below for a more convenient type-safe way to pass * See the overload below for a more convenient type-safe way to pass
* user data. * user data.
* @see @ref addRawCallback()
*/ */
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class V, class R> Player<T, K>& addWithCallbackOnChange(const TrackView<K, V, R>& track, void(*callback)(const K&, const R&, void*), R& destination, void* userData = nullptr); template<class V, class R> Player<T, K>& addWithCallbackOnChange(const TrackView<K, V, R>& track, void(*callback)(const K&, const R&, void*), R& destination, void* userData = nullptr);
@ -466,7 +471,8 @@ template<class T, class K
* Equivalent to calling the above with a lambda wrapper that casts * Equivalent to calling the above with a lambda wrapper that casts
* @cpp void* @ce back to @cpp T* @ce and dereferences it in order to * @cpp void* @ce back to @cpp T* @ce and dereferences it in order to
* pass it to @p callback. There is no additional overhead compared to * pass it to @p callback. There is no additional overhead compared to
* the overload taking the @cpp void* @ce pointer. * the overload taking the @cpp void* @ce pointer, however see
* @ref addRawCallback() for optimization possibilities.
*/ */
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
template<class V, class R, class U> Player<T, K>& addWithCallbackOnChange(const TrackView<K, V, R>& track, void(*callback)(const K&, const R&, void*), R& destination, U& userData); template<class V, class R, class U> Player<T, K>& addWithCallbackOnChange(const TrackView<K, V, R>& track, void(*callback)(const K&, const R&, void*), R& destination, U& userData);
@ -488,6 +494,48 @@ template<class T, class K
} }
#endif #endif
/**
* @brief Add a track with a raw callback
*
* This is a low-level function meant to be used if you want to avoid
* the extra overhead of an additional callback in @ref addWithCallback()
* or @ref addWithCallbackOnChange(), want more flexibility in the user
* callback or want to control the track interpolation directly --- for
* example taking advantage of @ref TrackView::atStrict() or passing an
* inlineable interpolator function instead of using the saved
* interpolator function pointer.
*
* The callback takes the raw @ref TrackViewStorage reference (which
* you need to cast to a correct type), the interpolated key and hint
* that's meant to be passed to @ref TrackView::at(), the destination
* pointer (equivalent to the one passed to @ref add()), user callback
* pointer (which again needs to be cast to a correct type) and user
* data pointer. The following code snippet shows implementation of the
* @ref addWithCallbackOnChange() API using this function, using a
* custom callback to add a value to a vector if it changes:
*
* @snippet MagnumAnimation.cpp Player-addRawCallback
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class V, class R, class Callback> Player<T, K>& addRawCallback(const TrackView<K, V, R>& track, void(*callback)(const TrackViewStorage<K>&, K, std::size_t&, void*, void(*)(), void*), void* destination, void(*userCallback)(), void* userData);
#else
template<class V, class R, class Callback> Player<T, K>& addRawCallback(const TrackView<K, V, R>& 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<class V, class R> Player<T, K>& addRawCallback(const Track<K, V, R>& track, void(*callback)(const TrackViewStorage<K>&, K, std::size_t&, void*, void(*)(), void*), void* destination, void(*userCallback)(), void* userData);
#else
template<class V, class R, class Callback> Player<T, K>& addRawCallback(const Track<K, V, R>& track, Callback callback, void* destination, void(*userCallback)(), void* userData) {
return addRawCallback(TrackView<K, V, R>{track}, callback, destination, userCallback, userData);
}
#endif
/** /**
* @brief State * @brief State
* *
@ -641,6 +689,11 @@ template<class T, class K> template<class V, class R, class U, class Callback> P
*static_cast<R*>(destination) = result; *static_cast<R*>(destination) = result;
}, &destination, reinterpret_cast<void(*)()>(callbackPtr), &userData); }, &destination, reinterpret_cast<void(*)()>(callbackPtr), &userData);
} }
template<class T, class K> template<class V, class R, class Callback> Player<T, K>& Player<T, K>::addRawCallback(const TrackView<K, V, R>& track, Callback callback, void* destination, void(*userCallback)(), void* userData) {
auto callbackPtr = static_cast<void(*)(const TrackViewStorage<K>&, K, std::size_t&, void*, void(*)(), void*)>(callback);
return addInternal(track, callbackPtr, destination, userCallback, userData);
}
#endif #endif
#if defined(CORRADE_TARGET_WINDOWS) && !defined(__MINGW32__) #if defined(CORRADE_TARGET_WINDOWS) && !defined(__MINGW32__)

34
src/Magnum/Animation/Test/Benchmark.cpp

@ -47,6 +47,8 @@ struct Benchmark: TestSuite::Tester {
void playerAdvanceEmptyTrack(); void playerAdvanceEmptyTrack();
void playerAdvance(); void playerAdvance();
void playerAdvanceCallback(); void playerAdvanceCallback();
void playerAdvanceRawCallback();
void playerAdvanceRawCallbackDirectInterpolator();
Containers::Array<Float> _keys; Containers::Array<Float> _keys;
Containers::Array<Int> _values; Containers::Array<Int> _values;
@ -76,7 +78,9 @@ Benchmark::Benchmark() {
&Benchmark::playerAdvanceEmpty, &Benchmark::playerAdvanceEmpty,
&Benchmark::playerAdvanceEmptyTrack, &Benchmark::playerAdvanceEmptyTrack,
&Benchmark::playerAdvance, &Benchmark::playerAdvance,
&Benchmark::playerAdvanceCallback}, 10); &Benchmark::playerAdvanceCallback,
&Benchmark::playerAdvanceRawCallback,
&Benchmark::playerAdvanceRawCallbackDirectInterpolator}, 10);
_keys = Containers::Array<Float>{DataSize}; _keys = Containers::Array<Float>{DataSize};
_values = Containers::Array<Int>{Containers::DirectInit, DataSize, 1}; _values = Containers::Array<Int>{Containers::DirectInit, DataSize, 1};
@ -228,6 +232,34 @@ void Benchmark::playerAdvanceCallback() {
CORRADE_COMPARE(result, 125000); CORRADE_COMPARE(result, 125000);
} }
void Benchmark::playerAdvanceRawCallback() {
Int result{};
Player<Float> player;
player.addRawCallback(_track, [](const TrackViewStorage<Float>& track, Float key, std::size_t& hint, void* destination, void(*)(), void*) {
*static_cast<Int*>(destination) += static_cast<const TrackView<Float, Int>&>(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<Float> player;
player.addRawCallback(_track, [](const TrackViewStorage<Float>& track, Float key, std::size_t& hint, void* destination, void(*)(), void*) {
*static_cast<Int*>(destination) += static_cast<const TrackView<Float, Int>&>(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) CORRADE_TEST_MAIN(Magnum::Animation::Test::Benchmark)

32
src/Magnum/Animation/Test/PlayerTest.cpp

@ -62,6 +62,7 @@ struct PlayerTest: TestSuite::Tester {
void addWithCallbackTemplate(); void addWithCallbackTemplate();
void addWithCallbackOnChange(); void addWithCallbackOnChange();
void addWithCallbackOnChangeTemplate(); void addWithCallbackOnChangeTemplate();
void addRawCallback();
void runFor100YearsFloat(); void runFor100YearsFloat();
void runFor100YearsChrono(); void runFor100YearsChrono();
@ -114,7 +115,8 @@ PlayerTest::PlayerTest() {
&PlayerTest::addWithCallback, &PlayerTest::addWithCallback,
&PlayerTest::addWithCallbackTemplate, &PlayerTest::addWithCallbackTemplate,
&PlayerTest::addWithCallbackOnChange, &PlayerTest::addWithCallbackOnChange,
&PlayerTest::addWithCallbackOnChangeTemplate}); &PlayerTest::addWithCallbackOnChangeTemplate,
&PlayerTest::addRawCallback});
addInstancedTests({ addInstancedTests({
&PlayerTest::runFor100YearsFloat, &PlayerTest::runFor100YearsFloat,
@ -704,6 +706,34 @@ void PlayerTest::addWithCallbackOnChangeTemplate() {
CORRADE_COMPARE(data.called, 2); CORRADE_COMPARE(data.called, 2);
} }
void PlayerTest::addRawCallback() {
Animation::Track<Float, Int> track;
Int result = -1;
std::vector<Int> data;
auto callback = [](std::vector<Int>& data, Int value) {
data.push_back(value);
};
Animation::Player<Float> player;
player.addRawCallback(track,
[](const Animation::TrackViewStorage<Float>& track, Float key,
std::size_t& hint, void* destination, void(*callback)(), void* userData) {
Int value = static_cast<const Animation::TrackView<Float, Int>&>(track).at(key, hint);
if(value == *static_cast<Int*>(destination)) return;
*static_cast<Int*>(destination) = value;
reinterpret_cast<void(*)(std::vector<Int>&, Int)>(callback)(*static_cast<std::vector<Int>*>(userData), value);
}, &result, reinterpret_cast<void(*)()>(+callback), &data)
.play({});
/* Should add the default-constructed value into the vector, but only once */
CORRADE_COMPARE(data, std::vector<Int>{});
player.advance({});
CORRADE_COMPARE(data, std::vector<Int>{0});
player.advance(1.0f);
CORRADE_COMPARE(data, std::vector<Int>{0});
}
void PlayerTest::runFor100YearsFloat() { void PlayerTest::runFor100YearsFloat() {
auto&& data = RunFor100YearsData[testCaseInstanceId()]; auto&& data = RunFor100YearsData[testCaseInstanceId()];
setTestCaseDescription(data.name); setTestCaseDescription(data.name);

Loading…
Cancel
Save