Android-View绘制原理(18)-GrTexture
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提交绘制命令了。