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-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] */
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
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<class T, class K
* @brief Whether the player is empty
*
* @see @ref size(), @ref add(), @ref addWithCallback(),
* @ref addWithCallbackOnChange()
* @ref addWithCallbackOnChange(), @ref addRawCallback()
*/
bool isEmpty() const;
@ -331,7 +333,7 @@ template<class T, class K
* @brief Count of tracks managed by this player
*
* @see @ref isEmpty(), @ref add(), @ref addWithCallback(),
* @ref addWithCallbackOnChange()
* @ref addWithCallbackOnChange(), @ref addRawCallback()
*/
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
* user data.
* @see @ref addRawCallback()
*/
#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);
@ -404,7 +407,8 @@ template<class T, class K
* 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
* 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
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
* user data.
* @see @ref addRawCallback()
*/
#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);
@ -466,7 +471,8 @@ template<class T, class K
* 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
* 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
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
/**
* @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
*
@ -641,6 +689,11 @@ template<class T, class K> template<class V, class R, class U, class Callback> P
*static_cast<R*>(destination) = result;
}, &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
#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 playerAdvance();
void playerAdvanceCallback();
void playerAdvanceRawCallback();
void playerAdvanceRawCallbackDirectInterpolator();
Containers::Array<Float> _keys;
Containers::Array<Int> _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<Float>{DataSize};
_values = Containers::Array<Int>{Containers::DirectInit, DataSize, 1};
@ -228,6 +232,34 @@ void Benchmark::playerAdvanceCallback() {
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)

32
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<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() {
auto&& data = RunFor100YearsData[testCaseInstanceId()];
setTestCaseDescription(data.name);

Loading…
Cancel
Save