Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 87baae10 authored by Mathias Agopian's avatar Mathias Agopian
Browse files

get rid of global regions that should be tracked per display

Change-Id: I3b871860cc29f1b2fdcc22b0c577a6eae65d9296
parent 53f10b7b
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -108,7 +108,8 @@ DisplayHardware::DisplayHardware(
      mDisplayId(display),
      mNativeWindow(surface),
      mFlags(0),
      mSecureLayerVisible(false)
      mSecureLayerVisible(false),
      mLayerStack(0)
{
    init(config);
}
+13 −2
Original line number Diff line number Diff line
@@ -45,6 +45,12 @@ class SurfaceTextureClient;
class DisplayHardware : public DisplayHardwareBase
{
public:
    // region in layer-stack space
    mutable Region dirtyRegion;
    // region in screen space
    mutable Region swapRegion;
    // region in screen space
    Region undefinedRegion;

    enum {
        PARTIAL_UPDATES = 0x00020000, // video driver feature
@@ -86,6 +92,8 @@ public:
    status_t                setOrientation(int orientation);
    int                     getOrientation() const { return mOrientation; }
    const Transform&        getTransform() const { return mGlobalTransform; }
    uint32_t                getLayerStack() const { return mLayerStack; }


    uint32_t getPageFlipCount() const;
    EGLDisplay getEGLDisplay() const { return mDisplay; }
@@ -144,6 +152,9 @@ private:
    Transform mGlobalTransform;
    int mOrientation;

    uint32_t mLayerStack;


    /*
     *  protected by mLock
     */
+115 −132
Original line number Diff line number Diff line
@@ -132,7 +132,6 @@ void SurfaceFlinger::onFirstRef()

SurfaceFlinger::~SurfaceFlinger()
{
    glDeleteTextures(1, &mWormholeTexName);
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglTerminate(display);
@@ -308,18 +307,6 @@ void SurfaceFlinger::initializeGL(EGLDisplay display, EGLSurface surface) {
        }
    } pack565;

    const uint16_t g0 = pack565(0x0F,0x1F,0x0F);
    const uint16_t g1 = pack565(0x17,0x2f,0x17);
    const uint16_t wormholeTexData[4] = { g0, g1, g1, g0 };
    glGenTextures(1, &mWormholeTexName);
    glBindTexture(GL_TEXTURE_2D, mWormholeTexName);
    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0,
            GL_RGB, GL_UNSIGNED_SHORT_5_6_5, wormholeTexData);

    const uint16_t protTexData[] = { pack565(0x03, 0x03, 0x03) };
    glGenTextures(1, &mProtectedTexName);
    glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
@@ -579,81 +566,80 @@ void SurfaceFlinger::handleMessageTransaction() {
    const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
    uint32_t transactionFlags = peekTransactionFlags(mask);
    if (transactionFlags) {
        Region dirtyRegion;
        dirtyRegion = handleTransaction(transactionFlags);
        // XXX: dirtyRegion should be per screen
        mDirtyRegion |= dirtyRegion;
        handleTransaction(transactionFlags);
    }
}

void SurfaceFlinger::handleMessageInvalidate() {
    Region dirtyRegion;
    dirtyRegion = handlePageFlip();
    // XXX: dirtyRegion should be per screen
    mDirtyRegion |= dirtyRegion;
    handlePageFlip();
}

void SurfaceFlinger::handleMessageRefresh() {
    handleRefresh();

    if (mVisibleRegionsDirty) {
        Region opaqueRegion;
        Region dirtyRegion;
        const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
        computeVisibleRegions(currentLayers, dirtyRegion, opaqueRegion);
        mDirtyRegion.orSelf(dirtyRegion);
        mVisibleRegionsDirty = false;
        invalidateHwcGeometry();

        /*
         *  rebuild the visible layer list per screen
         */

        const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
        // TODO: iterate through all displays
        {
            DisplayHardware& hw(const_cast<DisplayHardware&>(getDisplayHardware(0)));

            Region opaqueRegion;
            Region dirtyRegion;
            computeVisibleRegions(currentLayers,
                    hw.getLayerStack(), dirtyRegion, opaqueRegion);
            hw.dirtyRegion.orSelf(dirtyRegion);

            Vector< sp<LayerBase> > layersSortedByZ;
            const size_t count = currentLayers.size();
            for (size_t i=0 ; i<count ; i++) {
                const Layer::State& s(currentLayers[i]->drawingState());
                if (s.layerStack == hw.getLayerStack()) {
                    if (!currentLayers[i]->visibleRegion.isEmpty()) {
                // TODO: also check that this layer is associated to this display
                        layersSortedByZ.add(currentLayers[i]);
                    }
                }
            }
            hw.setVisibleLayersSortedByZ(layersSortedByZ);


        // FIXME: mWormholeRegion needs to be calculated per screen
        //const DisplayHardware& hw(getDefaultDisplayHardware()); // XXX: we can't keep that here
        mWormholeRegion = Region(hw.getBounds()).subtract(
                hw.getTransform().transform(opaqueRegion) );
        mVisibleRegionsDirty = false;
        invalidateHwcGeometry();
            hw.undefinedRegion.set(hw.getBounds());
            hw.undefinedRegion.subtractSelf(hw.getTransform().transform(opaqueRegion));
        }


    // XXX: dirtyRegion should be per screen, we should check all of them
    if (mDirtyRegion.isEmpty()) {
        return;
    }

    const bool repaintEverything = android_atomic_and(0, &mRepaintEverything);

    // TODO: iterate through all displays
    const DisplayHardware& hw(getDisplayHardware(0));
    for (int dpy=0 ; dpy<1 ; dpy++) {
        DisplayHardware& hw(const_cast<DisplayHardware&>(getDisplayHardware(0)));
        if (hw.dirtyRegion.isEmpty()) {
            continue;
        }

    // XXX: dirtyRegion should be per screen
        // transform the dirty region into this screen's coordinate space
        const Transform& planeTransform(hw.getTransform());
    mDirtyRegion = planeTransform.transform(mDirtyRegion);
    mDirtyRegion.orSelf(getAndClearInvalidateRegion());
    mDirtyRegion.andSelf(hw.bounds());

        Region dirtyRegion;
        if (repaintEverything) {
            dirtyRegion = planeTransform.transform(hw.dirtyRegion);
            dirtyRegion.andSelf(hw.bounds());
        } else {
            dirtyRegion.set(hw.bounds());
        }
        hw.dirtyRegion.clear();

    if (CC_UNLIKELY(mHwWorkListDirty)) {
        // build the h/w work list
        if (CC_UNLIKELY(mHwWorkListDirty)) {
            handleWorkList(hw);
        }

        if (CC_LIKELY(hw.canDraw())) {
            // repaint the framebuffer (if needed)
        handleRepaint(hw);
            handleRepaint(hw, dirtyRegion);
            // inform the h/w that we're done compositing
            hw.compositionComplete();
            postFramebuffer();
@@ -661,7 +647,10 @@ void SurfaceFlinger::handleMessageRefresh() {
            // pretend we did the post
            hw.compositionComplete();
        }
    }


#if 0
    // render to the external display if we have one
    EGLSurface externalDisplaySurface = getExternalDisplaySurface();
    if (externalDisplaySurface != EGL_NO_SURFACE) {
@@ -699,6 +688,7 @@ void SurfaceFlinger::handleMessageRefresh() {

        ALOGE_IF(!success, "eglMakeCurrent -> internal failed");
    }
#endif

}

@@ -729,7 +719,8 @@ void SurfaceFlinger::postFramebuffer()
        }
    }

    hw.flip(mSwapRegion);
    hw.flip(hw.swapRegion);
    hw.swapRegion.clear();

    if (hwc.initCheck() == NO_ERROR) {
        hwc.commit(mEGLDisplay, hw.getEGLSurface());
@@ -747,15 +738,12 @@ void SurfaceFlinger::postFramebuffer()

    mLastSwapBufferTime = systemTime() - now;
    mDebugInSwapBuffers = 0;
    mSwapRegion.clear();
}

Region SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
{
    ATRACE_CALL();

    Region dirtyRegion;

    Mutex::Autolock _l(mStateLock);
    const nsecs_t now = systemTime();
    mDebugInTransaction = now;
@@ -768,19 +756,16 @@ Region SurfaceFlinger::handleTransaction(uint32_t transactionFlags)

    const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
    transactionFlags = getTransactionFlags(mask);
    dirtyRegion = handleTransactionLocked(transactionFlags);
    handleTransactionLocked(transactionFlags);

    mLastTransactionTime = systemTime() - now;
    mDebugInTransaction = 0;
    invalidateHwcGeometry();
    // here the transaction has been committed

    return dirtyRegion;
}

Region SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
{
    Region dirtyRegion;
    const LayerVector& currentLayers(mCurrentState.layersSortedByZ);
    const size_t count = currentLayers.size();

@@ -811,13 +796,12 @@ Region SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
            // the orientation has changed, recompute all visible regions
            // and invalidate everything.

            const int dpy = 0; // TODO: should be a parameter
            const int dpy = 0; // FIXME: should be a parameter
            DisplayHardware& hw(const_cast<DisplayHardware&>(getDisplayHardware(dpy)));
            hw.setOrientation(mCurrentState.orientation);
            hw.dirtyRegion.set(hw.bounds());

            // FIXME: mVisibleRegionsDirty & mDirtyRegion should this be per DisplayHardware?
            mVisibleRegionsDirty = true;
            mDirtyRegion.set(hw.bounds());
        }

        if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) {
@@ -836,19 +820,19 @@ Region SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
                const sp<LayerBase>& layer(previousLayers[i]);
                if (currentLayers.indexOf(layer) < 0) {
                    // this layer is not visible anymore
                    // TODO: we could traverse the tree from front to back and compute the actual visible region
                    // TODO: we could traverse the tree from front to back and
                    //       compute the actual visible region
                    // TODO: we could cache the transformed region
                    Layer::State front(layer->drawingState());
                    Region visibleReg = front.transform.transform(
                            Region(Rect(front.active.w, front.active.h)));
                    dirtyRegion.orSelf(visibleReg);
                    invalidateLayerStack(front.layerStack, visibleReg);
                }
            }
        }
    }

    commitTransaction();
    return dirtyRegion;
}

void SurfaceFlinger::commitTransaction()
@@ -867,7 +851,8 @@ void SurfaceFlinger::commitTransaction()
}

void SurfaceFlinger::computeVisibleRegions(
    const LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion)
        const LayerVector& currentLayers, uint32_t layerStack,
        Region& outDirtyRegion, Region& outOpaqueRegion)
{
    ATRACE_CALL();

@@ -875,7 +860,7 @@ void SurfaceFlinger::computeVisibleRegions(
    Region aboveCoveredLayers;
    Region dirty;

    dirtyRegion.clear();
    outDirtyRegion.clear();

    size_t i = currentLayers.size();
    while (i--) {
@@ -884,6 +869,10 @@ void SurfaceFlinger::computeVisibleRegions(
        // start with the whole surface at its current location
        const Layer::State& s(layer->drawingState());

        // only consider the layers on the given later stack
        if (s.layerStack != layerStack)
            continue;

        /*
         * opaqueRegion: area of a surface that is fully opaque.
         */
@@ -977,7 +966,7 @@ void SurfaceFlinger::computeVisibleRegions(
        dirty.subtractSelf(aboveOpaqueLayers);

        // accumulate to the screen dirty region
        dirtyRegion.orSelf(dirty);
        outDirtyRegion.orSelf(dirty);

        // Update aboveOpaqueLayers for next (lower) layer
        aboveOpaqueLayers.orSelf(opaqueRegion);
@@ -987,10 +976,18 @@ void SurfaceFlinger::computeVisibleRegions(
        layer->setCoveredRegion(coveredRegion);
    }

    opaqueRegion = aboveOpaqueLayers;
    outOpaqueRegion = aboveOpaqueLayers;
}

Region SurfaceFlinger::handlePageFlip()
void SurfaceFlinger::invalidateLayerStack(uint32_t layerStack,
        const Region& dirty) {
    // FIXME: update the dirty region of all displays
    // presenting this layer's layer stack.
    DisplayHardware& hw(const_cast<DisplayHardware&>(getDisplayHardware(0)));
    hw.dirtyRegion.orSelf(dirty);
}

void SurfaceFlinger::handlePageFlip()
{
    ATRACE_CALL();
    Region dirtyRegion;
@@ -1002,12 +999,12 @@ Region SurfaceFlinger::handlePageFlip()
    sp<LayerBase> const* layers = currentLayers.array();
    for (size_t i=0 ; i<count ; i++) {
        const sp<LayerBase>& layer(layers[i]);
        dirtyRegion.orSelf( layer->latchBuffer(visibleRegions) );
        const Region dirty(layer->latchBuffer(visibleRegions));
        Layer::State s(layer->drawingState());
        invalidateLayerStack(s.layerStack, dirty);
    }

    mVisibleRegionsDirty |= visibleRegions;

    return dirtyRegion;
}

void SurfaceFlinger::invalidateHwcGeometry()
@@ -1052,15 +1049,18 @@ void SurfaceFlinger::handleWorkList(const DisplayHardware& hw)
    }
}

void SurfaceFlinger::handleRepaint(const DisplayHardware& hw)
void SurfaceFlinger::handleRepaint(const DisplayHardware& hw,
        const Region& inDirtyRegion)
{
    ATRACE_CALL();

    Region dirtyRegion(inDirtyRegion);

    // compute the invalid region
    mSwapRegion.orSelf(mDirtyRegion);
    hw.swapRegion.orSelf(dirtyRegion);

    if (CC_UNLIKELY(mDebugRegion)) {
        debugFlashRegions(hw);
        debugFlashRegions(hw, dirtyRegion);
    }

    // set the frame buffer
@@ -1072,27 +1072,26 @@ void SurfaceFlinger::handleRepaint(const DisplayHardware& hw)
        // we can redraw only what's dirty, but since SWAP_RECTANGLE only
        // takes a rectangle, we must make sure to update that whole
        // rectangle in that case
        mDirtyRegion.set(mSwapRegion.bounds());
        dirtyRegion.set(hw.swapRegion.bounds());
    } else {
        if (flags & DisplayHardware::PARTIAL_UPDATES) {
            // We need to redraw the rectangle that will be updated
            // (pushed to the framebuffer).
            // This is needed because PARTIAL_UPDATES only takes one
            // rectangle instead of a region (see DisplayHardware::flip())
            mDirtyRegion.set(mSwapRegion.bounds());
            dirtyRegion.set(hw.swapRegion.bounds());
        } else {
            // we need to redraw everything (the whole screen)
            mDirtyRegion.set(hw.bounds());
            mSwapRegion = mDirtyRegion;
            dirtyRegion.set(hw.bounds());
            hw.swapRegion = dirtyRegion;
        }
    }

    setupHardwareComposer(hw);
    composeSurfaces(hw, mDirtyRegion);
    composeSurfaces(hw, dirtyRegion);

    // update the swap region and clear the dirty region
    mSwapRegion.orSelf(mDirtyRegion);
    mDirtyRegion.clear();
    hw.swapRegion.orSelf(dirtyRegion);
}

void SurfaceFlinger::setupHardwareComposer(const DisplayHardware& hw)
@@ -1147,10 +1146,11 @@ void SurfaceFlinger::composeSurfaces(const DisplayHardware& hw, const Region& di
            glClearColor(0, 0, 0, 0);
            glClear(GL_COLOR_BUFFER_BIT);
        } else {
            const Region region(hw.undefinedRegion.intersect(dirty));
            // screen is already cleared here
            if (!mWormholeRegion.isEmpty()) {
            if (!region.isEmpty()) {
                // can happen with SurfaceView
                drawWormhole();
                drawWormhole(region);
            }
        }

@@ -1185,17 +1185,18 @@ void SurfaceFlinger::composeSurfaces(const DisplayHardware& hw, const Region& di
    }
}

void SurfaceFlinger::debugFlashRegions(const DisplayHardware& hw)
void SurfaceFlinger::debugFlashRegions(const DisplayHardware& hw,
        const Region& dirtyRegion)
{
    const uint32_t flags = hw.getFlags();
    const int32_t height = hw.getHeight();
    if (mSwapRegion.isEmpty()) {
    if (hw.swapRegion.isEmpty()) {
        return;
    }

    if (!(flags & DisplayHardware::SWAP_RECTANGLE)) {
        const Region repaint((flags & DisplayHardware::PARTIAL_UPDATES) ?
                mDirtyRegion.bounds() : hw.bounds());
                dirtyRegion.bounds() : hw.bounds());
        composeSurfaces(hw, repaint);
    }

@@ -1211,8 +1212,8 @@ void SurfaceFlinger::debugFlashRegions(const DisplayHardware& hw)
        glColor4f(1, 1, 0, 1);
    }

    Region::const_iterator it = mDirtyRegion.begin();
    Region::const_iterator const end = mDirtyRegion.end();
    Region::const_iterator it = dirtyRegion.begin();
    Region::const_iterator const end = dirtyRegion.end();
    while (it != end) {
        const Rect& r = *it++;
        GLfloat vertices[][2] = {
@@ -1225,18 +1226,14 @@ void SurfaceFlinger::debugFlashRegions(const DisplayHardware& hw)
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    }

    hw.flip(mSwapRegion);
    hw.flip(hw.swapRegion);

    if (mDebugRegion > 1)
        usleep(mDebugRegion * 1000);
}

void SurfaceFlinger::drawWormhole() const
void SurfaceFlinger::drawWormhole(const Region& region) const
{
    const Region region(mWormholeRegion.intersect(mDirtyRegion));
    if (region.isEmpty())
        return;

    glDisable(GL_TEXTURE_EXTERNAL_OES);
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);
@@ -1814,7 +1811,7 @@ void SurfaceFlinger::dumpAllLocked(
    snprintf(buffer, SIZE, "EXTS: %s\n", extensions.getExtension());
    result.append(buffer);

    mWormholeRegion.dump(result, "WormholeRegion");
    hw.undefinedRegion.dump(result, "undefinedRegion");
    snprintf(buffer, SIZE,
            "  orientation=%d, canDraw=%d\n",
            mCurrentState.orientation, hw.canDraw());
@@ -1975,24 +1972,10 @@ status_t SurfaceFlinger::onTransact(
}

void SurfaceFlinger::repaintEverything() {
    const DisplayHardware& hw(getDefaultDisplayHardware()); // FIXME: this cannot be bound the default display
    const Rect bounds(hw.getBounds());
    setInvalidateRegion(Region(bounds));
    android_atomic_or(1, &mRepaintEverything);
    signalTransaction();
}

void SurfaceFlinger::setInvalidateRegion(const Region& reg) {
    Mutex::Autolock _l(mInvalidateLock);
    mInvalidateRegion = reg;
}

Region SurfaceFlinger::getAndClearInvalidateRegion() {
    Mutex::Autolock _l(mInvalidateLock);
    Region reg(mInvalidateRegion);
    mInvalidateRegion.clear();
    return reg;
}

// ---------------------------------------------------------------------------

status_t SurfaceFlinger::renderScreenToTexture(DisplayID dpy,
@@ -2503,7 +2486,7 @@ status_t SurfaceFlinger::turnElectronBeamOnImplLocked(int32_t mode)
    }

    // make sure to redraw the whole screen when the animation is done
    mDirtyRegion.set(hw.bounds());
    hw.dirtyRegion.set(hw.bounds());
    signalTransaction();

    return NO_ERROR;
+14 −21
Original line number Diff line number Diff line
@@ -228,21 +228,17 @@ private:
    void handleMessageInvalidate();
    void handleMessageRefresh();

    Region handleTransaction(uint32_t transactionFlags);
    Region handleTransactionLocked(uint32_t transactionFlags);
    void handleTransaction(uint32_t transactionFlags);
    void handleTransactionLocked(uint32_t transactionFlags);

    /* handlePageFilp: this is were we latch a new buffer
     * if available and compute the dirty region.
     * The return value is the dirty region expressed in the
     * window manager's coordinate space (or the layer's state
     * space, which is the same thing), in particular the dirty
     * region is independent from a specific display's orientation.
     */
    Region handlePageFlip();
    void handlePageFlip();

    void handleRefresh();
    void handleWorkList(const DisplayHardware& hw);
    void handleRepaint(const DisplayHardware& hw);
    void handleRepaint(const DisplayHardware& hw, const Region& dirtyRegion);

    /* ------------------------------------------------------------------------
     * Transactions
@@ -319,12 +315,16 @@ private:
    uint32_t getMaxViewportDims() const;

    /* ------------------------------------------------------------------------
     * Display management
     * Display and layer stack management
     */
    const DisplayHardware& getDisplayHardware(DisplayID dpy) const {
        return *mDisplayHardwares[dpy];
    }

    // mark a region of a layer stack dirty. this updates the dirty
    // region of all screens presenting this layer stack.
    void invalidateLayerStack(uint32_t layerStack, const Region& dirty);

    /* ------------------------------------------------------------------------
     * H/W composer
     */
@@ -336,13 +336,12 @@ private:
     */
    void invalidateHwcGeometry();
    void computeVisibleRegions(const LayerVector& currentLayers,
        Region& dirtyRegion, Region& wormholeRegion);
            uint32_t layerStack,
            Region& dirtyRegion, Region& opaqueRegion);
    void postFramebuffer();
    void setupHardwareComposer(const DisplayHardware& hw);
    void composeSurfaces(const DisplayHardware& hw, const Region& dirty);
    void setInvalidateRegion(const Region& reg);
    Region getAndClearInvalidateRegion();
    void drawWormhole() const;
    void drawWormhole(const Region& region) const;
    GLuint getProtectedTexName() const {
        return mProtectedTexName;
    }
@@ -350,7 +349,7 @@ private:
    /* ------------------------------------------------------------------------
     * Debugging & dumpsys
     */
    void debugFlashRegions(const DisplayHardware& hw);
    void debugFlashRegions(const DisplayHardware& hw, const Region& dirtyReg);
    void listLayersLocked(const Vector<String16>& args, size_t& index,
        String8& result, char* buffer, size_t SIZE) const;
    void dumpStatsLocked(const Vector<String16>& args, size_t& index,
@@ -377,12 +376,10 @@ private:
    bool mLayersRemoved;

    // access must be protected by mInvalidateLock
    mutable Mutex mInvalidateLock;
    Region mInvalidateRegion;
    volatile int32_t mRepaintEverything;

    // constant members (no synchronization needed for access)
    HWComposer* mHwc;
    GLuint mWormholeTexName;
    GLuint mProtectedTexName;
    nsecs_t mBootTime;
    sp<EventThread> mEventThread;
@@ -395,10 +392,6 @@ private:
    // Can only accessed from the main thread, these members
    // don't need synchronization
    State mDrawingState;
    Region mDirtyRegion;
    Region mDirtyRegionRemovedLayer;
    Region mSwapRegion;
    Region mWormholeRegion;
    bool mVisibleRegionsDirty;
    bool mHwWorkListDirty;
    int32_t mElectronBeamAnimationMode;