mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-22 02:06:03 +08:00
223 lines
7.2 KiB
C++
Executable File
223 lines
7.2 KiB
C++
Executable File
#include "MemoryTestFixture.h"
|
|
#include "SpineEventMonitor.h"
|
|
|
|
#include "spine/spine.h"
|
|
|
|
#include "KMemory.h" // last include
|
|
|
|
#define SPINEBOY_JSON "testdata/spineboy/spineboy.json"
|
|
#define SPINEBOY_ATLAS "testdata/spineboy/spineboy.atlas"
|
|
|
|
#define MAX_RUN_TIME 6000 // equal to about 100 seconds of execution
|
|
|
|
MemoryTestFixture::~MemoryTestFixture()
|
|
{
|
|
finalize();
|
|
}
|
|
|
|
void MemoryTestFixture::initialize()
|
|
{
|
|
// on a Per- Fixture Basis, before Test execution
|
|
}
|
|
|
|
void MemoryTestFixture::finalize()
|
|
{
|
|
// on a Per- Fixture Basis, after all tests pass/fail
|
|
}
|
|
|
|
void MemoryTestFixture::setUp()
|
|
{
|
|
// Setup on Per-Test Basis
|
|
}
|
|
|
|
void MemoryTestFixture::tearDown()
|
|
{
|
|
// Tear Down on Per-Test Basis
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Helper methods
|
|
static spSkeletonData* readSkeletonJsonData(const char* filename, spAtlas* atlas) {
|
|
spSkeletonJson* json = spSkeletonJson_create(atlas);
|
|
ASSERT(json != nullptr);
|
|
|
|
spSkeletonData* skeletonData = spSkeletonJson_readSkeletonDataFile(json, filename);
|
|
ASSERT(skeletonData != nullptr);
|
|
|
|
spSkeletonJson_dispose(json);
|
|
return skeletonData;
|
|
}
|
|
|
|
static void LoadSpineboyExample(spAtlas* &atlas, spSkeletonData* &skeletonData, spAnimationStateData* &stateData, spSkeleton* &skeleton, spAnimationState* &state)
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Global Animation Information
|
|
atlas = spAtlas_createFromFile(SPINEBOY_ATLAS, 0);
|
|
ASSERT(atlas != nullptr);
|
|
|
|
skeletonData = readSkeletonJsonData(SPINEBOY_JSON, atlas);
|
|
ASSERT(skeletonData != nullptr);
|
|
|
|
stateData = spAnimationStateData_create(skeletonData);
|
|
ASSERT(stateData != nullptr);
|
|
stateData->defaultMix = 0.4f; // force mixing
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Animation Instance
|
|
skeleton = spSkeleton_create(skeletonData);
|
|
ASSERT(skeleton != nullptr);
|
|
|
|
state = spAnimationState_create(stateData);
|
|
ASSERT(state != nullptr);
|
|
}
|
|
|
|
static void DisposeAll(spSkeleton* skeleton, spAnimationState* state, spAnimationStateData* stateData, spSkeletonData* skeletonData, spAtlas* atlas)
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Dispose Instance
|
|
spSkeleton_dispose(skeleton);
|
|
spAnimationState_dispose(state);
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Dispose Global
|
|
spAnimationStateData_dispose(stateData);
|
|
spSkeletonData_dispose(skeletonData);
|
|
spAtlas_dispose(atlas);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Reproduce Memory leak as described in Issue #776
|
|
// https://github.com/EsotericSoftware/spine-runtimes/issues/776
|
|
void MemoryTestFixture::reproduceIssue_776()
|
|
{
|
|
spAtlas* atlas = nullptr;
|
|
spSkeletonData* skeletonData = nullptr;
|
|
spAnimationStateData* stateData = nullptr;
|
|
spSkeleton* skeleton = nullptr;
|
|
spAnimationState* state = nullptr;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Initialize Animations
|
|
LoadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Run animation
|
|
spSkeleton_setToSetupPose(skeleton);
|
|
InterruptMonitor eventMonitor(state);
|
|
//eventMonitor.SetDebugLogging(true);
|
|
|
|
// Interrupt the animation on this specific sequence of spEventType(s)
|
|
eventMonitor
|
|
.AddInterruptEvent(SP_ANIMATION_INTERRUPT, "jump")
|
|
.AddInterruptEvent(SP_ANIMATION_START);
|
|
|
|
spAnimationState_setAnimationByName(state, 0, "walk", true);
|
|
spAnimationState_addAnimationByName(state, 0, "jump", false, 0.0f);
|
|
spAnimationState_addAnimationByName(state, 0, "run", true, 0.0f);
|
|
spAnimationState_addAnimationByName(state, 0, "jump", false, 3.0f);
|
|
spAnimationState_addAnimationByName(state, 0, "walk", true, 0.0f);
|
|
spAnimationState_addAnimationByName(state, 0, "idle", false, 1.0f);
|
|
|
|
for (int i = 0; i < MAX_RUN_TIME && eventMonitor.isAnimationPlaying(); ++i) {
|
|
const float timeSlice = 1.0f / 60.0f;
|
|
spSkeleton_update(skeleton, timeSlice);
|
|
spAnimationState_update(state, timeSlice);
|
|
spAnimationState_apply(state, skeleton);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Cleanup Animations
|
|
DisposeAll(skeleton, state, stateData, skeletonData, atlas);
|
|
}
|
|
|
|
void MemoryTestFixture::reproduceIssue_777()
|
|
{
|
|
spAtlas* atlas = nullptr;
|
|
spSkeletonData* skeletonData = nullptr;
|
|
spAnimationStateData* stateData = nullptr;
|
|
spSkeleton* skeleton = nullptr;
|
|
spAnimationState* state = nullptr;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Initialize Animations
|
|
LoadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Run animation
|
|
spSkeleton_setToSetupPose(skeleton);
|
|
SpineEventMonitor eventMonitor(state);
|
|
//eventMonitor.SetDebugLogging(true);
|
|
|
|
// Set Animation and Play for 5 frames
|
|
spAnimationState_setAnimationByName(state, 0, "walk", true);
|
|
for (int i = 0; i < 5; ++i) {
|
|
const float timeSlice = 1.0f / 60.0f;
|
|
spSkeleton_update(skeleton, timeSlice);
|
|
spAnimationState_update(state, timeSlice);
|
|
spAnimationState_apply(state, skeleton);
|
|
}
|
|
|
|
// Change animation twice in a row
|
|
spAnimationState_setAnimationByName(state, 0, "walk", false);
|
|
spAnimationState_setAnimationByName(state, 0, "run", false);
|
|
|
|
// run normal update
|
|
for (int i = 0; i < 5; ++i) {
|
|
const float timeSlice = 1.0f / 60.0f;
|
|
spSkeleton_update(skeleton, timeSlice);
|
|
spAnimationState_update(state, timeSlice);
|
|
spAnimationState_apply(state, skeleton);
|
|
}
|
|
|
|
// Now we'd lose mixingFrom (the first "walk" entry we set above) and should leak
|
|
spAnimationState_setAnimationByName(state, 0, "run", false);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Cleanup Animations
|
|
DisposeAll(skeleton, state, stateData, skeletonData, atlas);
|
|
}
|
|
|
|
spSkeleton* skeleton = nullptr;
|
|
static void spineAnimStateHandler(spAnimationState* state, int type, spTrackEntry* entry, spEvent* event)
|
|
{
|
|
if (type == SP_ANIMATION_COMPLETE)
|
|
{
|
|
spAnimationState_setAnimationByName(state, 0, "walk", false);
|
|
spAnimationState_update(state, 0);
|
|
spAnimationState_apply(state, skeleton);
|
|
}
|
|
}
|
|
|
|
void MemoryTestFixture::reproduceIssue_Loop()
|
|
{
|
|
spAtlas* atlas = nullptr;
|
|
spSkeletonData* skeletonData = nullptr;
|
|
spAnimationStateData* stateData = nullptr;
|
|
spAnimationState* state = nullptr;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Initialize Animations
|
|
LoadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
if (state)
|
|
state->listener = (spAnimationStateListener)&spineAnimStateHandler;
|
|
|
|
spAnimationState_setAnimationByName(state, 0, "walk", false);
|
|
|
|
// run normal update
|
|
for (int i = 0; i < 50; ++i) {
|
|
const float timeSlice = 1.0f / 60.0f;
|
|
spSkeleton_update(skeleton, timeSlice);
|
|
spAnimationState_update(state, timeSlice);
|
|
spAnimationState_apply(state, skeleton);
|
|
}
|
|
|
|
DisposeAll(skeleton, state, stateData, skeletonData, atlas);
|
|
}
|
|
|
|
|