美文网首页一路向下之AOSP研究
Android-View绘制原理(18)-GrTexture

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

作者: 代码多哥 | 来源:发表于2023-09-07 18:42 被阅读0次

    GrSurface有两个主要的子类,一个GrRenderTarget, 上一篇文章已经分析过,它包装的是一个GrBackendRenderTarget,另外一个兄弟就是GrTexture,它代表的是GPU上的一个纹理,同时GrTexture也有配套的代理类GrTextureProxy。GrTextureProxy继承自GrSurfaceProxy。本文继续来研究GrTexture的生成和初始化相关的逻辑

    1. 创建对象

    还是回到前面已经分析过的代码
    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));
    

    这里生成了一个SkImageInfo描述对象,包括宽高,颜色,透明等,然后调用kSurface::MakeRenderTarget来生成一个GrTextureProxy,这与前面的wrap方式是不一样的。这里会进入到Surface_Gpu的实现
    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 传入的参数为0, budgeted 为 SkBudgeted::kYes, 在SkGpuDevice::Make内部将生成一个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));
    }
    

    再MakeSurfaceDrawContext时将会调用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);
    }
    

    传了几个参数,fit传入的是SkBackingFit::kExact,表示大小是精确的,GrProtected::kNo内容时不受保护的
    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模式一样,进入到了proxyProvider,只是这里调用的是的createProxy方法, 第三个参数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()));
    }
    

    因为renderable为GrRenderable::kYes,因此构造的是一个GrTextureRenderTargetProxy对象,它同时继承了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);
    }
    

    这里没有开到给fTarget赋值的逻辑,因此这个过程仅仅是生成了代理对象,还没有生成被代理的GrSurface

    2. 初始化

    GrTextureProxy创建的是时候它的fTarget是没有值的,而是在flush的时候,才会对GrSurface进行初始化,我们先直接分析一下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;
    }
    

    它接受一个resourceProvider是GPU资源提供者,方法内部调用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;
    }
    

    通过调用createSurfaceImpl方法来创建一个GrSurface,然后调用assign方法赋值给fTarget,这时这个Proxy所代理的GrSurface就存在了

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

    进一步看一下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;
    }
    

    它进一步调用resourceProvider->createTexture方法来创建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);
    }
    

    调用 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;
    }
    

    接着调用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中实现

    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);
    }
    
    

    这个方法很复杂,有很多的OpenGL操作,这里都忽略了,有兴趣的同学可以去源码查看。它内部调用createTexture方法,返回GPU上纹理的Id,这个Id保存到了texDesc.fID ,然后在以texDesc等为参数,构造出一个GrGLTextureRenderTarget。所以fTarget的值就是这个GrGLTextureRenderTarget类型的对象,它内部保存了纹理的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;
    }
    

    调用glGenTextures接口创建GPU上的纹理对象,然后绑定到目标GR_GL_TEXTURE_2D,也就是一个GrSurface对应的是一个二维纹理,然后在该纹理上分配一个TexStorage2D或者TexImage2D纹理图像,对应着GPU上的显存。 返回的结果是纹理的id并被GrTexure持有。

    3. 总结

    本文分析了GrTexture的相关的知识点,包含创建GrTextureProxy的流程以及通过这个GrTextureProxy去初始化一个GrTexture的流程,初始化就是调用OpenGL的接口在GPU去创建分配纹理图像的过程,纹理的Id将保存在创建出来的GrGLTextureRenderTarget对象中。到这里,代理类的GrSurface成员fTarget就创建出来了,对应的Gpu显存也申请好了,可以开始向GPU提交绘制命令了。

    相关文章

      网友评论

        本文标题:Android-View绘制原理(18)-GrTexture

        本文链接:https://www.haomeiwen.com/subject/rykrvdtx.html