Loading libs/gui/DisplayEventDispatcher.cpp +16 −0 Original line number Diff line number Diff line Loading @@ -130,6 +130,19 @@ int DisplayEventDispatcher::handleEvent(int, int events, void*) { return 1; // keep the callback } void DisplayEventDispatcher::populateFrameTimelines(const DisplayEventReceiver::Event& event, VsyncEventData* outVsyncEventData) const { for (size_t i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) { DisplayEventReceiver::Event::VSync::FrameTimeline receiverTimeline = event.vsync.frameTimelines[i]; outVsyncEventData->frameTimelines[i] = {.id = receiverTimeline.vsyncId, .deadlineTimestamp = receiverTimeline.deadlineTimestamp, .expectedPresentTime = receiverTimeline.expectedVSyncTimestamp}; } } bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount, Loading @@ -154,6 +167,9 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp; outVsyncEventData->frameInterval = ev.vsync.frameInterval; outVsyncEventData->expectedPresentTime = ev.vsync.expectedVSyncTimestamp; outVsyncEventData->preferredFrameTimelineIndex = ev.vsync.preferredFrameTimelineIndex; populateFrameTimelines(ev, outVsyncEventData); break; case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); Loading libs/gui/include/gui/DisplayEventDispatcher.h +24 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <gui/DisplayEventReceiver.h> #include <utils/Log.h> #include <utils/Looper.h> #include <array> namespace android { using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; Loading @@ -36,6 +37,26 @@ struct VsyncEventData { // The anticipated Vsync present time. int64_t expectedPresentTime = 0; struct FrameTimeline { // The Vsync Id corresponsing to this vsync event. This will be used to // populate ISurfaceComposer::setFrameTimelineVsync and // SurfaceComposerClient::setFrameTimelineVsync int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID; // The deadline in CLOCK_MONOTONIC that the app needs to complete its // frame by (both on the CPU and the GPU) int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max(); // The anticipated Vsync present time. int64_t expectedPresentTime = 0; }; // Sorted possible frame timelines. std::array<FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength> frameTimelines; // Index into the frameTimelines that represents the platform's preferred frame timeline. size_t preferredFrameTimelineIndex = std::numeric_limits<size_t>::max(); }; class DisplayEventDispatcher : public LooperCallback { Loading Loading @@ -77,5 +98,8 @@ private: bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount, VsyncEventData* outVsyncEventData); void populateFrameTimelines(const DisplayEventReceiver::Event& event, VsyncEventData* outVsyncEventData) const; }; } // namespace android libs/gui/include/gui/DisplayEventReceiver.h +9 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,9 @@ static inline constexpr uint32_t fourcc(char c1, char c2, char c3, char c4) { // ---------------------------------------------------------------------------- class DisplayEventReceiver { public: // Max amount of frame timelines is arbitrarily set to be reasonable. static constexpr int64_t kFrameTimelinesLength = 7; enum { DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), Loading Loading @@ -77,6 +80,12 @@ public: nsecs_t deadlineTimestamp __attribute__((aligned(8))); nsecs_t frameInterval __attribute__((aligned(8))); int64_t vsyncId; size_t preferredFrameTimelineIndex __attribute__((aligned(8))); struct FrameTimeline { nsecs_t expectedVSyncTimestamp __attribute__((aligned(8))); nsecs_t deadlineTimestamp __attribute__((aligned(8))); int64_t vsyncId; } frameTimelines[kFrameTimelinesLength]; }; struct Hotplug { Loading libs/nativedisplay/AChoreographer.cpp +11 −23 Original line number Diff line number Diff line Loading @@ -100,17 +100,10 @@ class Choreographer; * Implementation of AChoreographerFrameCallbackData. */ struct ChoreographerFrameCallbackDataImpl { struct FrameTimeline { int64_t vsyncId{0}; int64_t expectedPresentTimeNanos{0}; int64_t deadlineNanos{0}; }; int64_t frameTimeNanos{0}; size_t frameTimelinesLength; std::vector<FrameTimeline> frameTimelines; std::array<VsyncEventData::FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength> frameTimelines; size_t preferredFrameTimelineIndex; Loading Loading @@ -456,14 +449,9 @@ bool Choreographer::inCallback() const { } ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const { std::vector<ChoreographerFrameCallbackDataImpl::FrameTimeline> frameTimelines; frameTimelines.push_back({.vsyncId = mLastVsyncEventData.id, .expectedPresentTimeNanos = mLastVsyncEventData.expectedPresentTime, .deadlineNanos = mLastVsyncEventData.deadlineTimestamp}); return {.frameTimeNanos = timestamp, .frameTimelinesLength = 1, .preferredFrameTimelineIndex = 0, .frameTimelines = frameTimelines, .preferredFrameTimelineIndex = mLastVsyncEventData.preferredFrameTimelineIndex, .frameTimelines = mLastVsyncEventData.frameTimelines, .choreographer = this}; } Loading Loading @@ -646,7 +634,7 @@ size_t AChoreographerFrameCallbackData_getFrameTimelinesLength( AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), "Data is only valid in callback"); return frameCallbackData->frameTimelinesLength; return frameCallbackData->frameTimelines.size(); } size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex( const AChoreographerFrameCallbackData* data) { Loading @@ -662,8 +650,8 @@ int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId( AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), "Data is only valid in callback"); LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds"); return frameCallbackData->frameTimelines[index].vsyncId; LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds"); return frameCallbackData->frameTimelines[index].id; } int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime( const AChoreographerFrameCallbackData* data, size_t index) { Loading @@ -671,8 +659,8 @@ int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime( AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), "Data is only valid in callback"); LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds"); return frameCallbackData->frameTimelines[index].expectedPresentTimeNanos; LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds"); return frameCallbackData->frameTimelines[index].expectedPresentTime; } int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline( const AChoreographerFrameCallbackData* data, size_t index) { Loading @@ -680,8 +668,8 @@ int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline( AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), "Data is only valid in callback"); LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds"); return frameCallbackData->frameTimelines[index].deadlineNanos; LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds"); return frameCallbackData->frameTimelines[index].deadlineTimestamp; } AChoreographer* AChoreographer_create() { Loading services/surfaceflinger/Scheduler/EventThread.cpp +37 −8 Original line number Diff line number Diff line Loading @@ -355,14 +355,7 @@ void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp std::lock_guard<std::mutex> lock(mMutex); LOG_FATAL_IF(!mVSyncState); const int64_t vsyncId = [&] { if (mTokenManager != nullptr) { return mTokenManager->generateTokenForPredictions( {timestamp, deadlineTimestamp, expectedVSyncTimestamp}); } return FrameTimelineInfo::INVALID_VSYNC_ID; }(); const int64_t vsyncId = generateToken(timestamp, deadlineTimestamp, expectedVSyncTimestamp); mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count, expectedVSyncTimestamp, deadlineTimestamp, vsyncId)); mCondition.notify_all(); Loading Loading @@ -567,12 +560,48 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, } } int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp, nsecs_t deadlineTimestamp) const { if (mTokenManager != nullptr) { return mTokenManager->generateTokenForPredictions( {timestamp, deadlineTimestamp, expectedVSyncTimestamp}); } return FrameTimelineInfo::INVALID_VSYNC_ID; } void EventThread::generateFrameTimeline(DisplayEventReceiver::Event& event) const { // Add 1 to ensure the preferredFrameTimelineIndex entry (when multiplier == 0) is included. for (int multiplier = -DisplayEventReceiver::kFrameTimelinesLength + 1, currentIndex = 0; currentIndex < DisplayEventReceiver::kFrameTimelinesLength; multiplier++) { nsecs_t deadline = event.vsync.deadlineTimestamp + multiplier * event.vsync.frameInterval; // Valid possible frame timelines must have future values. if (deadline > event.header.timestamp) { if (multiplier == 0) { event.vsync.preferredFrameTimelineIndex = currentIndex; event.vsync.frameTimelines[currentIndex] = {.vsyncId = event.vsync.vsyncId, .deadlineTimestamp = event.vsync.deadlineTimestamp, .expectedVSyncTimestamp = event.vsync.expectedVSyncTimestamp}; } else { nsecs_t expectedVSync = event.vsync.expectedVSyncTimestamp + multiplier * event.vsync.frameInterval; event.vsync.frameTimelines[currentIndex] = {.vsyncId = generateToken(event.header.timestamp, expectedVSync, deadline), .deadlineTimestamp = deadline, .expectedVSyncTimestamp = expectedVSync}; } currentIndex++; } } } void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, const DisplayEventConsumers& consumers) { for (const auto& consumer : consumers) { DisplayEventReceiver::Event copy = event; if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { copy.vsync.frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid); generateFrameTimeline(copy); } switch (consumer->postEvent(copy)) { case NO_ERROR: Loading Loading
libs/gui/DisplayEventDispatcher.cpp +16 −0 Original line number Diff line number Diff line Loading @@ -130,6 +130,19 @@ int DisplayEventDispatcher::handleEvent(int, int events, void*) { return 1; // keep the callback } void DisplayEventDispatcher::populateFrameTimelines(const DisplayEventReceiver::Event& event, VsyncEventData* outVsyncEventData) const { for (size_t i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) { DisplayEventReceiver::Event::VSync::FrameTimeline receiverTimeline = event.vsync.frameTimelines[i]; outVsyncEventData->frameTimelines[i] = {.id = receiverTimeline.vsyncId, .deadlineTimestamp = receiverTimeline.deadlineTimestamp, .expectedPresentTime = receiverTimeline.expectedVSyncTimestamp}; } } bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount, Loading @@ -154,6 +167,9 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp; outVsyncEventData->frameInterval = ev.vsync.frameInterval; outVsyncEventData->expectedPresentTime = ev.vsync.expectedVSyncTimestamp; outVsyncEventData->preferredFrameTimelineIndex = ev.vsync.preferredFrameTimelineIndex; populateFrameTimelines(ev, outVsyncEventData); break; case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); Loading
libs/gui/include/gui/DisplayEventDispatcher.h +24 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <gui/DisplayEventReceiver.h> #include <utils/Log.h> #include <utils/Looper.h> #include <array> namespace android { using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; Loading @@ -36,6 +37,26 @@ struct VsyncEventData { // The anticipated Vsync present time. int64_t expectedPresentTime = 0; struct FrameTimeline { // The Vsync Id corresponsing to this vsync event. This will be used to // populate ISurfaceComposer::setFrameTimelineVsync and // SurfaceComposerClient::setFrameTimelineVsync int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID; // The deadline in CLOCK_MONOTONIC that the app needs to complete its // frame by (both on the CPU and the GPU) int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max(); // The anticipated Vsync present time. int64_t expectedPresentTime = 0; }; // Sorted possible frame timelines. std::array<FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength> frameTimelines; // Index into the frameTimelines that represents the platform's preferred frame timeline. size_t preferredFrameTimelineIndex = std::numeric_limits<size_t>::max(); }; class DisplayEventDispatcher : public LooperCallback { Loading Loading @@ -77,5 +98,8 @@ private: bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount, VsyncEventData* outVsyncEventData); void populateFrameTimelines(const DisplayEventReceiver::Event& event, VsyncEventData* outVsyncEventData) const; }; } // namespace android
libs/gui/include/gui/DisplayEventReceiver.h +9 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,9 @@ static inline constexpr uint32_t fourcc(char c1, char c2, char c3, char c4) { // ---------------------------------------------------------------------------- class DisplayEventReceiver { public: // Max amount of frame timelines is arbitrarily set to be reasonable. static constexpr int64_t kFrameTimelinesLength = 7; enum { DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), Loading Loading @@ -77,6 +80,12 @@ public: nsecs_t deadlineTimestamp __attribute__((aligned(8))); nsecs_t frameInterval __attribute__((aligned(8))); int64_t vsyncId; size_t preferredFrameTimelineIndex __attribute__((aligned(8))); struct FrameTimeline { nsecs_t expectedVSyncTimestamp __attribute__((aligned(8))); nsecs_t deadlineTimestamp __attribute__((aligned(8))); int64_t vsyncId; } frameTimelines[kFrameTimelinesLength]; }; struct Hotplug { Loading
libs/nativedisplay/AChoreographer.cpp +11 −23 Original line number Diff line number Diff line Loading @@ -100,17 +100,10 @@ class Choreographer; * Implementation of AChoreographerFrameCallbackData. */ struct ChoreographerFrameCallbackDataImpl { struct FrameTimeline { int64_t vsyncId{0}; int64_t expectedPresentTimeNanos{0}; int64_t deadlineNanos{0}; }; int64_t frameTimeNanos{0}; size_t frameTimelinesLength; std::vector<FrameTimeline> frameTimelines; std::array<VsyncEventData::FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength> frameTimelines; size_t preferredFrameTimelineIndex; Loading Loading @@ -456,14 +449,9 @@ bool Choreographer::inCallback() const { } ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const { std::vector<ChoreographerFrameCallbackDataImpl::FrameTimeline> frameTimelines; frameTimelines.push_back({.vsyncId = mLastVsyncEventData.id, .expectedPresentTimeNanos = mLastVsyncEventData.expectedPresentTime, .deadlineNanos = mLastVsyncEventData.deadlineTimestamp}); return {.frameTimeNanos = timestamp, .frameTimelinesLength = 1, .preferredFrameTimelineIndex = 0, .frameTimelines = frameTimelines, .preferredFrameTimelineIndex = mLastVsyncEventData.preferredFrameTimelineIndex, .frameTimelines = mLastVsyncEventData.frameTimelines, .choreographer = this}; } Loading Loading @@ -646,7 +634,7 @@ size_t AChoreographerFrameCallbackData_getFrameTimelinesLength( AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), "Data is only valid in callback"); return frameCallbackData->frameTimelinesLength; return frameCallbackData->frameTimelines.size(); } size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex( const AChoreographerFrameCallbackData* data) { Loading @@ -662,8 +650,8 @@ int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId( AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), "Data is only valid in callback"); LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds"); return frameCallbackData->frameTimelines[index].vsyncId; LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds"); return frameCallbackData->frameTimelines[index].id; } int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime( const AChoreographerFrameCallbackData* data, size_t index) { Loading @@ -671,8 +659,8 @@ int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime( AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), "Data is only valid in callback"); LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds"); return frameCallbackData->frameTimelines[index].expectedPresentTimeNanos; LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds"); return frameCallbackData->frameTimelines[index].expectedPresentTime; } int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline( const AChoreographerFrameCallbackData* data, size_t index) { Loading @@ -680,8 +668,8 @@ int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline( AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data); LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(), "Data is only valid in callback"); LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds"); return frameCallbackData->frameTimelines[index].deadlineNanos; LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds"); return frameCallbackData->frameTimelines[index].deadlineTimestamp; } AChoreographer* AChoreographer_create() { Loading
services/surfaceflinger/Scheduler/EventThread.cpp +37 −8 Original line number Diff line number Diff line Loading @@ -355,14 +355,7 @@ void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp std::lock_guard<std::mutex> lock(mMutex); LOG_FATAL_IF(!mVSyncState); const int64_t vsyncId = [&] { if (mTokenManager != nullptr) { return mTokenManager->generateTokenForPredictions( {timestamp, deadlineTimestamp, expectedVSyncTimestamp}); } return FrameTimelineInfo::INVALID_VSYNC_ID; }(); const int64_t vsyncId = generateToken(timestamp, deadlineTimestamp, expectedVSyncTimestamp); mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count, expectedVSyncTimestamp, deadlineTimestamp, vsyncId)); mCondition.notify_all(); Loading Loading @@ -567,12 +560,48 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, } } int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp, nsecs_t deadlineTimestamp) const { if (mTokenManager != nullptr) { return mTokenManager->generateTokenForPredictions( {timestamp, deadlineTimestamp, expectedVSyncTimestamp}); } return FrameTimelineInfo::INVALID_VSYNC_ID; } void EventThread::generateFrameTimeline(DisplayEventReceiver::Event& event) const { // Add 1 to ensure the preferredFrameTimelineIndex entry (when multiplier == 0) is included. for (int multiplier = -DisplayEventReceiver::kFrameTimelinesLength + 1, currentIndex = 0; currentIndex < DisplayEventReceiver::kFrameTimelinesLength; multiplier++) { nsecs_t deadline = event.vsync.deadlineTimestamp + multiplier * event.vsync.frameInterval; // Valid possible frame timelines must have future values. if (deadline > event.header.timestamp) { if (multiplier == 0) { event.vsync.preferredFrameTimelineIndex = currentIndex; event.vsync.frameTimelines[currentIndex] = {.vsyncId = event.vsync.vsyncId, .deadlineTimestamp = event.vsync.deadlineTimestamp, .expectedVSyncTimestamp = event.vsync.expectedVSyncTimestamp}; } else { nsecs_t expectedVSync = event.vsync.expectedVSyncTimestamp + multiplier * event.vsync.frameInterval; event.vsync.frameTimelines[currentIndex] = {.vsyncId = generateToken(event.header.timestamp, expectedVSync, deadline), .deadlineTimestamp = deadline, .expectedVSyncTimestamp = expectedVSync}; } currentIndex++; } } } void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, const DisplayEventConsumers& consumers) { for (const auto& consumer : consumers) { DisplayEventReceiver::Event copy = event; if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { copy.vsync.frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid); generateFrameTimeline(copy); } switch (consumer->postEvent(copy)) { case NO_ERROR: Loading