From f7b8b8affec91fcfab0d79199e466c16c254fe56 Mon Sep 17 00:00:00 2001 From: ericrk Date: Wed, 24 Feb 2016 14:49:51 -0800 Subject: [PATCH] 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 --- include/core/SkSurface.h | 17 +++++++- include/gpu/GrTextureProvider.h | 2 +- src/gpu/GrGpu.cpp | 20 +++++++++ src/gpu/GrGpu.h | 11 ++++- src/gpu/GrResourceProvider.cpp | 7 ++++ src/gpu/GrResourceProvider.h | 9 ++++ src/gpu/GrTest.cpp | 5 +++ src/gpu/gl/GrGLGpu.cpp | 73 +++++++++++++++++++++++++++++---- src/gpu/gl/GrGLGpu.h | 2 + src/image/SkSurface.cpp | 9 ++++ src/image/SkSurface_Base.h | 5 +++ src/image/SkSurface_Gpu.cpp | 24 +++++++++++ src/image/SkSurface_Gpu.h | 1 + 13 files changed, 173 insertions(+), 12 deletions(-) diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h index d5a66b4bb7..975c80e3f0 100644 --- a/include/core/SkSurface.h +++ b/include/core/SkSurface.h @@ -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*); diff --git a/include/gpu/GrTextureProvider.h b/include/gpu/GrTextureProvider.h index 338dddbaca..3fa359d19a 100644 --- a/include/gpu/GrTextureProvider.h +++ b/include/gpu/GrTextureProvider.h @@ -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); diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp index e7adf0b321..4fcd4d57af 100644 --- a/src/gpu/GrGpu.cpp +++ b/src/gpu/GrGpu.cpp @@ -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); diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h index 147c481020..fd134eae2c 100644 --- a/src/gpu/GrGpu.h +++ b/src/gpu/GrGpu.h @@ -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; diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp index 19fa1cfbb6..79146d0911 100644 --- a/src/gpu/GrResourceProvider.cpp +++ b/src/gpu/GrResourceProvider.cpp @@ -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); +} diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h index 783c7c743b..3dfc9ba863 100644 --- a/src/gpu/GrResourceProvider.h +++ b/src/gpu/GrResourceProvider.h @@ -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, diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp index b4fb27bcc5..f082a8693a 100644 --- a/src/gpu/GrTest.cpp +++ b/src/gpu/GrTest.cpp @@ -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; } diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index 73d219d033..1070f53c5c 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -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(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(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()); diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h index 00acd34d9d..87c5c3db37 100644 --- a/src/gpu/gl/GrGLGpu.h +++ b/src/gpu/gl/GrGLGpu.h @@ -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); diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp index 711dfda6a4..4fc904539b 100644 --- a/src/image/SkSurface.cpp +++ b/src/image/SkSurface.cpp @@ -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 diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h index aaa19cf592..159780b33a 100644 --- a/src/image/SkSurface_Base.h +++ b/src/image/SkSurface_Base.h @@ -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); diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp index 03fdecef3a..5eca56e8fb 100644 --- a/src/image/SkSurface_Gpu.cpp +++ b/src/image/SkSurface_Gpu.cpp @@ -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 rt( + context->resourceProvider()->wrapBackendTextureAsRenderTarget(desc)); + if (!rt) { + return nullptr; + } + SkAutoTUnref device(SkGpuDevice::Create(rt, props, + SkGpuDevice::kUninit_InitContents)); + if (!device) { + return nullptr; + } + return new SkSurface_Gpu(device); +} + #endif diff --git a/src/image/SkSurface_Gpu.h b/src/image/SkSurface_Gpu.h index 23aed2cd3c..dff7970d46 100644 --- a/src/image/SkSurface_Gpu.h +++ b/src/image/SkSurface_Gpu.h @@ -26,6 +26,7 @@ public: SkImage* onNewImageSnapshot(Budgeted, ForceCopyMode) override; void onCopyOnWrite(ContentChangeMode) override; void onDiscard() override; + void onPrepareForExternalIO() override; SkGpuDevice* getDevice() { return fDevice; }