Add wrapBackendTextureAsRenderTarget API

Skia's GrTextureProvider currently exposes two APIs for wrapping backend
objects:
 * wrapBackendTexture - wraps a texture into a GrTexture. Depending on
   flags, this GrTexture can be converted to a GrRenderTarget. Skia
   manages the render target objects it may create to provide a render
   target for the texture. This allows Skia to create stencil buffers
   if needed and manager MSAA resolves.
 * wrapBackendRenderTarget - wraps a FBO into a GrRenderTarget. This
   object cannot be converted to a GrTexture. Skia does not manage
   the render target objects for such a GrRenderTarget, and as such
   cannot attach stencil buffers or perform MSAA resolves on the
   created GrRenderTarget.

Given these two options, wrapBackendTexture provides more versatility
and allows Skia more room for optimization. Chrome currently uses
wrapBackendTexture for this reason.

While these two functions cover most cases, they do not provide a way
for Skia to wrap a texture into a render target (and gain the MSAA and
stencil buffer management), without also creating a GrTexture. This is
problematic in cases where a texture can be bound to a render target,
but cannot be textured from, as is the case in Chrome's limited support
for GL_TEXTURE_RECTANGLE.

To address this, a new function is created:
 * wrapBackendTextureAsRenderTarget - wraps a texture into a
   GrRenderTarget. As with wrapBackendTexture, the created render
   target objects are fully managed by Skia. Unlike wrapBackendTexture
   no GrTexture is created, and the created object will never be
   textured from.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1709163003

Review URL: https://codereview.chromium.org/1709163003
This commit is contained in:
ericrk 2016-02-24 14:49:51 -08:00 committed by Commit bot
parent 0cc2f85a19
commit f7b8b8affe
13 changed files with 173 additions and 12 deletions

View File

@ -108,7 +108,6 @@ public:
return NewFromBackendTexture(ctx, desc, props);
}
/**
* Used to wrap a pre-existing 3D API rendering target as a SkSurface. Skia will not assume
* ownership of the render target and the client must ensure the render target is valid for the
@ -117,6 +116,17 @@ public:
static SkSurface* NewFromBackendRenderTarget(GrContext*, const GrBackendRenderTargetDesc&,
const SkSurfaceProps*);
/**
* Used to wrap a pre-existing 3D API texture as a SkSurface. Skia will treat the texture as
* a rendering target only, but unlike NewFromBackendRenderTarget, Skia will manage and own
* the associated render target objects (but not the provided texture). The kRenderTarget flag
* must be set on GrBackendTextureDesc for this to succeed. Skia will not assume ownership
* of the texture and the client must ensure the texture is valid for the lifetime of the
* SkSurface.
*/
static SkSurface* NewFromBackendTextureAsRenderTarget(
GrContext*, const GrBackendTextureDesc&, const SkSurfaceProps*);
/**
* Return a new surface whose contents will be drawn to an offscreen
* render target, allocated by the surface.
@ -292,6 +302,11 @@ public:
const SkSurfaceProps& props() const { return fProps; }
/**
* Issue any pending surface IO to the current backend 3D API and resolve any surface MSAA.
*/
void prepareForExternalIO();
protected:
SkSurface(int width, int height, const SkSurfaceProps*);
SkSurface(const SkImageInfo&, const SkSurfaceProps*);

View File

@ -98,7 +98,7 @@ public:
* the client will resolve to a texture). Currently wrapped render targets
* always use the kBorrow_GrWrapOwnership semantics.
*
* @return GrTexture object or NULL on failure.
* @return GrRenderTarget object or NULL on failure.
*/
GrRenderTarget* wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc);

View File

@ -166,6 +166,10 @@ GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc, GrWrapOwn
!this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
return nullptr;
}
int maxSize = this->caps()->maxTextureSize();
if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
return nullptr;
}
GrTexture* tex = this->onWrapBackendTexture(desc, ownership);
if (nullptr == tex) {
return nullptr;
@ -189,6 +193,22 @@ GrRenderTarget* GrGpu::wrapBackendRenderTarget(const GrBackendRenderTargetDesc&
return this->onWrapBackendRenderTarget(desc, ownership);
}
GrRenderTarget* GrGpu::wrapBackendTextureAsRenderTarget(const GrBackendTextureDesc& desc,
GrWrapOwnership ownership) {
this->handleDirtyContext();
if (!(desc.fFlags & kRenderTarget_GrBackendTextureFlag)) {
return nullptr;
}
if (!this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
return nullptr;
}
int maxSize = this->caps()->maxTextureSize();
if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
return nullptr;
}
return this->onWrapBackendTextureAsRenderTarget(desc, ownership);
}
GrVertexBuffer* GrGpu::createVertexBuffer(size_t size, bool dynamic) {
this->handleDirtyContext();
GrVertexBuffer* vb = this->onCreateVertexBuffer(size, dynamic);

View File

@ -97,15 +97,20 @@ public:
const void* srcData, size_t rowBytes);
/**
* Implements GrContext::wrapBackendTexture
* Implements GrTextureProvider::wrapBackendTexture
*/
GrTexture* wrapBackendTexture(const GrBackendTextureDesc&, GrWrapOwnership);
/**
* Implements GrContext::wrapBackendTexture
* Implements GrTextureProvider::wrapBackendTexture
*/
GrRenderTarget* wrapBackendRenderTarget(const GrBackendRenderTargetDesc&, GrWrapOwnership);
/**
* Implements GrTextureProvider::wrapBackendTextureAsRenderTarget
*/
GrRenderTarget* wrapBackendTextureAsRenderTarget(const GrBackendTextureDesc&, GrWrapOwnership);
/**
* Creates a vertex buffer.
*
@ -512,6 +517,8 @@ private:
virtual GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&, GrWrapOwnership) = 0;
virtual GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&,
GrWrapOwnership) = 0;
virtual GrRenderTarget* onWrapBackendTextureAsRenderTarget(const GrBackendTextureDesc&,
GrWrapOwnership) = 0;
virtual GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) = 0;
virtual GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) = 0;
virtual GrTransferBuffer* onCreateTransferBuffer(size_t size, TransferType type) = 0;

View File

@ -227,4 +227,11 @@ GrStencilAttachment* GrResourceProvider::attachStencilAttachment(GrRenderTarget*
return rt->renderTargetPriv().getStencilAttachment();
}
GrRenderTarget* GrResourceProvider::wrapBackendTextureAsRenderTarget(
const GrBackendTextureDesc& desc, GrWrapOwnership ownership) {
if (this->isAbandoned()) {
return nullptr;
}
return this->gpu()->wrapBackendTextureAsRenderTarget(desc, ownership);
}

View File

@ -147,6 +147,15 @@ public:
const GrCaps* caps() { return this->gpu()->caps(); }
/**
* Wraps an existing texture with a GrRenderTarget object. This is useful when the provided
* texture has a format that cannot be textured from by Skia, but we want to raster to it.
*
* @return GrRenderTarget object or NULL on failure.
*/
GrRenderTarget* wrapBackendTextureAsRenderTarget(const GrBackendTextureDesc& desc,
GrWrapOwnership = kBorrow_GrWrapOwnership);
private:
const GrIndexBuffer* createInstancedIndexBuffer(const uint16_t* pattern,
int patternSize,

View File

@ -345,6 +345,11 @@ private:
return nullptr;
}
GrRenderTarget* onWrapBackendTextureAsRenderTarget(const GrBackendTextureDesc&,
GrWrapOwnership) override {
return nullptr;
}
GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) override { return nullptr; }
GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) override { return nullptr; }

View File

@ -547,11 +547,6 @@ GrTexture* GrGLGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc,
}
#endif
int maxSize = this->caps()->maxTextureSize();
if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
return nullptr;
}
// next line relies on GrBackendTextureDesc's flags matching GrTexture's
bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrBackendTextureFlag);
@ -657,7 +652,72 @@ GrRenderTarget* GrGLGpu::onWrapBackendRenderTarget(const GrBackendRenderTargetDe
return GrGLRenderTarget::CreateWrapped(this, desc, idDesc, wrapDesc.fStencilBits);
}
GrRenderTarget* GrGLGpu::onWrapBackendTextureAsRenderTarget(const GrBackendTextureDesc& desc,
GrWrapOwnership ownership) {
#ifdef SK_IGNORE_GL_TEXTURE_TARGET
if (!desc.fTextureHandle) {
return nullptr;
}
#else
const GrGLTextureInfo* info = reinterpret_cast<const GrGLTextureInfo*>(desc.fTextureHandle);
if (!info || !info->fID) {
return nullptr;
}
#endif
GrGLTexture::IDDesc idDesc;
GrSurfaceDesc surfDesc;
#ifdef SK_IGNORE_GL_TEXTURE_TARGET
idDesc.fInfo.fID = static_cast<GrGLuint>(desc.fTextureHandle);
// We only support GL_TEXTURE_2D at the moment.
idDesc.fInfo.fTarget = GR_GL_TEXTURE_2D;
#else
idDesc.fInfo = *info;
#endif
if (GR_GL_TEXTURE_RECTANGLE != idDesc.fInfo.fTarget &&
GR_GL_TEXTURE_2D != idDesc.fInfo.fTarget) {
// Only texture rectangle and texture 2d are supported. We do not check whether texture
// rectangle is supported by Skia - if the caller provided us with a texture rectangle,
// we assume the necessary support exists.
return nullptr;
}
switch (ownership) {
case kAdopt_GrWrapOwnership:
idDesc.fLifeCycle = GrGpuResource::kAdopted_LifeCycle;
break;
case kBorrow_GrWrapOwnership:
idDesc.fLifeCycle = GrGpuResource::kBorrowed_LifeCycle;
break;
}
surfDesc.fFlags = (GrSurfaceFlags) desc.fFlags;
surfDesc.fWidth = desc.fWidth;
surfDesc.fHeight = desc.fHeight;
surfDesc.fConfig = desc.fConfig;
surfDesc.fSampleCnt = SkTMin(desc.fSampleCnt, this->caps()->maxSampleCount());
// FIXME: this should be calling resolve_origin(), but Chrome code is currently
// assuming the old behaviour, which is that backend textures are always
// BottomLeft, even for non-RT's. Once Chrome is fixed, change this to:
// glTexDesc.fOrigin = resolve_origin(desc.fOrigin, renderTarget);
if (kDefault_GrSurfaceOrigin == desc.fOrigin) {
surfDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
} else {
surfDesc.fOrigin = desc.fOrigin;
}
GrGLRenderTarget::IDDesc rtIDDesc;
if (!this->createRenderTargetObjects(surfDesc, GrGpuResource::kUncached_LifeCycle,
idDesc.fInfo, &rtIDDesc)) {
return nullptr;
}
return GrGLRenderTarget::CreateWrapped(this, surfDesc, rtIDDesc, 0);
}
////////////////////////////////////////////////////////////////////////////////
bool GrGLGpu::onGetWritePixelsInfo(GrSurface* dstSurface, int width, int height,
GrPixelConfig srcConfig,
DrawPreference* drawPreference,
@ -1534,9 +1594,6 @@ bool GrGLGpu::createTextureExternalAllocatorImpl(
GrStencilAttachment* GrGLGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
int width,
int height) {
// All internally created RTs are also textures. We don't create
// SBs for a client's standalone RT (that is a RT that isn't also a texture).
SkASSERT(rt->asTexture());
SkASSERT(width >= rt->width());
SkASSERT(height >= rt->height());

View File

@ -155,6 +155,8 @@ private:
GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&, GrWrapOwnership) override;
GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&,
GrWrapOwnership) override;
GrRenderTarget* onWrapBackendTextureAsRenderTarget(const GrBackendTextureDesc&,
GrWrapOwnership) override;
// Given a GrPixelConfig return the index into the stencil format array on GrGLCaps to a
// compatible stencil format, or negative if there is no compatible stencil format.
int getCompatibleStencilIndex(GrPixelConfig config);

View File

@ -199,6 +199,10 @@ bool SkSurface::getRenderTargetHandle(GrBackendObject* obj, BackendHandleAccess
return asSB(this)->onGetRenderTargetHandle(obj, access);
}
void SkSurface::prepareForExternalIO() {
asSB(this)->onPrepareForExternalIO();
}
//////////////////////////////////////////////////////////////////////////////////////
#if !SK_SUPPORT_GPU
@ -222,4 +226,9 @@ SkSurface* SkSurface::NewFromBackendRenderTarget(GrContext*, const GrBackendRend
return nullptr;
}
SkSurface* NewFromBackendTextureAsRenderTarget(GrContext*, const GrBackendTextureDesc&,
const SkSurfaceProps*) {
return nullptr;
}
#endif

View File

@ -75,6 +75,11 @@ public:
*/
virtual void onRestoreBackingMutability() {}
/**
* Issue any pending surface IO to the current backend 3D API and resolve any surface MSAA.
*/
virtual void onPrepareForExternalIO() {}
inline SkCanvas* getCachedCanvas();
inline SkImage* refCachedImage(Budgeted, ForceUnique);

View File

@ -7,6 +7,7 @@
#include "SkSurface_Gpu.h"
#include "GrResourceProvider.h"
#include "SkCanvas.h"
#include "SkGpuDevice.h"
#include "SkImage_Base.h"
@ -125,6 +126,10 @@ void SkSurface_Gpu::onDiscard() {
fDevice->accessRenderTarget()->discard();
}
void SkSurface_Gpu::onPrepareForExternalIO() {
fDevice->accessRenderTarget()->prepareForExternalIO();
}
///////////////////////////////////////////////////////////////////////////////
SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget* target, const SkSurfaceProps* props) {
@ -187,4 +192,23 @@ SkSurface* SkSurface::NewFromBackendRenderTarget(GrContext* context,
return new SkSurface_Gpu(device);
}
SkSurface* SkSurface::NewFromBackendTextureAsRenderTarget(GrContext* context,
const GrBackendTextureDesc& desc,
const SkSurfaceProps* props) {
if (nullptr == context) {
return nullptr;
}
SkAutoTUnref<GrRenderTarget> rt(
context->resourceProvider()->wrapBackendTextureAsRenderTarget(desc));
if (!rt) {
return nullptr;
}
SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(rt, props,
SkGpuDevice::kUninit_InitContents));
if (!device) {
return nullptr;
}
return new SkSurface_Gpu(device);
}
#endif

View File

@ -26,6 +26,7 @@ public:
SkImage* onNewImageSnapshot(Budgeted, ForceCopyMode) override;
void onCopyOnWrite(ContentChangeMode) override;
void onDiscard() override;
void onPrepareForExternalIO() override;
SkGpuDevice* getDevice() { return fDevice; }