Add SkSurface factory for wrapping an FBO in SkSurface

Review URL: https://codereview.chromium.org/1221853003
This commit is contained in:
bsalomon 2015-06-30 12:04:40 -07:00 committed by Commit bot
parent c49e8682ab
commit d3e259a16c
4 changed files with 143 additions and 74 deletions

View File

@ -88,11 +88,27 @@ public:
} }
/** /**
* Used to wrap a pre-existing backend 3D API texture in a SkSurface. The kRenderTarget flag * Used to wrap a pre-existing backend 3D API texture as a SkSurface. The kRenderTarget flag
* must be set on GrBackendTextureDesc for this to succeed. * 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* NewWrappedRenderTarget(GrContext*, GrBackendTextureDesc, static SkSurface* NewFromBackendTexture(GrContext*, const GrBackendTextureDesc&,
const SkSurfaceProps*); const SkSurfaceProps*);
// Legacy alias
static SkSurface* NewWrappedRenderTarget(GrContext* ctx, const GrBackendTextureDesc& desc,
const SkSurfaceProps* props) {
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
* lifetime of the SkSurface.
*/
static SkSurface* NewFromBackendRenderTarget(GrContext*, const GrBackendRenderTargetDesc&,
const SkSurfaceProps*);
/** /**
* Return a new surface whose contents will be drawn to an offscreen * Return a new surface whose contents will be drawn to an offscreen

View File

@ -192,9 +192,14 @@ SkSurface* SkSurface::NewRenderTarget(GrContext*, Budgeted, const SkImageInfo&,
return NULL; return NULL;
} }
SkSurface* SkSurface::NewWrappedRenderTarget(GrContext*, GrBackendTextureDesc, SkSurface* SkSurface::NewFromBackendTexture(GrContext*, const GrBackendTextureDesc&,
const SkSurfaceProps*) { const SkSurfaceProps*) {
return NULL; return NULL;
} }
SkSurface* SkSurface::NewFromBackendRenderTarget(GrContext*, const GrBackendRenderTargetDesc&,
const SkSurfaceProps*) {
return NULL;
}
#endif #endif

View File

@ -116,8 +116,8 @@ SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, Budgeted budgeted, const S
return SkNEW_ARGS(SkSurface_Gpu, (device)); return SkNEW_ARGS(SkSurface_Gpu, (device));
} }
SkSurface* SkSurface::NewWrappedRenderTarget(GrContext* context, GrBackendTextureDesc desc, SkSurface* SkSurface::NewFromBackendTexture(GrContext* context, const GrBackendTextureDesc& desc,
const SkSurfaceProps* props) { const SkSurfaceProps* props) {
if (NULL == context) { if (NULL == context) {
return NULL; return NULL;
} }
@ -137,4 +137,22 @@ SkSurface* SkSurface::NewWrappedRenderTarget(GrContext* context, GrBackendTextur
return SkNEW_ARGS(SkSurface_Gpu, (device)); return SkNEW_ARGS(SkSurface_Gpu, (device));
} }
SkSurface* SkSurface::NewFromBackendRenderTarget(GrContext* context,
const GrBackendRenderTargetDesc& desc,
const SkSurfaceProps* props) {
if (NULL == context) {
return NULL;
}
SkAutoTUnref<GrRenderTarget> rt(context->textureProvider()->wrapBackendRenderTarget(desc));
if (!rt) {
return NULL;
}
SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(rt, props,
SkGpuDevice::kUninit_InitContents));
if (!device) {
return NULL;
}
return SkNEW_ARGS(SkSurface_Gpu, (device));
}
#endif #endif

View File

@ -102,7 +102,7 @@ static void test_empty_surface(skiatest::Reporter* reporter, GrContext* ctx) {
} }
#if SK_SUPPORT_GPU #if SK_SUPPORT_GPU
static void test_wrapped_texture_surface(skiatest::Reporter* reporter, GrContext* ctx) { static void test_wrapped_surface(skiatest::Reporter* reporter, GrContext* ctx) {
if (NULL == ctx) { if (NULL == ctx) {
return; return;
} }
@ -120,77 +120,107 @@ static void test_wrapped_texture_surface(skiatest::Reporter* reporter, GrContext
return; return;
} }
// Test the wrapped factory for SkSurface by creating a texture using GL and then wrap it in for (int useFBO = 0; useFBO < 2; ++useFBO) {
// a SkSurface. // Test the wrapped factory for SkSurface by creating a texture using GL and then wrap it in
GrGLuint texID; // a SkSurface.
static const int kW = 100; GrGLuint texID;
static const int kH = 100; static const int kW = 100;
static const uint32_t kOrigColor = 0xFFAABBCC; static const int kH = 100;
SkAutoTArray<uint32_t> pixels(kW * kH); static const uint32_t kOrigColor = 0xFFAABBCC;
sk_memset32(pixels.get(), kOrigColor, kW * kH); SkAutoTArray<uint32_t> pixels(kW * kH);
GR_GL_CALL(gl, GenTextures(1, &texID)); sk_memset32(pixels.get(), kOrigColor, kW * kH);
GR_GL_CALL(gl, ActiveTexture(GR_GL_TEXTURE0)); GR_GL_CALL(gl, GenTextures(1, &texID));
GR_GL_CALL(gl, PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1)); GR_GL_CALL(gl, ActiveTexture(GR_GL_TEXTURE0));
GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, texID)); GR_GL_CALL(gl, PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1));
GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MAG_FILTER, GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, texID));
GR_GL_NEAREST)); GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MAG_FILTER,
GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MIN_FILTER, GR_GL_NEAREST));
GR_GL_NEAREST)); GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MIN_FILTER,
GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_S, GR_GL_NEAREST));
GR_GL_CLAMP_TO_EDGE)); GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_S,
GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_T, GR_GL_CLAMP_TO_EDGE));
GR_GL_CLAMP_TO_EDGE)); GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_T,
GR_GL_CALL(gl, TexImage2D(GR_GL_TEXTURE_2D, 0, GR_GL_RGBA, kW, kH, 0, GR_GL_RGBA, GR_GL_CLAMP_TO_EDGE));
GR_GL_UNSIGNED_BYTE, GR_GL_CALL(gl, TexImage2D(GR_GL_TEXTURE_2D, 0, GR_GL_RGBA, kW, kH, 0, GR_GL_RGBA,
pixels.get())); GR_GL_UNSIGNED_BYTE,
pixels.get()));
GrBackendTextureDesc wrappedDesc; SkAutoTUnref<SkSurface> surface;
wrappedDesc.fConfig = kRGBA_8888_GrPixelConfig; GrGLuint fboID = 0;
wrappedDesc.fWidth = kW; if (useFBO) {
wrappedDesc.fHeight = kH; GR_GL_CALL(gl, GenFramebuffers(1, &fboID));
wrappedDesc.fOrigin = kBottomLeft_GrSurfaceOrigin; GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, fboID));
wrappedDesc.fSampleCnt = 0; GR_GL_CALL(gl, FramebufferTexture2D(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0,
wrappedDesc.fFlags = kRenderTarget_GrBackendTextureFlag; GR_GL_TEXTURE_2D, texID, 0));
wrappedDesc.fTextureHandle = texID; GrBackendRenderTargetDesc wrappedDesc;
wrappedDesc.fConfig = kRGBA_8888_GrPixelConfig;
wrappedDesc.fWidth = kW;
wrappedDesc.fHeight = kH;
wrappedDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
wrappedDesc.fSampleCnt = 0;
wrappedDesc.fRenderTargetHandle = fboID;
wrappedDesc.fStencilBits = 0;
SkAutoTUnref<SkSurface> surface(SkSurface::NewWrappedRenderTarget(ctx, wrappedDesc, NULL)); ctx->resetContext();
REPORTER_ASSERT(reporter, surface); surface.reset(SkSurface::NewFromBackendRenderTarget(ctx, wrappedDesc, NULL));
if (surface) { } else {
// Validate that we can draw to the canvas and that the original texture color is preserved GrBackendTextureDesc wrappedDesc;
// in pixels that aren't rendered to via the surface. wrappedDesc.fConfig = kRGBA_8888_GrPixelConfig;
SkPaint paint; wrappedDesc.fWidth = kW;
static const SkColor kRectColor = ~kOrigColor | 0xFF000000; wrappedDesc.fHeight = kH;
paint.setColor(kRectColor); wrappedDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
surface->getCanvas()->drawRect(SkRect::MakeWH(SkIntToScalar(kW), SkIntToScalar(kH)/2), wrappedDesc.fSampleCnt = 0;
paint); wrappedDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
SkImageInfo readInfo = SkImageInfo::MakeN32Premul(kW, kH); wrappedDesc.fTextureHandle = texID;
surface->readPixels(readInfo, pixels.get(), kW * sizeof(uint32_t), 0, 0);
bool stop = false; ctx->resetContext();
SkPMColor origColorPM = SkPackARGB32((kOrigColor >> 24 & 0xFF), surface.reset(SkSurface::NewFromBackendTexture(ctx, wrappedDesc, NULL));
(kOrigColor >> 0 & 0xFF), }
(kOrigColor >> 8 & 0xFF), REPORTER_ASSERT(reporter, surface);
(kOrigColor >> 16 & 0xFF)); if (surface) {
SkPMColor rectColorPM = SkPackARGB32((kRectColor >> 24 & 0xFF), // Validate that we can draw to the canvas and that the original texture color is
(kRectColor >> 16 & 0xFF), // preserved in pixels that aren't rendered to via the surface.
(kRectColor >> 8 & 0xFF), SkPaint paint;
(kRectColor >> 0 & 0xFF)); static const SkColor kRectColor = ~kOrigColor | 0xFF000000;
for (int y = 0; y < kH/2 && !stop; ++y) { paint.setColor(kRectColor);
for (int x = 0; x < kW && !stop; ++x) { surface->getCanvas()->drawRect(SkRect::MakeWH(SkIntToScalar(kW), SkIntToScalar(kH)/2),
REPORTER_ASSERT(reporter, rectColorPM == pixels[x + y * kW]); paint);
if (rectColorPM != pixels[x + y * kW]) { SkImageInfo readInfo = SkImageInfo::MakeN32Premul(kW, kH);
stop = true; surface->readPixels(readInfo, pixels.get(), kW * sizeof(uint32_t), 0, 0);
bool stop = false;
SkPMColor origColorPM = SkPackARGB32((kOrigColor >> 24 & 0xFF),
(kOrigColor >> 0 & 0xFF),
(kOrigColor >> 8 & 0xFF),
(kOrigColor >> 16 & 0xFF));
SkPMColor rectColorPM = SkPackARGB32((kRectColor >> 24 & 0xFF),
(kRectColor >> 16 & 0xFF),
(kRectColor >> 8 & 0xFF),
(kRectColor >> 0 & 0xFF));
for (int y = 0; y < kH/2 && !stop; ++y) {
for (int x = 0; x < kW && !stop; ++x) {
REPORTER_ASSERT(reporter, rectColorPM == pixels[x + y * kW]);
if (rectColorPM != pixels[x + y * kW]) {
stop = true;
}
}
}
stop = false;
for (int y = kH/2; y < kH && !stop; ++y) {
for (int x = 0; x < kW && !stop; ++x) {
REPORTER_ASSERT(reporter, origColorPM == pixels[x + y * kW]);
if (origColorPM != pixels[x + y * kW]) {
stop = true;
}
} }
} }
} }
stop = false; if (texID) {
for (int y = kH/2; y < kH && !stop; ++y) { GR_GL_CALL(gl, DeleteTextures(1, &texID));
for (int x = 0; x < kW && !stop; ++x) {
REPORTER_ASSERT(reporter, origColorPM == pixels[x + y * kW]);
if (origColorPM != pixels[x + y * kW]) {
stop = true;
}
}
} }
if (fboID) {
GR_GL_CALL(gl, DeleteFramebuffers(1, &fboID));
}
} }
} }
#endif #endif
@ -837,7 +867,7 @@ DEF_GPUTEST(Surface, reporter, factory) {
TestGetTexture(reporter, kGpuScratch_SurfaceType, context); TestGetTexture(reporter, kGpuScratch_SurfaceType, context);
test_empty_surface(reporter, context); test_empty_surface(reporter, context);
test_surface_budget(reporter, context); test_surface_budget(reporter, context);
test_wrapped_texture_surface(reporter, context); test_wrapped_surface(reporter, context);
} }
} }
} }