Android-View繪制原理(18)-GrTexture

GrSurface有兩個(gè)主要的子類,一個(gè)GrRenderTarget, 上一篇文章已經(jīng)分析過(guò),它包裝的是一個(gè)GrBackendRenderTarget,另外一個(gè)兄弟就是GrTexture,它代表的是GPU上的一個(gè)紋理,同時(shí)GrTexture也有配套的代理類GrTextureProxy。GrTextureProxy繼承自GrSurfaceProxy。本文繼續(xù)來(lái)研究GrTexture的生成和初始化相關(guān)的邏輯

1. 創(chuàng)建對(duì)象

還是回到前面已經(jīng)分析過(guò)的代碼
frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp

        SkImageInfo info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),
                                 kPremul_SkAlphaType, getSurfaceColorSpace());
        SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
        SkASSERT(mRenderThread.getGrContext() != nullptr);
        node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
                                                          SkBudgeted::kYes, info, 0,
                                                          this->getSurfaceOrigin(), &props));

這里生成了一個(gè)SkImageInfo描述對(duì)象,包括寬高,顏色,透明等,然后調(diào)用kSurface::MakeRenderTarget來(lái)生成一個(gè)GrTextureProxy,這與前面的wrap方式是不一樣的。這里會(huì)進(jìn)入到Surface_Gpu的實(shí)現(xiàn)
external/skia/src/image/SkSurface_Gpu.cpp

sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrRecordingContext* ctx, SkBudgeted budgeted,
                                             const SkImageInfo& info, int sampleCount,
                                             GrSurfaceOrigin origin, const SkSurfaceProps* props,
                                             bool shouldCreateWithMips) {
   ...
    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(
            ctx, budgeted, info, sampleCount, origin, props, mipMapped,
            SkGpuDevice::kClear_InitContents));
    if (!device) {
        return nullptr;
    }
    return sk_make_sp<SkSurface_Gpu>(std::move(device));
}

這里sampleCount 傳入的參數(shù)為0, budgeted 為 SkBudgeted::kYes, 在SkGpuDevice::Make內(nèi)部將生成一個(gè)surfaceDrawContext

sk_sp<SkGpuDevice> SkGpuDevice::Make(GrRecordingContext* context, SkBudgeted budgeted,
                                     const SkImageInfo& info, int sampleCount,
                                     GrSurfaceOrigin origin, const SkSurfaceProps* props,
                                     GrMipmapped mipMapped, InitContents init) {
   ...
    auto surfaceDrawContext =
            MakeSurfaceDrawContext(context, budgeted, info, sampleCount, origin, props, mipMapped);
    if (!surfaceDrawContext) {
        return nullptr;
    }

    return sk_sp<SkGpuDevice>(new SkGpuDevice(std::move(surfaceDrawContext), flags));
}

再M(fèi)akeSurfaceDrawContext時(shí)將會(huì)調(diào)用GrSurfaceDrawContext的Make方法


std::unique_ptr<GrSurfaceDrawContext> SkGpuDevice::MakeSurfaceDrawContext(
        GrRecordingContext* context,
        SkBudgeted budgeted,
        const SkImageInfo& origInfo,
        int sampleCount,
        GrSurfaceOrigin origin,
        const SkSurfaceProps* surfaceProps,
        GrMipmapped mipmapped) {
    if (!context) {
        return nullptr;
    }

    return GrSurfaceDrawContext::Make(
            context, SkColorTypeToGrColorType(origInfo.colorType()), origInfo.refColorSpace(),
            SkBackingFit::kExact, origInfo.dimensions(), SkSurfacePropsCopyOrDefault(surfaceProps),
            sampleCount, mipmapped, GrProtected::kNo, origin, budgeted);
}

傳了幾個(gè)參數(shù),fit傳入的是SkBackingFit::kExact,表示大小是精確的,GrProtected::kNo內(nèi)容時(shí)不受保護(hù)的
external/skia/src/gpu/GrSurfaceDrawContext.cpp

std::unique_ptr<GrSurfaceDrawContext> GrSurfaceDrawContext::Make(
        GrRecordingContext* context,
        sk_sp<SkColorSpace> colorSpace,
        SkBackingFit fit,
        SkISize dimensions,
        const GrBackendFormat& format,
        int sampleCnt,
        GrMipmapped mipMapped,
        GrProtected isProtected,
        GrSwizzle readSwizzle,
        GrSwizzle writeSwizzle,
        GrSurfaceOrigin origin,
        SkBudgeted budgeted,
        const SkSurfaceProps& surfaceProps) {

    sk_sp<GrTextureProxy> proxy = context->priv().proxyProvider()->createProxy(
            format,
            dimensions,
            GrRenderable::kYes,
            sampleCnt,
            mipMapped,
            fit,
            budgeted,
            isProtected);
    if (!proxy) {
        return nullptr;
    }

    GrSurfaceProxyView readView (           proxy, origin,  readSwizzle);
    GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle);

    auto rtc = std::make_unique<GrSurfaceDrawContext>(context,
                                                      std::move(readView),
                                                      std::move(writeView),
                                                      GrColorType::kUnknown,
                                                      std::move(colorSpace),
                                                      surfaceProps);
    rtc->discard();
    return rtc;
}

和前面wrap模式一樣,進(jìn)入到了proxyProvider,只是這里調(diào)用的是的createProxy方法, 第三個(gè)參數(shù)renderable傳入的是GrRenderable::kYes,

sk_sp<GrTextureProxy> GrProxyProvider::createProxy(const GrBackendFormat& format,
                                                   SkISize dimensions,
                                                   GrRenderable renderable,
                                                   int renderTargetSampleCnt,
                                                   GrMipmapped mipMapped,
                                                   SkBackingFit fit,
                                                   SkBudgeted budgeted,
                                                   GrProtected isProtected,
                                                   GrInternalSurfaceFlags surfaceFlags,
                                                   GrSurfaceProxy::UseAllocator useAllocator) {
    ...
    
    if (renderable == GrRenderable::kYes) {
        renderTargetSampleCnt = caps->getRenderTargetSampleCount(renderTargetSampleCnt, format);
        SkASSERT(renderTargetSampleCnt);
        GrInternalSurfaceFlags extraFlags = caps->getExtraSurfaceFlagsForDeferredRT();
        // We know anything we instantiate later from this deferred path will be
        // both texturable and renderable
        return sk_sp<GrTextureProxy>(new GrTextureRenderTargetProxy(
                *caps, format, dimensions, renderTargetSampleCnt, mipMapped, mipmapStatus, fit,
                budgeted, isProtected, surfaceFlags | extraFlags, useAllocator,
                this->isDDLProvider()));
    }

    return sk_sp<GrTextureProxy>(new GrTextureProxy(format, dimensions, mipMapped, mipmapStatus,
                                                    fit, budgeted, isProtected, surfaceFlags,
                                                    useAllocator, this->isDDLProvider()));
}

因?yàn)閞enderable為GrRenderable::kYes,因此構(gòu)造的是一個(gè)GrTextureRenderTargetProxy對(duì)象,它同時(shí)繼承了GrRenderTargetProxy 和GrTextureProxy。

GrTextureRenderTargetProxy::GrTextureRenderTargetProxy(const GrCaps& caps,
                                                       LazyInstantiateCallback&& callback,
                                                       const GrBackendFormat& format,
                                                       SkISize dimensions,
                                                       int sampleCnt,
                                                       GrMipmapped mipMapped,
                                                       GrMipmapStatus mipmapStatus,
                                                       SkBackingFit fit,
                                                       SkBudgeted budgeted,
                                                       GrProtected isProtected,
                                                       GrInternalSurfaceFlags surfaceFlags,
                                                       UseAllocator useAllocator,
                                                       GrDDLProvider creatingProvider)
        : GrSurfaceProxy(std::move(callback), format, dimensions, fit, budgeted, isProtected,
                         surfaceFlags, useAllocator)
        // Since we have virtual inheritance, we initialize GrSurfaceProxy directly. Send null
        // callbacks to the texture and RT proxies simply to route to the appropriate constructors.
        , GrRenderTargetProxy(LazyInstantiateCallback(), format, dimensions, sampleCnt, fit,
                              budgeted, isProtected, surfaceFlags, useAllocator,
                              WrapsVkSecondaryCB::kNo)
        , GrTextureProxy(LazyInstantiateCallback(), format, dimensions, mipMapped, mipmapStatus,
                         fit, budgeted, isProtected, surfaceFlags, useAllocator,
                         creatingProvider) {
    this->initSurfaceFlags(caps);
}

這里沒(méi)有開到給fTarget賦值的邏輯,因此這個(gè)過(guò)程僅僅是生成了代理對(duì)象,還沒(méi)有生成被代理的GrSurface

2. 初始化

GrTextureProxy創(chuàng)建的是時(shí)候它的fTarget是沒(méi)有值的,而是在flush的時(shí)候,才會(huì)對(duì)GrSurface進(jìn)行初始化,我們先直接分析一下GrTextureProxy的instantiate方法:

external/skia/src/gpu/GrTextureProxy.cpp

bool GrTextureRenderTargetProxy::instantiate(GrResourceProvider* resourceProvider) {
    ...
    if (!this->instantiateImpl(resourceProvider, this->numSamples(), GrRenderable::kYes,
                               this->mipmapped(), key.isValid() ? &key : nullptr)) {
        return false;
    }
   ...
    return true;
}

它接受一個(gè)resourceProvider是GPU資源提供者,方法內(nèi)部調(diào)用instantiateImpl方法,這是父類GrSurfaceProxy中的方法
external/skia/src/gpu/GrSurfaceProxy.cpp

bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
                                     GrRenderable renderable, GrMipmapped mipMapped,
                                     const GrUniqueKey* uniqueKey) {
    SkASSERT(!this->isLazy());
    if (fTarget) {
        if (uniqueKey && uniqueKey->isValid()) {
            SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey);
        }
        return true;
    }

    sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, sampleCnt, renderable,
                                                       mipMapped);
   ...

    this->assign(std::move(surface));

    return true;
}

通過(guò)調(diào)用createSurfaceImpl方法來(lái)創(chuàng)建一個(gè)GrSurface,然后調(diào)用assign方法賦值給fTarget,這時(shí)這個(gè)Proxy所代理的GrSurface就存在了

void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) {
    SkASSERT(!fTarget && surface);
    SkDEBUGCODE(this->validateSurface(surface.get());)
    fTarget = std::move(surface);
}

進(jìn)一步看一下createSurfaceImpl方法,如下:

sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl(GrResourceProvider* resourceProvider,
                                                   int sampleCnt,
                                                   GrRenderable renderable,
                                                   GrMipmapped mipMapped) const {
    ...
    sk_sp<GrSurface> surface;
    ...
    surface = resourceProvider->createTexture(fDimensions, fFormat, renderable, sampleCnt, mipMapped, fBudgeted, fIsProtected);
    }
    if (!surface) {
        return nullptr;
    }

    return surface;
}

它進(jìn)一步調(diào)用resourceProvider->createTexture方法來(lái)創(chuàng)建GrSurface:

external/skia/src/gpu/GrResourceProvider.cpp

sk_sp<GrTexture> GrResourceProvider::createTexture(SkISize dimensions,
                                                   const GrBackendFormat& format,
                                                   GrRenderable renderable,
                                                   int renderTargetSampleCnt,
                                                   GrMipmapped mipmapped,
                                                   SkBudgeted budgeted,
                                                   GrProtected isProtected) {
    ...
    return fGpu->createTexture(dimensions, format, renderable, renderTargetSampleCnt, mipmapped,
                               budgeted, isProtected);
}

調(diào)用 fGpu->createTexture方法:

external/skia/src/gpu/GrGpu.cpp

sk_sp<GrTexture> GrGpu::createTexture(SkISize dimensions,
                                      const GrBackendFormat& format,
                                      GrRenderable renderable,
                                      int renderTargetSampleCnt,
                                      GrMipmapped mipMapped,
                                      SkBudgeted budgeted,
                                      GrProtected isProtected) {
    int mipLevelCount = 1;
    if (mipMapped == GrMipmapped::kYes) {
        mipLevelCount =
                32 - SkCLZ(static_cast<uint32_t>(std::max(dimensions.fWidth, dimensions.fHeight)));
    }
    uint32_t levelClearMask =
            this->caps()->shouldInitializeTextures() ? (1 << mipLevelCount) - 1 : 0;
    auto tex = this->createTextureCommon(dimensions, format, renderable, renderTargetSampleCnt,
                                         budgeted, isProtected, mipLevelCount, levelClearMask);
    if (tex && mipMapped == GrMipmapped::kYes && levelClearMask) {
        tex->markMipmapsClean();
    }
    return tex;
}

接著調(diào)用createTextureCommon方法

sk_sp<GrTexture> GrGpu::createTextureCommon(SkISize dimensions,
                                            const GrBackendFormat& format,
                                            GrRenderable renderable,
                                            int renderTargetSampleCnt,
                                            SkBudgeted budgeted,
                                            GrProtected isProtected,
                                            int mipLevelCount,
                                            uint32_t levelClearMask) {
    ...
    if (renderable == GrRenderable::kYes) {
        renderTargetSampleCnt =
                this->caps()->getRenderTargetSampleCount(renderTargetSampleCnt, format);
    }
   ...
    auto tex = this->onCreateTexture(dimensions,
                                     format,
                                     renderable,
                                     renderTargetSampleCnt,
                                     budgeted,
                                     isProtected,
                                     mipLevelCount,
                                     levelClearMask);
   ...
    return tex;
}

onCreateTexture方法在子類GrGLGpu中實(shí)現(xiàn)

external/skia/src/gpu/gl/GrGLGpu.cpp

sk_sp<GrTexture> GrGLGpu::onCreateTexture(SkISize dimensions,
                                          const GrBackendFormat& format,
                                          GrRenderable renderable,
                                          int renderTargetSampleCnt,
                                          SkBudgeted budgeted,
                                          GrProtected isProtected,
                                          int mipLevelCount,
                                          uint32_t levelClearMask) {
   ...
    switch (format.textureType()) {
        ...
        case GrTextureType::k2D:
            texDesc.fTarget = GR_GL_TEXTURE_2D;
            break;
        ...
    }
    texDesc.fFormat = format.asGLFormat();
    texDesc.fOwnership = GrBackendObjectOwnership::kOwned;
    SkASSERT(texDesc.fFormat != GrGLFormat::kUnknown);
    SkASSERT(!GrGLFormatIsCompressed(texDesc.fForcreateTexturemat));

    texDesc.fID = this->createTexture(dimensions, texDesc.fFormat, texDesc.fTarget, renderable,
                                      &initialState, mipLevelCount);

    ...
   sk_sp<GrGLTexture> tex;
    if (renderable == GrRenderable::kYes) {
        ...
        tex = sk_make_sp<GrGLTextureRenderTarget>(
                this, budgeted, renderTargetSampleCnt, texDesc, rtIDDesc, mipmapStatus);
        tex->baseLevelWasBoundToFBO();
    } else {
        tex = sk_make_sp<GrGLTexture>(this, budgeted, texDesc, mipmapStatus);
    }
    
    return std::move(tex);
}

這個(gè)方法很復(fù)雜,有很多的OpenGL操作,這里都忽略了,有興趣的同學(xué)可以去源碼查看。它內(nèi)部調(diào)用createTexture方法,返回GPU上紋理的Id,這個(gè)Id保存到了texDesc.fID ,然后在以texDesc等為參數(shù),構(gòu)造出一個(gè)GrGLTextureRenderTarget。所以fTarget的值就是這個(gè)GrGLTextureRenderTarget類型的對(duì)象,它內(nèi)部保存了紋理的Id

GrGLTextureRenderTarget::GrGLTextureRenderTarget(GrGLGpu* gpu,
                                                 int sampleCount,
                                                 const GrGLTexture::Desc& texDesc,
                                                 sk_sp<GrGLTextureParameters> parameters,
                                                 const GrGLRenderTarget::IDs& rtIDs,
                                                 GrWrapCacheable cacheable,
                                                 GrMipmapStatus mipmapStatus)
        : GrSurface(gpu, texDesc.fSize, GrProtected::kNo)
        , GrGLTexture(gpu, texDesc, std::move(parameters), mipmapStatus)
        , GrGLRenderTarget(gpu, texDesc.fSize, texDesc.fFormat, sampleCount,
                           rtIDs) {
    this->registerWithCacheWrapped(cacheable);
}

createTexture方法如下:

GrGLuint GrGLGpu::createTexture(SkISize dimensions,
                                GrGLFormat format,
                                GrGLenum target,
                                GrRenderable renderable,
                                GrGLTextureParameters::SamplerOverriddenState* initialState,
                                int mipLevelCount) {
   ...
    GrGLuint id = 0;
    GL_CALL(GenTextures(1, &id));

    this->bindTextureToScratchUnit(target, id);

    if (GrRenderable::kYes == renderable && this->glCaps().textureUsageSupport()) {
        // provides a hint about how this texture will be used
        GL_CALL(TexParameteri(target, GR_GL_TEXTURE_USAGE, GR_GL_FRAMEBUFFER_ATTACHMENT));
    }

    if (initialState) {
        *initialState = set_initial_texture_params(this->glInterface(), target);
    } else {
        set_initial_texture_params(this->glInterface(), target);
    }

    GrGLenum internalFormat = this->glCaps().getTexImageOrStorageInternalFormat(format);

    bool success = false;
    if (internalFormat) {
        if (this->glCaps().formatSupportsTexStorage(format)) {
            auto levelCount = std::max(mipLevelCount, 1);
            GrGLenum error = GL_ALLOC_CALL(TexStorage2D(target, levelCount, internalFormat,
                                                        dimensions.width(), dimensions.height()));
            success = (error == GR_GL_NO_ERROR);
        } else {
            GrGLenum externalFormat, externalType;
            this->glCaps().getTexImageExternalFormatAndType(format, &externalFormat, &externalType);
            GrGLenum error = GR_GL_NO_ERROR;
            if (externalFormat && externalType) {
                for (int level = 0; level < mipLevelCount && error == GR_GL_NO_ERROR; level++) {
                    const int twoToTheMipLevel = 1 << level;
                    const int currentWidth = std::max(1, dimensions.width() / twoToTheMipLevel);
                    const int currentHeight = std::max(1, dimensions.height() / twoToTheMipLevel);
                    error = GL_ALLOC_CALL(TexImage2D(target, level, internalFormat, currentWidth,
                                                     currentHeight, 0, externalFormat, externalType,
                                                     nullptr));
                }
                success = (error == GR_GL_NO_ERROR);
            }
        }
    }
    if (success) {
        return id;
    }
    GL_CALL(DeleteTextures(1, &id));
    return 0;
}

調(diào)用glGenTextures接口創(chuàng)建GPU上的紋理對(duì)象,然后綁定到目標(biāo)GR_GL_TEXTURE_2D,也就是一個(gè)GrSurface對(duì)應(yīng)的是一個(gè)二維紋理,然后在該紋理上分配一個(gè)TexStorage2D或者TexImage2D紋理圖像,對(duì)應(yīng)著GPU上的顯存。 返回的結(jié)果是紋理的id并被GrTexure持有。

3. 總結(jié)

本文分析了GrTexture的相關(guān)的知識(shí)點(diǎn),包含創(chuàng)建GrTextureProxy的流程以及通過(guò)這個(gè)GrTextureProxy去初始化一個(gè)GrTexture的流程,初始化就是調(diào)用OpenGL的接口在GPU去創(chuàng)建分配紋理圖像的過(guò)程,紋理的Id將保存在創(chuàng)建出來(lái)的GrGLTextureRenderTarget對(duì)象中。到這里,代理類的GrSurface成員fTarget就創(chuàng)建出來(lái)了,對(duì)應(yīng)的Gpu顯存也申請(qǐng)好了,可以開始向GPU提交繪制命令了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容