Android View绘制原理-GrTexture

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

1. 创建对象

还是回到前面已经分析过的代码
frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp

java 复制代码
        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

java 复制代码
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

java 复制代码
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方法

java 复制代码
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

java 复制代码
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,

java 复制代码
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。

java 复制代码
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

java 复制代码
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

java 复制代码
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就存在了

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

进一步看一下createSurfaceImpl方法,如下:

java 复制代码
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

java 复制代码
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

java 复制代码
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方法

java 复制代码
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

java 复制代码
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

java 复制代码
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方法如下:

java 复制代码
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老皮!!!欢迎大家来找我探讨交流👀

相关推荐
li_liuliu22 分钟前
Android4.4 在系统中添加自己的System Service
android
C4rpeDime3 小时前
自建MD5解密平台-续
android
鲤籽鲲4 小时前
C# Random 随机数 全面解析
android·java·c#
m0_548514778 小时前
2024.12.10——攻防世界Web_php_include
android·前端·php
凤邪摩羯8 小时前
Android-性能优化-03-启动优化-启动耗时
android
凤邪摩羯8 小时前
Android-性能优化-02-内存优化-LeakCanary原理解析
android
喀什酱豆腐9 小时前
Handle
android
m0_7482329210 小时前
Android Https和WebView
android·网络协议·https
m0_7482517211 小时前
Android webview 打开本地H5项目(Cocos游戏以及Unity游戏)
android·游戏·unity
m0_7482546613 小时前
go官方日志库带色彩格式化
android·开发语言·golang