显示HWC合成(hwc_display_contents_1_t,hwc_layer_1_t数据结构关系)

在之前的博客中,分析了SurfaceFlinger的流程,以及hwc和Gralloc模块的大致功能,但是对其中一些数据结构不是很清楚. 这篇博客我们主要从创建hwc_display_contents_1_t数据结构入手开始对hal层 hwc的一些数据结构进行分析。

创建hwc_display_contents_1_t

我们先来看看setUpHWComposer函数中如下代码,会遍历各个display(每个显示设备),然后调用HWComposer的createWorkList函数,并且把当前layer的数量count作为参数。后面还会调用hwc的prepare函数。

    ......
    HWComposer& hwc(getHwComposer());
    if (hwc.initCheck() == NO_ERROR) {
        // build the h/w work list
        if (CC_UNLIKELY(mHwWorkListDirty)) {
            mHwWorkListDirty = false;
            for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
                sp<const DisplayDevice> hw(mDisplays[dpy]);
                const int32_t id = hw->getHwcDisplayId();
                if (id >= 0) {
                    const Vector< sp<Layer> >& currentLayers(
                        hw->getVisibleLayersSortedByZ());
                    const size_t count = currentLayers.size();
                    if (hwc.createWorkList(id, count) == NO_ERROR) {
                        ......
                    }
                }
            }
        }
    ......
    status_t err = hwc.prepare();
    ......

我们先来看DisplayData数据结构,它就代表一个显示设备的数据。其中list变量(hwc_display_contents_1_t类型)包括这个显示设备上所有的layer数据,layer数据放在hwLayers中。这个list的最后一个就是framebufferTarget(gpu合成之后的layer),然后在DisplayData数据结构中单独给了一个变量framebufferTarget。

    struct DisplayData {
        DisplayData();
        ~DisplayData();
        Vector<DisplayConfig> configs;
        size_t currentConfig;
        uint32_t format;    // pixel format from FB hal, for pre-hwc-1.1
        bool connected;
        bool hasFbComp;
        bool hasOvComp;
        size_t capacity;
        hwc_display_contents_1* list;//各个layer
        hwc_layer_1* framebufferTarget;//gpu合成之后的layer
        buffer_handle_t fbTargetHandle;
        sp<Fence> lastRetireFence;  // signals when the last set op retires
        sp<Fence> lastDisplayFence; // signals when the last set op takes
                                    // effect on screen
        buffer_handle_t outbufHandle;
        sp<Fence> outbufAcquireFence;

        // protected by mEventControlLock
        int32_t events;
    };

createWorkList函数先是计算我们要申请layer的内存大小,然后通过malloc申请内存,并且地址赋给disp.list.后面我们把disp.list->hwLayers的最后一个其实就是gpu合成之后的layer framebufferTarget。当然我们也就把这个hwc_layer_1赋给了disp.framebufferTarget.

status_t HWComposer::createWorkList(int32_t id, size_t numLayers) {
    if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
        return BAD_INDEX;
    }

    if (mHwc) {
        DisplayData& disp(mDisplayData[id]);
        if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
            // we need space for the HWC_FRAMEBUFFER_TARGET
            numLayers++;
        }
        if (disp.capacity < numLayers || disp.list == NULL) {
            size_t size = sizeof(hwc_display_contents_1_t)
                    + numLayers * sizeof(hwc_layer_1_t);//申请内存的大小
            free(disp.list);
            disp.list = (hwc_display_contents_1_t*)malloc(size);//malloc内存
            disp.capacity = numLayers;
        }
        if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
            disp.framebufferTarget = &disp.list->hwLayers[numLayers - 1];//list的hwLayers最后一个就是framebufferTarget
            memset(disp.framebufferTarget, 0, sizeof(hwc_layer_1_t));
            const DisplayConfig& currentConfig =
                    disp.configs[disp.currentConfig];
            const hwc_rect_t r = { 0, 0,
                    (int) currentConfig.width, (int) currentConfig.height };
            disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET;//类型变成target的
            disp.framebufferTarget->hints = 0;
            disp.framebufferTarget->flags = 0;
            disp.framebufferTarget->handle = disp.fbTargetHandle;
            disp.framebufferTarget->transform = 0;
            disp.framebufferTarget->blending = HWC_BLENDING_PREMULT;
            if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
                disp.framebufferTarget->sourceCropf.left = 0;
                disp.framebufferTarget->sourceCropf.top = 0;
                disp.framebufferTarget->sourceCropf.right =
                        currentConfig.width;
                disp.framebufferTarget->sourceCropf.bottom =
                        currentConfig.height;
            } else {
                disp.framebufferTarget->sourceCrop = r;
            }
            disp.framebufferTarget->displayFrame = r;
            disp.framebufferTarget->visibleRegionScreen.numRects = 1;
            disp.framebufferTarget->visibleRegionScreen.rects =
                &disp.framebufferTarget->displayFrame;
            disp.framebufferTarget->acquireFenceFd = -1;
            disp.framebufferTarget->releaseFenceFd = -1;
            disp.framebufferTarget->planeAlpha = 0xFF;
        }
        disp.list->retireFenceFd = -1;
        disp.list->flags = HWC_GEOMETRY_CHANGED;
        disp.list->numHwLayers = numLayers;
    }
    return NO_ERROR;
}
hwc_display_contents_1的数据结构如下,代表的是一个设备的数据。

typedef struct hwc_display_contents_1 {
    /* File descriptor referring to a Sync HAL fence object which will signal
     * when this composition is retired. For a physical display, a composition
     * is retired when it has been replaced on-screen by a subsequent set. For
     * a virtual display, the composition is retired when the writes to
     * outputBuffer are complete and can be read. The fence object is created
     * and returned by the set call; this field will be -1 on entry to prepare
     * and set. SurfaceFlinger will close the returned file descriptor.
     */
    int retireFenceFd;

    union {
        /* Fields only relevant for HWC_DEVICE_VERSION_1_0. */
        struct {
            /* (dpy, sur) is the target of SurfaceFlinger's OpenGL ES
             * composition for HWC_DEVICE_VERSION_1_0. They aren't relevant to
             * prepare. The set call should commit this surface atomically to
             * the display along with any overlay layers.
             */
            hwc_display_t dpy;
            hwc_surface_t sur;
        };

        /* These fields are used for virtual displays when the h/w composer
         * version is at least HWC_DEVICE_VERSION_1_3. */
        struct {
            /* outbuf is the buffer that receives the composed image for
             * virtual displays. Writes to the outbuf must wait until
             * outbufAcquireFenceFd signals. A fence that will signal when
             * writes to outbuf are complete should be returned in
             * retireFenceFd.
             *
             * This field is set before prepare(), so properties of the buffer
             * can be used to decide which layers can be handled by h/w
             * composer.
             *
             * If prepare() sets all layers to FRAMEBUFFER, then GLES
             * composition will happen directly to the output buffer. In this
             * case, both outbuf and the FRAMEBUFFER_TARGET layer's buffer will
             * be the same, and set() has no work to do besides managing fences.
             *
             * If the TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS board config
             * variable is defined (not the default), then this behavior is
             * changed: if all layers are marked for FRAMEBUFFER, GLES
             * composition will take place to a scratch framebuffer, and
             * h/w composer must copy it to the output buffer. This allows the
             * h/w composer to do format conversion if there are cases where
             * that is more desirable than doing it in the GLES driver or at the
             * virtual display consumer.
             *
             * If some or all layers are marked OVERLAY, then the framebuffer
             * and output buffer will be different. As with physical displays,
             * the framebuffer handle will not change between frames if all
             * layers are marked for OVERLAY.
             */
            buffer_handle_t outbuf;

            /* File descriptor for a fence that will signal when outbuf is
             * ready to be written. The h/w composer is responsible for closing
             * this when no longer needed.
             *
             * Will be -1 whenever outbuf is NULL, or when the outbuf can be
             * written immediately.
             */
            int outbufAcquireFenceFd;
        };
    };

    /* List of layers that will be composed on the display. The buffer handles
     * in the list will be unique. If numHwLayers is 0, all composition will be
     * performed by SurfaceFlinger.
     */
    uint32_t flags;
    size_t numHwLayers;//layer的个数
    hwc_layer_1_t hwLayers[0];//各个layer的起始指针

} hwc_display_contents_1_t;

再来看看hwc_layer_1 的数据结构

typedef struct hwc_layer_1 {
    /*
     * compositionType is used to specify this layer's type and is set by either
     * the hardware composer implementation, or by the caller (see below).
     *
     *  This field is always reset to HWC_BACKGROUND or HWC_FRAMEBUFFER
     *  before (*prepare)() is called when the HWC_GEOMETRY_CHANGED flag is
     *  also set, otherwise, this field is preserved between (*prepare)()
     *  calls.
     *
     * HWC_BACKGROUND
     *   Always set by the caller before calling (*prepare)(), this value
     *   indicates this is a special "background" layer. The only valid field
     *   is backgroundColor.
     *   The HWC can toggle this value to HWC_FRAMEBUFFER to indicate it CANNOT
     *   handle the background color.
     *
     *
     * HWC_FRAMEBUFFER_TARGET
     *   Always set by the caller before calling (*prepare)(), this value
     *   indicates this layer is the framebuffer surface used as the target of
     *   OpenGL ES composition. If the HWC sets all other layers to HWC_OVERLAY
     *   or HWC_BACKGROUND, then no OpenGL ES composition will be done, and
     *   this layer should be ignored during set().
     *
     *   This flag (and the framebuffer surface layer) will only be used if the
     *   HWC version is HWC_DEVICE_API_VERSION_1_1 or higher. In older versions,
     *   the OpenGL ES target surface is communicated by the (dpy, sur) fields
     *   in hwc_compositor_device_1_t.
     *
     *   This value cannot be set by the HWC implementation.
     *
     *
     * HWC_FRAMEBUFFER
     *   Set by the caller before calling (*prepare)() ONLY when the
     *   HWC_GEOMETRY_CHANGED flag is also set.
     *
     *   Set by the HWC implementation during (*prepare)(), this indicates
     *   that the layer will be drawn into the framebuffer using OpenGL ES.
     *   The HWC can toggle this value to HWC_OVERLAY to indicate it will
     *   handle the layer.
     *
     *
     * HWC_OVERLAY
     *   Set by the HWC implementation during (*prepare)(), this indicates
     *   that the layer will be handled by the HWC (ie: it must not be
     *   composited with OpenGL ES).
     *
     *
     * HWC_SIDEBAND
     *   Set by the caller before calling (*prepare)(), this value indicates
     *   the contents of this layer come from a sideband video stream.
     *
     *   The h/w composer is responsible for receiving new image buffers from
     *   the stream at the appropriate time (e.g. synchronized to a separate
     *   audio stream), compositing them with the current contents of other
     *   layers, and displaying the resulting image. This happens
     *   independently of the normal prepare/set cycle. The prepare/set calls
     *   only happen when other layers change, or when properties of the
     *   sideband layer such as position or size change.
     *
     *   If the h/w composer can't handle the layer as a sideband stream for
     *   some reason (e.g. unsupported scaling/blending/rotation, or too many
     *   sideband layers) it can set compositionType to HWC_FRAMEBUFFER in
     *   (*prepare)(). However, doing so will result in the layer being shown
     *   as a solid color since the platform is not currently able to composite
     *   sideband layers with the GPU. This may be improved in future
     *   versions of the platform.
     *
     *
     * HWC_CURSOR_OVERLAY
     *   Set by the HWC implementation during (*prepare)(), this value
     *   indicates the layer's composition will now be handled by the HWC.
     *   Additionally, the client can now asynchronously update the on-screen
     *   position of this layer using the setCursorPositionAsync() api.
     */
    int32_t compositionType;//注意上面注释

    /*
     * hints is bit mask set by the HWC implementation during (*prepare)().
     * It is preserved between (*prepare)() calls, unless the
     * HWC_GEOMETRY_CHANGED flag is set, in which case it is reset to 0.
     *
     * see hwc_layer_t::hints
     */
    uint32_t hints;

    /* see hwc_layer_t::flags */
    uint32_t flags;

    union {
        /* color of the background.  hwc_color_t.a is ignored */
        hwc_color_t backgroundColor;

        struct {
            union {
                /* When compositionType is HWC_FRAMEBUFFER, HWC_OVERLAY,
                 * HWC_FRAMEBUFFER_TARGET, this is the handle of the buffer to
                 * compose. This handle is guaranteed to have been allocated
                 * from gralloc using the GRALLOC_USAGE_HW_COMPOSER usage flag.
                 * If the layer's handle is unchanged across two consecutive
                 * prepare calls and the HWC_GEOMETRY_CHANGED flag is not set
                 * for the second call then the HWComposer implementation may
                 * assume that the contents of the buffer have not changed. */
                buffer_handle_t handle;//放数据的变量,下篇博客分析

                /* When compositionType is HWC_SIDEBAND, this is the handle
                 * of the sideband video stream to compose. */
                const native_handle_t* sidebandStream;
            };

            /* transformation to apply to the buffer during composition */
            uint32_t transform;

            /* blending to apply during composition */
            int32_t blending;

            ......

            /* where to composite the sourceCrop onto the display. The sourceCrop
             * is scaled using linear filtering to the displayFrame. The origin is the
             * top-left corner of the screen.
             */
            hwc_rect_t displayFrame;

            /* visible region in screen space. The origin is the
             * top-left corner of the screen.
             * The visible region INCLUDES areas overlapped by a translucent layer.
             */
            hwc_region_t visibleRegionScreen;

            /* Sync fence object that will be signaled when the buffer's
             * contents are available. May be -1 if the contents are already
             * available. This field is only valid during set(), and should be
             * ignored during prepare(). The set() call must not wait for the
             * fence to be signaled before returning, but the HWC must wait for
             * all buffers to be signaled before reading from them.
             *
             * HWC_FRAMEBUFFER layers will never have an acquire fence, since
             * reads from them are complete before the framebuffer is ready for
             * display.
             *
             * HWC_SIDEBAND layers will never have an acquire fence, since
             * synchronization is handled through implementation-defined
             * sideband mechanisms.
             *
             * The HWC takes ownership of the acquireFenceFd and is responsible
             * for closing it when no longer needed.
             */
            int acquireFenceFd;

            /* During set() the HWC must set this field to a file descriptor for
             * a sync fence object that will signal after the HWC has finished
             * reading from the buffer. The field is ignored by prepare(). Each
             * layer should have a unique file descriptor, even if more than one
             * refer to the same underlying fence object; this allows each to be
             * closed independently.
             *
             * If buffer reads can complete at significantly different times,
             * then using independent fences is preferred. For example, if the
             * HWC handles some layers with a blit engine and others with
             * overlays, then the blit layers can be reused immediately after
             * the blit completes, but the overlay layers can't be reused until
             * a subsequent frame has been displayed.
             *
             * Since HWC doesn't read from HWC_FRAMEBUFFER layers, it shouldn't
             * produce a release fence for them. The releaseFenceFd will be -1
             * for these layers when set() is called.
             *
             * Since HWC_SIDEBAND buffers don't pass through the HWC client,
             * the HWC shouldn't produce a release fence for them. The
             * releaseFenceFd will be -1 for these layers when set() is called.
             *
             * The HWC client taks ownership of the releaseFenceFd and is
             * responsible for closing it when no longer needed.
             */
            int releaseFenceFd;

            ......

            /*
             * Availability: HWC_DEVICE_API_VERSION_1_5
             *
             * This defines the region of the source buffer that has been
             * modified since the last frame.
             *
             * If surfaceDamage.numRects > 0, then it may be assumed that any
             * portion of the source buffer not covered by one of the rects has
             * not been modified this frame. If surfaceDamage.numRects == 0,
             * then the whole source buffer must be treated as if it had been
             * modified.
             *
             * If the layer's contents are not modified relative to the prior
             * prepare/set cycle, surfaceDamage will contain exactly one empty
             * rect ([0, 0, 0, 0]).
             *
             * The damage rects are relative to the pre-transformed buffer, and
             * their origin is the top-left corner.
             */
            hwc_region_t surfaceDamage;
        };
    };

......

} hwc_layer_1_t;

我们再来看HWComposer的prepare函数,这个函数主要是调用了HWC模块的prepare函数,然后根据disp.list中各个layer数据再修改disp中相应的变量,比如hasOvComp是否有需要hwc合成,hasFbComp是否需要gpu合成。而HWComposer的prepare函数主要就是将对应的图层的类型设置为HWC_FRAMEBUFFER代表就是需要GPU合成的意思。

还有就是HWComposer中的mList就是对应各个显示设备,是DisplayData的list变量就是hwc_display_contents_1

status_t HWComposer::prepare() {
    Mutex::Autolock _l(mDisplayLock);
    for (size_t i=0 ; i<mNumDisplays ; i++) {
        DisplayData& disp(mDisplayData[i]);
        if (disp.framebufferTarget) {
            // make sure to reset the type to HWC_FRAMEBUFFER_TARGET
            // DO NOT reset the handle field to NULL, because it's possible
            // that we have nothing to redraw (eg: eglSwapBuffers() not called)
            // in which case, we should continue to use the same buffer.
            LOG_FATAL_IF(disp.list == NULL);
            disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET;//将framebufferTarget的类型赋值
        }
        if (!disp.connected && disp.list != NULL) {
            ALOGW("WARNING: disp %zu: connected, non-null list, layers=%zu",
                  i, disp.list->numHwLayers);
        }
        mLists[i] = disp.list;//mLists中的数据就是对应各个显示设备的hwc_display_contents_1
        if (mLists[i]) {
            if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
                mLists[i]->outbuf = disp.outbufHandle;
                mLists[i]->outbufAcquireFenceFd = -1;
            } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
                // garbage data to catch improper use
                mLists[i]->dpy = (hwc_display_t)0xDEADBEEF;
                mLists[i]->sur = (hwc_surface_t)0xDEADBEEF;
            } else {
                mLists[i]->dpy = EGL_NO_DISPLAY;
                mLists[i]->sur = EGL_NO_SURFACE;
            }
        }
    }

    int err = mHwc->prepare(mHwc, mNumDisplays, mLists);//调用hwc的prepare函数

    if (err == NO_ERROR) {
        for (size_t i=0 ; i<mNumDisplays ; i++) {
            DisplayData& disp(mDisplayData[i]);
            disp.hasFbComp = false;
            disp.hasOvComp = false;
            if (disp.list) {
                for (size_t i=0 ; i<disp.list->numHwLayers ; i++) {
                    hwc_layer_1_t& l = disp.list->hwLayers[i];//遍历各个layer,设置disp的各个变量

                    if (l.flags & HWC_SKIP_LAYER) {
                        l.compositionType = HWC_FRAMEBUFFER;
                    }
                    if (l.compositionType == HWC_FRAMEBUFFER) {
                        disp.hasFbComp = true;
                    }
                    if (l.compositionType == HWC_OVERLAY) {
                        disp.hasOvComp = true;
                    }
                    if (l.compositionType == HWC_CURSOR_OVERLAY) {
                        disp.hasOvComp = true;
                    }
                }
                if (disp.list->numHwLayers == (disp.framebufferTarget ? 1 : 0)) {
                    disp.hasFbComp = true;
                }
            } else {
                disp.hasFbComp = true;
            }
        }
    }
    return (status_t)err;
}


egl合成

http://blog.csdn.net/kc58236582/article/details/52868973#t1博客中,我们分析过GPU合成各个layer的流程。我们直接从egl合成好数据好之后开始分析,就到FramebufferSurface的onFrameAvailable函数。这个函数直接调用了HWComposer的fbPost函数。

void FramebufferSurface::onFrameAvailable(const BufferItem& /* item */) {
    sp<GraphicBuffer> buf;
    sp<Fence> acquireFence;
    status_t err = nextBuffer(buf, acquireFence);
    if (err != NO_ERROR) {
        ALOGE("error latching nnext FramebufferSurface buffer: %s (%d)",
                strerror(-err), err);
        return;
    }
    err = mHwc.fbPost(mDisplayType, acquireFence, buf);
    if (err != NO_ERROR) {
        ALOGE("error posting framebuffer: %d", err);
    }
}
fbPost函数我们这里是调用了setFramebufferTarget函数。

int HWComposer::fbPost(int32_t id,
        const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) {
    if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
        return setFramebufferTarget(id, acquireFence, buffer);
    } else {
        acquireFence->waitForever("HWComposer::fbPost");
        return mFbDev->post(mFbDev, buffer->handle);
    }
}

setFramebufferTarget函数主要是将Target的handle对象给Framework层的DisplayData的fbTargetHandle

status_t HWComposer::setFramebufferTarget(int32_t id,
        const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf) {
    if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
        return BAD_INDEX;
    }
    DisplayData& disp(mDisplayData[id]);
    if (!disp.framebufferTarget) {
        // this should never happen, but apparently eglCreateWindowSurface()
        // triggers a Surface::queueBuffer()  on some
        // devices (!?) -- log and ignore.
        ALOGE("HWComposer: framebufferTarget is null");
        return NO_ERROR;
    }

    int acquireFenceFd = -1;
    if (acquireFence->isValid()) {
        acquireFenceFd = acquireFence->dup();
    }

    // ALOGD("fbPost: handle=%p, fence=%d", buf->handle, acquireFenceFd);
    disp.fbTargetHandle = buf->handle;//将target的buffer的handle给DisplayData的fbTargetHandle
    disp.framebufferTarget->handle = disp.fbTargetHandle;
    disp.framebufferTarget->acquireFenceFd = acquireFenceFd;
    return NO_ERROR;
}


HWC输出到显示设备

最后在SurfaceFlinger的doComposition函数中会调用postFramebuffer函数,这个函数主要是调用了HWComposer的commit函数,就到HWC模块来最后到显示设备上。

status_t HWComposer::commit() {
    int err = NO_ERROR;
    if (mHwc) {
        if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
            // On version 1.0, the OpenGL ES target surface is communicated
            // by the (dpy, sur) fields and we are guaranteed to have only
            // a single display.
            mLists[0]->dpy = eglGetCurrentDisplay();
            mLists[0]->sur = eglGetCurrentSurface(EGL_DRAW);
        }

        for (size_t i=VIRTUAL_DISPLAY_ID_BASE; i<mNumDisplays; i++) {//虚拟设备,需要outbuf和outbufAcquireFenceFd 
            DisplayData& disp(mDisplayData[i]);
            if (disp.outbufHandle) {
                mLists[i]->outbuf = disp.outbufHandle;
                mLists[i]->outbufAcquireFenceFd =
                        disp.outbufAcquireFence->dup();
            }
        }

        err = mHwc->set(mHwc, mNumDisplays, mLists);//调用HWC的set函数,输出到显示设备

        for (size_t i=0 ; i<mNumDisplays ; i++) {
            DisplayData& disp(mDisplayData[i]);
            disp.lastDisplayFence = disp.lastRetireFence;
            disp.lastRetireFence = Fence::NO_FENCE;
            if (disp.list) {
                if (disp.list->retireFenceFd != -1) {
                    disp.lastRetireFence = new Fence(disp.list->retireFenceFd);
                    disp.list->retireFenceFd = -1;
                }
                disp.list->flags &= ~HWC_GEOMETRY_CHANGED;
            }
        }
    }
    return (status_t)err;
}






  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值