Add GrBackendTexture/RenderTarget accessors to SkSurface

Change-Id: I63477fd4b8d48dc50af72736f0f8df566cd96d4a
Reviewed-on: https://skia-review.googlesource.com/85220
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Cary Clark <caryclark@skia.org>
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Robert Phillips 2018-04-05 09:30:38 -04:00 committed by Skia Commit-Bot
parent 91749c8252
commit 8caf85f9f4
14 changed files with 265 additions and 74 deletions

View File

@ -1089,6 +1089,56 @@ In OpenGL this returns the frame buffer object ID.
# ------------------------------------------------------------------------------
#Method GrBackendTexture getBackendTexture(BackendHandleAccess backendHandleAccess)
#In Property
#Line # returns the GPU reference to texture ##
Retrieves the backend texture. If Surface has no backend texture, an invalid
object is returned. Call GrBackendTexture::isValid to determine if the result
is valid.
The returned GrBackendTexture should be discarded if the Surface is drawn to or deleted.
#Param backendHandleAccess one of: kFlushRead_BackendHandleAccess,
kFlushWrite_BackendHandleAccess, kDiscardWrite_BackendHandleAccess
##
#Return GPU texture reference; invalid on failure ##
#NoExample
##
#SeeAlso GrBackendTexture BackendHandleAccess getBackendRenderTarget
#Method ##
# ------------------------------------------------------------------------------
#Method GrBackendRenderTarget getBackendRenderTarget(BackendHandleAccess backendHandleAccess)
#In Property
#Line # returns the GPU reference to render target ##
Retrieves the backend render target. If Surface has no backend render target, an invalid
object is returned. Call GrBackendRenderTarget::isValid to determine if the result
is valid.
The returned GrBackendRenderTarget should be discarded if the Surface is drawn to
or deleted.
#Param backendHandleAccess one of: kFlushRead_BackendHandleAccess,
kFlushWrite_BackendHandleAccess, kDiscardWrite_BackendHandleAccess
##
#Return GPU render target reference; invalid on failure ##
#NoExample
##
#SeeAlso GrBackendRenderTarget BackendHandleAccess getBackendTexture
#Method ##
# ------------------------------------------------------------------------------
#Method SkCanvas* getCanvas()
#In Property
#Line # returns Canvas that draws into Surface ##

View File

@ -713,6 +713,13 @@ FT_Load_Glyph
##
##
#Topic RenderTarget
#Class GrBackendRenderTarget
#Method bool isValid() const
##
##
##
#Topic Transfer_Mode
##

View File

@ -20,6 +20,7 @@ class SkPaint;
class SkSurfaceCharacterization;
class GrBackendRenderTarget;
class GrBackendSemaphore;
class GrBackendTexture;
class GrContext;
class GrRenderTarget;
@ -458,6 +459,35 @@ public:
bool getRenderTargetHandle(GrBackendObject* backendObject,
BackendHandleAccess backendHandleAccess);
#if GR_TEST_UTILS
/** Retrieves the backend texture. If Surface has no backend texture, an invalid
object is returned. Call GrBackendTexture::isValid to determine if the result
is valid.
The returned GrBackendTexture should be discarded if the Surface is drawn to or deleted.
@param backendHandleAccess one of: kFlushRead_BackendHandleAccess,
kFlushWrite_BackendHandleAccess,
kDiscardWrite_BackendHandleAccess
@return GPU texture reference; invalid on failure
*/
GrBackendTexture getBackendTexture(BackendHandleAccess backendHandleAccess);
/** Retrieves the backend render target. If Surface has no backend render target, an invalid
object is returned. Call GrBackendRenderTarget::isValid to determine if the result
is valid.
The returned GrBackendRenderTarget should be discarded if the Surface is drawn to
or deleted.
@param backendHandleAccess one of: kFlushRead_BackendHandleAccess,
kFlushWrite_BackendHandleAccess,
kDiscardWrite_BackendHandleAccess
@return GPU render target reference; invalid on failure
*/
GrBackendRenderTarget getBackendRenderTarget(BackendHandleAccess backendHandleAccess);
#endif
/** Returns SkCanvas that draws into SkSurface. Subsequent calls return the same SkCanvas.
SkCanvas returned is managed and owned by SkSurface, and is deleted when SkSurface
is deleted.

View File

@ -16,6 +16,24 @@
#include "vk/GrVkTypes.h"
#endif
#if !SK_SUPPORT_GPU
// SkSurface and SkImage rely on a minimal version of these always being available
class SK_API GrBackendTexture {
public:
GrBackendTexture() {}
bool isValid() const { return false; }
};
class SK_API GrBackendRenderTarget {
public:
GrBackendRenderTarget() {}
bool isValid() const { return false; }
};
#else
class SK_API GrBackendFormat {
public:
// Creates an invalid backend format.
@ -239,7 +257,11 @@ public:
// Returns true if the backend texture has been initialized.
bool isValid() const { return fConfig != kUnknown_GrPixelConfig; }
#if GR_TEST_UTILS
GrPixelConfig testingOnly_getPixelConfig() const;
static bool TestingOnly_Equals(const GrBackendRenderTarget&, const GrBackendRenderTarget&);
#endif
private:
// Friending for access to the GrPixelConfig
@ -272,3 +294,5 @@ private:
#endif
#endif

View File

@ -124,6 +124,10 @@ GR_STATIC_ASSERT(sizeof(GrBackendObject) >= sizeof(const GrGLTextureInfo*));
struct GrGLFramebufferInfo {
GrGLuint fFBOID;
GrGLenum fFormat = 0;
bool operator==(const GrGLFramebufferInfo& that) const {
return fFBOID == that.fFBOID && fFormat == that.fFormat;
}
};
GR_STATIC_ASSERT(sizeof(GrBackendObject) >= sizeof(const GrGLFramebufferInfo*));

View File

@ -290,3 +290,40 @@ const GrMockRenderTargetInfo* GrBackendRenderTarget::getMockRenderTargetInfo() c
}
return nullptr;
}
#if GR_TEST_UTILS
bool GrBackendRenderTarget::TestingOnly_Equals(const GrBackendRenderTarget& r0,
const GrBackendRenderTarget& r1) {
if (!r0.isValid() || !r1.isValid()) {
return false; // two invalid backend rendertargets are not considered equal
}
if (r0.fWidth != r1.fWidth ||
r0.fHeight != r1.fHeight ||
r0.fSampleCnt != r1.fSampleCnt ||
r0.fStencilBits != r1.fStencilBits ||
r0.fConfig != r1.fConfig ||
r0.fBackend != r1.fBackend) {
return false;
}
switch (r0.fBackend) {
case kOpenGL_GrBackend:
return r0.fGLInfo == r1.fGLInfo;
case kMock_GrBackend:
return r0.fMockInfo == r1.fMockInfo;
case kVulkan_GrBackend:
#ifdef SK_VULKAN
return r0.fVkInfo == r1.fVkInfo;
#else
// fall through
#endif
case kMetal_GrBackend: // fall through
default:
return false;
}
SkASSERT(0);
return false;
}
#endif

View File

@ -29,6 +29,7 @@
#include "GrContext.h"
#include "SkImage_Gpu.h"
#endif
#include "GrBackendSurface.h"
SkImage::SkImage(int width, int height, uint32_t uniqueID)
: fWidth(width)
@ -218,6 +219,11 @@ SkImage_Base::~SkImage_Base() {
}
}
GrBackendTexture SkImage_Base::onGetBackendTexture(bool flushPendingGrContextIO,
GrSurfaceOrigin* origin) const {
return GrBackendTexture(); // invalid
}
bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY, chint);
}

View File

@ -13,17 +13,9 @@
#include "SkSurface.h"
#if SK_SUPPORT_GPU
#include "GrBackendSurface.h"
#include "GrTextureProxy.h"
class GrTexture;
#else
class SK_API GrBackendTexture {
public:
GrBackendTexture() {}
bool isValid() const { return false; }
};
#endif
#include <new>
@ -67,13 +59,12 @@ public:
GrSurfaceOrigin* origin) const {
return 0;
}
virtual GrBackendTexture onGetBackendTexture(bool flushPendingGrContextIO,
GrSurfaceOrigin* origin) const {
return GrBackendTexture(); // invalid
}
virtual GrTexture* onGetTexture() const { return nullptr; }
#endif
virtual GrBackendTexture onGetBackendTexture(bool flushPendingGrContextIO,
GrSurfaceOrigin* origin) const;
virtual SkImageCacherator* peekCacherator() const { return nullptr; }
// return a read-only copy of the pixels. We promise to not modify them,

View File

@ -6,11 +6,13 @@
*/
#include "SkAtomics.h"
#include "SkSurface_Base.h"
#include "SkImagePriv.h"
#include "SkCanvas.h"
#include "SkFontLCDConfig.h"
#include "SkImagePriv.h"
#include "SkSurface_Base.h"
#include "GrBackendSurface.h"
static SkPixelGeometry compute_default_geometry() {
SkFontLCDConfig::LCDOrder order = SkFontLCDConfig::GetSubpixelOrder();
if (SkFontLCDConfig::kNONE_LCDOrder == order) {
@ -70,6 +72,14 @@ SkSurface_Base::~SkSurface_Base() {
}
}
GrBackendTexture SkSurface_Base::onGetBackendTexture(BackendHandleAccess) {
return GrBackendTexture(); // invalid
}
GrBackendRenderTarget SkSurface_Base::onGetBackendRenderTarget(BackendHandleAccess) {
return GrBackendRenderTarget(); // invalid
}
void SkSurface_Base::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) {
auto image = this->makeImageSnapshot();
if (image) {
@ -216,6 +226,16 @@ bool SkSurface::getRenderTargetHandle(GrBackendObject* obj, BackendHandleAccess
return asSB(this)->onGetRenderTargetHandle(obj, access);
}
#if GR_TEST_UTILS
GrBackendTexture SkSurface::getBackendTexture(BackendHandleAccess access) {
return asSB(this)->onGetBackendTexture(access);
}
GrBackendRenderTarget SkSurface::getBackendRenderTarget(BackendHandleAccess access) {
return asSB(this)->onGetBackendRenderTarget(access);
}
#endif
void SkSurface::prepareForExternalIO() {
this->flush();
}

View File

@ -27,6 +27,9 @@ public:
return false;
}
virtual GrBackendTexture onGetBackendTexture(BackendHandleAccess);
virtual GrBackendRenderTarget onGetBackendRenderTarget(BackendHandleAccess);
/**
* Allocate a canvas that will draw into this surface. We will cache this
* canvas, to return the same object to the caller multiple times. We

View File

@ -72,6 +72,27 @@ bool SkSurface_Gpu::onGetRenderTargetHandle(GrBackendObject* obj, BackendHandleA
return true;
}
GrBackendTexture SkSurface_Gpu::onGetBackendTexture(BackendHandleAccess access) {
GrRenderTarget* rt = prepare_rt_for_external_access(this, access);
if (!rt) {
return GrBackendTexture(); // invalid
}
GrTexture* texture = rt->asTexture();
if (texture) {
return texture->getBackendTexture();
}
return GrBackendTexture(); // invalid
}
GrBackendRenderTarget SkSurface_Gpu::onGetBackendRenderTarget(BackendHandleAccess access) {
GrRenderTarget* rt = prepare_rt_for_external_access(this, access);
if (!rt) {
return GrBackendRenderTarget(); // invalid
}
return rt->getBackendRenderTarget();
}
SkCanvas* SkSurface_Gpu::onNewCanvas() {
SkCanvas::InitFlags flags = SkCanvas::kDefault_InitFlags;
flags = static_cast<SkCanvas::InitFlags>(flags | SkCanvas::kConservativeRasterClip_InitFlag);

View File

@ -25,6 +25,10 @@ public:
GrBackendObject onGetTextureHandle(BackendHandleAccess) override;
bool onGetRenderTargetHandle(GrBackendObject*, BackendHandleAccess) override;
GrBackendTexture onGetBackendTexture(BackendHandleAccess) override;
GrBackendRenderTarget onGetBackendRenderTarget(BackendHandleAccess) override;
SkCanvas* onNewCanvas() override;
sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override;
sk_sp<SkImage> onNewImageSnapshot() override;

View File

@ -16,6 +16,7 @@
#include "Test.h"
#if SK_SUPPORT_GPU
#include "GrBackendSurface.h"
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrProxyProvider.h"
@ -126,17 +127,20 @@ static void test_image(const sk_sp<SkSpecialImage>& img, skiatest::Reporter* rep
SkPixmap tmpPixmap;
REPORTER_ASSERT(reporter, isGPUBacked != !!tightImg->peekPixels(&tmpPixmap));
}
#if SK_SUPPORT_GPU
{
SkImageFilter::OutputProperties outProps(img->getColorSpace());
sk_sp<SkSurface> tightSurf(img->makeTightSurface(outProps, subset.size()));
REPORTER_ASSERT(reporter, tightSurf->width() == subset.width());
REPORTER_ASSERT(reporter, tightSurf->height() == subset.height());
REPORTER_ASSERT(reporter, isGPUBacked ==
!!tightSurf->getTextureHandle(SkSurface::kDiscardWrite_BackendHandleAccess));
GrBackendTexture backendTex = tightSurf->getBackendTexture(
SkSurface::kDiscardWrite_BackendHandleAccess);
REPORTER_ASSERT(reporter, isGPUBacked == backendTex.isValid());
SkPixmap tmpPixmap;
REPORTER_ASSERT(reporter, isGPUBacked != !!tightSurf->peekPixels(&tmpPixmap));
}
#endif
}
DEF_TEST(SpecialImage_Raster, reporter) {

View File

@ -285,62 +285,48 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceSnapshotAlphaType_Gpu, reporter, ctxIn
}
}
}
#endif
static GrBackendObject get_surface_backend_texture_handle(
SkSurface* s, SkSurface::BackendHandleAccess a) {
return s->getTextureHandle(a);
}
static GrBackendObject get_surface_backend_render_target_handle(
SkSurface* s, SkSurface::BackendHandleAccess a) {
GrBackendObject result;
if (!s->getRenderTargetHandle(&result, a)) {
return 0;
}
return result;
}
static void test_backend_handle_access_copy_on_write(
skiatest::Reporter* reporter, SkSurface* surface, SkSurface::BackendHandleAccess mode,
GrBackendObject (*func)(SkSurface*, SkSurface::BackendHandleAccess)) {
GrBackendObject obj1 = func(surface, mode);
static void test_backend_texture_access_copy_on_write(
skiatest::Reporter* reporter, SkSurface* surface, SkSurface::BackendHandleAccess access) {
GrBackendTexture tex1 = surface->getBackendTexture(access);
sk_sp<SkImage> snap1(surface->makeImageSnapshot());
GrBackendObject obj2 = func(surface, mode);
GrBackendTexture tex2 = surface->getBackendTexture(access);
sk_sp<SkImage> snap2(surface->makeImageSnapshot());
// If the access mode triggers CoW, then the backend objects should reflect it.
REPORTER_ASSERT(reporter, (obj1 == obj2) == (snap1 == snap2));
REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(tex1, tex2) == (snap1 == snap2));
}
DEF_TEST(SurfaceBackendHandleAccessCopyOnWrite, reporter) {
static void test_backend_rendertarget_access_copy_on_write(
skiatest::Reporter* reporter, SkSurface* surface, SkSurface::BackendHandleAccess access) {
GrBackendRenderTarget rt1 = surface->getBackendRenderTarget(access);
sk_sp<SkImage> snap1(surface->makeImageSnapshot());
GrBackendRenderTarget rt2 = surface->getBackendRenderTarget(access);
sk_sp<SkImage> snap2(surface->makeImageSnapshot());
// If the access mode triggers CoW, then the backend objects should reflect it.
REPORTER_ASSERT(reporter, GrBackendRenderTarget::TestingOnly_Equals(rt1, rt2) ==
(snap1 == snap2));
}
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceBackendSurfaceAccessCopyOnWrite_Gpu, reporter, ctxInfo) {
const SkSurface::BackendHandleAccess accessModes[] = {
SkSurface::kFlushRead_BackendHandleAccess,
SkSurface::kFlushWrite_BackendHandleAccess,
SkSurface::kDiscardWrite_BackendHandleAccess,
};
for (auto& handle_access_func :
{ &get_surface_backend_texture_handle, &get_surface_backend_render_target_handle }) {
for (auto& accessMode : accessModes) {
auto surface(create_surface());
test_backend_handle_access_copy_on_write(reporter, surface.get(), accessMode,
handle_access_func);
}
}
}
#if SK_SUPPORT_GPU
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceBackendHandleAccessCopyOnWrite_Gpu, reporter, ctxInfo) {
const SkSurface::BackendHandleAccess accessModes[] = {
SkSurface::kFlushRead_BackendHandleAccess,
SkSurface::kFlushWrite_BackendHandleAccess,
SkSurface::kDiscardWrite_BackendHandleAccess,
};
for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) {
for (auto& handle_access_func :
{ &get_surface_backend_texture_handle, &get_surface_backend_render_target_handle }) {
for (auto& accessMode : accessModes) {
for (auto& accessMode : accessModes) {
{
auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
test_backend_handle_access_copy_on_write(reporter, surface.get(), accessMode,
handle_access_func);
test_backend_texture_access_copy_on_write(reporter, surface.get(), accessMode);
}
{
auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
test_backend_rendertarget_access_copy_on_write(reporter, surface.get(), accessMode);
}
}
}
@ -349,38 +335,42 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceBackendHandleAccessCopyOnWrite_Gpu, re
#if SK_SUPPORT_GPU
static void test_backend_handle_unique_id(
skiatest::Reporter* reporter, SkSurface* surface,
GrBackendObject (*func)(SkSurface*, SkSurface::BackendHandleAccess)) {
template<typename Type, Type(SkSurface::*func)(SkSurface::BackendHandleAccess)>
static void test_backend_unique_id(skiatest::Reporter* reporter, SkSurface* surface) {
sk_sp<SkImage> image0(surface->makeImageSnapshot());
GrBackendObject obj = func(surface, SkSurface::kFlushRead_BackendHandleAccess);
REPORTER_ASSERT(reporter, obj != 0);
Type obj = (surface->*func)(SkSurface::kFlushRead_BackendHandleAccess);
REPORTER_ASSERT(reporter, obj.isValid());
sk_sp<SkImage> image1(surface->makeImageSnapshot());
// just read access should not affect the snapshot
REPORTER_ASSERT(reporter, image0->uniqueID() == image1->uniqueID());
obj = func(surface, SkSurface::kFlushWrite_BackendHandleAccess);
REPORTER_ASSERT(reporter, obj != 0);
obj = (surface->*func)(SkSurface::kFlushWrite_BackendHandleAccess);
REPORTER_ASSERT(reporter, obj.isValid());
sk_sp<SkImage> image2(surface->makeImageSnapshot());
// expect a new image, since we claimed we would write
REPORTER_ASSERT(reporter, image0->uniqueID() != image2->uniqueID());
obj = func(surface, SkSurface::kDiscardWrite_BackendHandleAccess);
REPORTER_ASSERT(reporter, obj != 0);
obj = (surface->*func)(SkSurface::kDiscardWrite_BackendHandleAccess);
REPORTER_ASSERT(reporter, obj.isValid());
sk_sp<SkImage> image3(surface->makeImageSnapshot());
// expect a new(er) image, since we claimed we would write
REPORTER_ASSERT(reporter, image0->uniqueID() != image3->uniqueID());
REPORTER_ASSERT(reporter, image2->uniqueID() != image3->uniqueID());
}
// No CPU test.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceBackendHandleAccessIDs_Gpu, reporter, ctxInfo) {
for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) {
for (auto& test_func : { &test_backend_handle_unique_id }) {
for (auto& handle_access_func :
{ &get_surface_backend_texture_handle, &get_surface_backend_render_target_handle}) {
auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
test_func(reporter, surface.get(), handle_access_func);
}
{
auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
test_backend_unique_id<GrBackendTexture, &SkSurface::getBackendTexture>(reporter,
surface.get());
}
{
auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
test_backend_unique_id<GrBackendRenderTarget, &SkSurface::getBackendRenderTarget>(
reporter, surface.get());
}
}
}