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

相关推荐
sky北城1 小时前
You are not able to choose some of the languages, because locales for them a
android
儿歌八万首1 小时前
Jetpack Compose 实战:打造高性能轮播图 (Carousel) 组件
android·前端·kotlin
QING6181 小时前
Kotlin Flow 防抖(Debounce)详解
android·kotlin·android jetpack
QING6181 小时前
Kotlin Flow 防抖(Debounce)、节流(Throttle)、去重(distinctUntilChanged) —— 新手指南
android·kotlin·android jetpack
AI视觉网奇2 小时前
android yolo12 android 实战笔记
android·笔记·yolo
海上飞猪2 小时前
【Mysql】Mysql的安装部署和使用
android·mysql·adb
我是好小孩2 小时前
【Android】项目的组件化搭建
android
aqi003 小时前
FFmpeg开发笔记(九十四)基于Kotlin的国产开源推拉流框架anyRTC
android·ffmpeg·kotlin·音视频·直播·流媒体
马 孔 多 在下雨3 小时前
Android 组件化开发基础实践
android
技术摆渡人3 小时前
Android 系统技术探索(2)构建大脑(System Services & PMS)
android