fix: complete STEP-047 playback validation controls
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
OpenAI Codex
2026-09-04 07:39:01 +08:00
parent 49b2e8aebd
commit 0609199cce
6 changed files with 110 additions and 27 deletions

View File

@@ -69,10 +69,11 @@ RasterArrayTimelineFrame RasterArrayTimelineRuntime::tick() {
while (mix >= 1.0f) {
if (gap || invalid) {
mix = 1.0f;
stateBeforeWait = state;
state = RasterArrayPlaybackState::Waiting;
Log::Warning(Event::Render,
std::string("ParticleTimeline: event=") + (invalid ? "INVALID_WAITING" : "GAP_WAITING") +
" t1=" + entries[t0 + 1].field->fixtureID);
std::string("ParticleTimeline: event=TIMELINE_REJECTED reason=") +
(invalid ? "INVALID_NEXT" : "GAP_HOLD") + " t1=" + entries[t0 + 1].field->fixtureID);
return frameLocked();
}
if (!moveToLocked(t0 + 1)) {
@@ -84,44 +85,70 @@ RasterArrayTimelineFrame RasterArrayTimelineRuntime::tick() {
Log::Info(Event::Render,
"ParticleTimeline: event=WINDOW_BOUNDARY oldT1=" + entries[t0].field->fixtureID +
" newT1=" + entries[t0 + 1].field->fixtureID + " result=APPLIED reuse=OLD_T1_TO_NEW_T0");
Log::Info(Event::Render,
"ParticleTimeline: event=TIMELINE_CACHE_HIT index=" + std::to_string(t0 + 1) +
" cacheSize=" + std::to_string(entries.size()) + " capacity=" + std::to_string(cacheCapacity_));
}
return frameLocked();
}
bool RasterArrayTimelineRuntime::play() {
std::lock_guard<std::mutex> lock(mutex);
if (state == RasterArrayPlaybackState::Waiting || state == RasterArrayPlaybackState::Ended) return false;
if (state == RasterArrayPlaybackState::Waiting) {
Log::Warning(Event::Render, "ParticleTimeline: event=PLAYBACK_REJECTED reason=WAITING");
return false;
}
if (state == RasterArrayPlaybackState::Ended) {
t0 = 0;
mix = 0.0f;
++windowRevision;
Log::Info(Event::Render, "ParticleTimeline: event=PLAYBACK_PLAY reason=RESTART_FROM_END");
}
state = RasterArrayPlaybackState::Playing;
lastTick = std::chrono::steady_clock::now();
Log::Info(Event::Render, "ParticleTimeline: event=PLAY speed=" + std::to_string(speed));
Log::Info(Event::Render,
"ParticleTimeline: event=PLAYBACK_PLAY speed=" + std::to_string(speed) + " cursor=" + std::to_string(t0));
return true;
}
bool RasterArrayTimelineRuntime::pause() {
std::lock_guard<std::mutex> lock(mutex);
if (state == RasterArrayPlaybackState::Waiting || state == RasterArrayPlaybackState::Ended) return false;
if (state == RasterArrayPlaybackState::Waiting || state == RasterArrayPlaybackState::Ended) {
Log::Warning(Event::Render, "ParticleTimeline: event=PLAYBACK_REJECTED reason=ENDED_OR_WAITING");
return false;
}
state = RasterArrayPlaybackState::Paused;
Log::Info(Event::Render, "ParticleTimeline: event=PAUSE");
Log::Info(Event::Render, "ParticleTimeline: event=PLAYBACK_PAUSE cursor=" + std::to_string(t0));
return true;
}
bool RasterArrayTimelineRuntime::setSpeed(const float value) {
if (value != 0.5f && value != 1.0f && value != 2.0f) return false;
std::lock_guard<std::mutex> lock(mutex);
const auto oldSpeed = speed;
speed = value;
Log::Info(Event::Render, "ParticleTimeline: event=SPEED value=" + std::to_string(speed));
Log::Info(Event::Render,
"ParticleTimeline: event=PLAYBACK_SPEED oldSpeed=" + std::to_string(oldSpeed) +
" newSpeed=" + std::to_string(speed) + " cursor=" + std::to_string(t0));
return true;
}
bool RasterArrayTimelineRuntime::seek(const size_t index) {
std::lock_guard<std::mutex> lock(mutex);
if (index >= entries.size()) return false;
const auto fromState = state;
const auto fromCursor = t0;
if (index + 1 == entries.size()) {
t0 = index - 1;
mix = 1.0f;
state = RasterArrayPlaybackState::Ended;
} else {
moveToLocked(index);
state = RasterArrayPlaybackState::Paused;
state = fromState == RasterArrayPlaybackState::Playing ? RasterArrayPlaybackState::Playing
: RasterArrayPlaybackState::Paused;
}
Log::Info(Event::Render, "ParticleTimeline: event=SEEK index=" + std::to_string(index));
Log::Info(Event::Render,
"ParticleTimeline: event=PLAYBACK_SEEK fromState=" + std::to_string(static_cast<int>(fromState)) +
" toState=" + std::to_string(static_cast<int>(state)) + " fromCursor=" + std::to_string(fromCursor) +
" toCursor=" + std::to_string(t0) + " targetIndex=" + std::to_string(index) + " t0Index=" +
std::to_string(t0) + " t1Index=" + std::to_string(t0 + 1) + " mix=" + std::to_string(mix));
return true;
}
bool RasterArrayTimelineRuntime::step(const int direction) {
@@ -149,8 +176,8 @@ bool RasterArrayTimelineRuntime::injectInvalid() {
bool RasterArrayTimelineRuntime::recover() {
std::lock_guard<std::mutex> lock(mutex);
gap = invalid = false;
if (state == RasterArrayPlaybackState::Waiting) state = RasterArrayPlaybackState::Paused;
Log::Info(Event::Render, "ParticleTimeline: event=RECOVERED");
if (state == RasterArrayPlaybackState::Waiting) state = stateBeforeWait;
Log::Info(Event::Render, "ParticleTimeline: event=RECOVERED state=" + std::to_string(static_cast<int>(state)));
return true;
}
@@ -332,13 +359,13 @@ void RasterArraySource::tryInitializeTimeline() {
std::vector<RasterArrayTimelineRuntime::Entry> entries;
entries.reserve(runtimeFields.size() + 2);
entries.push_back({std::make_shared<particle::ParticleFieldSnapshot>(*impl().field), 0});
entries.push_back({std::make_shared<particle::ParticleFieldSnapshot>(*impl().nextField), 4000});
entries.push_back({std::make_shared<particle::ParticleFieldSnapshot>(*impl().nextField), 40000});
for (size_t index = 0; index < runtimeFields.size(); ++index) {
if (!runtimeFields[index].snapshot) return;
auto field = *runtimeFields[index].snapshot;
field.generation = ++nextRasterArrayFieldGeneration;
entries.push_back({std::make_shared<particle::ParticleFieldSnapshot>(std::move(field)),
8000 + static_cast<uint64_t>(index) * 4000});
80000 + static_cast<uint64_t>(index) * 40000});
}
try {
timeline = std::make_shared<RasterArrayTimelineRuntime>(std::move(entries));
@@ -348,6 +375,12 @@ void RasterArraySource::tryInitializeTimeline() {
Log::Info(Event::Render,
"ParticleTimeline: event=READY snapshots=" + std::to_string(runtimeFields.size() + 2) +
" cacheCapacity=" + std::to_string(timeline->cacheCapacity()));
for (size_t index = 0; index < runtimeFields.size() + 2; ++index) {
Log::Info(Event::Render,
"ParticleTimeline: event=TIMELINE_PRELOAD timelineId=" + getID() +
" index=" + std::to_string(index) + " cacheSize=" + std::to_string(runtimeFields.size() + 2) +
" capacity=" + std::to_string(timeline->cacheCapacity()));
}
observer->onSourceChanged(*this);
} catch (const std::exception& error) {
observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(error.what())));