SkImage* SkSurface_Gpu::onNewImageSnapshot(SkBudgeted budgeted, ForceCopyMode forceCopyMode) { GrRenderTarget* rt = fDevice->accessRenderTarget(); SkASSERT(rt); GrTexture* tex = rt->asTexture(); SkAutoTUnref<GrTexture> copy; // TODO: Force a copy when the rt is an external resource. if (kYes_ForceCopyMode == forceCopyMode || !tex) { GrSurfaceDesc desc = fDevice->accessRenderTarget()->desc(); GrContext* ctx = fDevice->context(); desc.fFlags = desc.fFlags & ~kRenderTarget_GrSurfaceFlag; copy.reset(ctx->textureProvider()->createTexture(desc, budgeted)); if (!copy) { return nullptr; } if (!ctx->copySurface(copy, rt)) { return nullptr; } tex = copy; } const SkImageInfo info = fDevice->imageInfo(); SkImage* image = nullptr; if (tex) { image = new SkImage_Gpu(info.width(), info.height(), kNeedNewImageUniqueID, info.alphaType(), tex, budgeted); } return image; }
sk_sp<SkImage> SkSurface_Gpu::onNewImageSnapshot(SkBudgeted budgeted, ForceCopyMode forceCopyMode) { GrRenderTarget* rt = fDevice->accessDrawContext()->accessRenderTarget(); SkASSERT(rt); GrTexture* tex = rt->asTexture(); SkAutoTUnref<GrTexture> copy; // If the original render target is a buffer originally created by the client, then we don't // want to ever retarget the SkSurface at another buffer we create. Force a copy now to avoid // copy-on-write. if (kYes_ForceCopyMode == forceCopyMode || !tex || rt->resourcePriv().refsWrappedObjects()) { GrSurfaceDesc desc = fDevice->accessDrawContext()->desc(); GrContext* ctx = fDevice->context(); desc.fFlags = desc.fFlags & ~kRenderTarget_GrSurfaceFlag; copy.reset(ctx->textureProvider()->createTexture(desc, budgeted)); if (!copy) { return nullptr; } if (!ctx->copySurface(copy, rt)) { return nullptr; } tex = copy; } const SkImageInfo info = fDevice->imageInfo(); sk_sp<SkImage> image; if (tex) { image = sk_make_sp<SkImage_Gpu>(info.width(), info.height(), kNeedNewImageUniqueID, info.alphaType(), tex, sk_ref_sp(info.colorSpace()), budgeted); } return image; }
// This tests that GrTextureStripAtlas flushes pending IO on the texture it acquires. DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrTextureStripAtlasFlush, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); GrSurfaceDesc desc; desc.fWidth = 32; desc.fHeight = 32; desc.fConfig = kRGBA_8888_GrPixelConfig; GrTexture* texture = context->textureProvider()->createTexture(desc, SkBudgeted::kYes, nullptr, 0); GrSurfaceDesc targetDesc = desc; targetDesc.fFlags = kRenderTarget_GrSurfaceFlag; GrTexture* target = context->textureProvider()->createTexture(targetDesc, SkBudgeted::kYes, nullptr, 0); SkAutoTMalloc<uint32_t> pixels(desc.fWidth * desc.fHeight); memset(pixels.get(), 0xFF, sizeof(uint32_t) * desc.fWidth * desc.fHeight); texture->writePixels(0, 0, desc.fWidth, desc.fHeight, kRGBA_8888_GrPixelConfig, pixels.get()); // Add a pending read to the texture, and then make it available for reuse. context->copySurface(target, texture); texture->unref(); // Create an atlas with parameters that allow it to reuse the texture. GrTextureStripAtlas::Desc atlasDesc; atlasDesc.fContext = context; atlasDesc.fConfig = desc.fConfig; atlasDesc.fWidth = desc.fWidth; atlasDesc.fHeight = desc.fHeight; atlasDesc.fRowHeight = 1; GrTextureStripAtlas* atlas = GrTextureStripAtlas::GetAtlas(atlasDesc); // Write to the atlas' texture. SkImageInfo info = SkImageInfo::MakeN32(desc.fWidth, desc.fHeight, kPremul_SkAlphaType); size_t rowBytes = desc.fWidth * GrBytesPerPixel(desc.fConfig); SkBitmap bitmap; bitmap.allocPixels(info, rowBytes); memset(bitmap.getPixels(), 1, rowBytes * desc.fHeight); int row = atlas->lockRow(bitmap); if (!context->caps()->preferVRAMUseOverFlushes()) REPORTER_ASSERT(reporter, texture == atlas->getTexture()); // The atlas' use of its texture shouldn't change which pixels got copied to the target. SkAutoTMalloc<uint32_t> actualPixels(desc.fWidth * desc.fHeight); bool success = target->readPixels(0, 0, desc.fWidth, desc.fHeight, kRGBA_8888_GrPixelConfig, actualPixels.get()); REPORTER_ASSERT(reporter, success); REPORTER_ASSERT(reporter, !memcmp(pixels.get(), actualPixels.get(), sizeof(uint32_t) * desc.fWidth * desc.fHeight)); target->unref(); atlas->unlockRow(row); }
sk_sp<SkImage> SkImage_Gpu::onMakeSubset(const SkIRect& subset) const { GrContext* ctx = fTexture->getContext(); GrSurfaceDesc desc = fTexture->desc(); desc.fWidth = subset.width(); desc.fHeight = subset.height(); sk_sp<GrTexture> subTx(ctx->textureProvider()->createTexture(desc, fBudgeted)); if (!subTx) { return nullptr; } ctx->copySurface(subTx.get(), fTexture.get(), subset, SkIPoint::Make(0, 0)); return sk_make_sp<SkImage_Gpu>(desc.fWidth, desc.fHeight, kNeedNewImageUniqueID, fAlphaType, std::move(subTx), fColorSpace, fBudgeted); }
GrTexture* GrDeepCopyTexture(GrTexture* src, bool budgeted) { GrContext* ctx = src->getContext(); GrSurfaceDesc desc = src->desc(); GrTexture* dst = ctx->textureProvider()->createTexture(desc, budgeted, nullptr, 0); if (!dst) { return nullptr; } const SkIRect srcR = SkIRect::MakeWH(desc.fWidth, desc.fHeight); const SkIPoint dstP = SkIPoint::Make(0, 0); ctx->copySurface(dst, src, srcR, dstP, GrContext::kFlushWrites_PixelOp); return dst; }
SkImage* SkImage_Gpu::onNewSubset(const SkIRect& subset) const { GrContext* ctx = fTexture->getContext(); GrSurfaceDesc desc = fTexture->desc(); desc.fWidth = subset.width(); desc.fHeight = subset.height(); GrTexture* subTx = ctx->textureProvider()->createTexture(desc, SkSurface::kYes_Budgeted == fBudgeted); if (!subTx) { return nullptr; } ctx->copySurface(subTx, fTexture, subset, SkIPoint::Make(0, 0)); return new SkImage_Gpu(desc.fWidth, desc.fHeight, kNeedNewImageUniqueID, fAlphaType, subTx, fBudgeted); }
static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorType dstCT, SkColorProfileType dstPT, const SkIRect* subset) { if (NULL == texture || kUnknown_SkColorType == dstCT) { return NULL; } GrContext* context = texture->getContext(); if (NULL == context) { return NULL; } GrSurfaceDesc desc; SkIRect srcRect; if (!subset) { desc.fWidth = texture->width(); desc.fHeight = texture->height(); srcRect = SkIRect::MakeWH(texture->width(), texture->height()); } else { SkASSERT(SkIRect::MakeWH(texture->width(), texture->height()).contains(*subset)); // Create a new texture that is the size of subset. desc.fWidth = subset->width(); desc.fHeight = subset->height(); srcRect = *subset; } desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, kPremul_SkAlphaType, dstPT); GrTexture* dst = context->createTexture(desc, false, NULL, 0); if (NULL == dst) { return NULL; } // Blink is relying on the above copy being sent to GL immediately in the case when the source // is a WebGL canvas backing store. We could have a TODO to remove this flush flag, but we have // a larger TODO to remove SkGrPixelRef entirely. context->copySurface(dst->asRenderTarget(), texture, srcRect, SkIPoint::Make(0,0), GrContext::kFlushWrites_PixelOp); SkImageInfo info = SkImageInfo::Make(desc.fWidth, desc.fHeight, dstCT, kPremul_SkAlphaType, dstPT); SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, dst)); SkSafeUnref(dst); return pixelRef; }
sk_sp<SkImage> onMakeTightSubset(const SkIRect& subset) const override { if (0 == subset.fLeft && 0 == subset.fTop && fTexture->width() == subset.width() && fTexture->height() == subset.height()) { // The existing GrTexture is already tight so reuse it in the SkImage return sk_make_sp<SkImage_Gpu>(fTexture->width(), fTexture->height(), kNeedNewImageUniqueID, fAlphaType, fTexture, SkBudgeted::kYes); } GrContext* ctx = fTexture->getContext(); GrSurfaceDesc desc = fTexture->desc(); desc.fWidth = subset.width(); desc.fHeight = subset.height(); SkAutoTUnref<GrTexture> subTx(ctx->textureProvider()->createTexture(desc, SkBudgeted::kYes)); if (!subTx) { return nullptr; } ctx->copySurface(subTx, fTexture, subset, SkIPoint::Make(0, 0)); return sk_make_sp<SkImage_Gpu>(desc.fWidth, desc.fHeight, kNeedNewImageUniqueID, fAlphaType, subTx, SkBudgeted::kYes); }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CopySurface, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); static const int kW = 10; static const int kH = 10; static const size_t kRowBytes = sizeof(uint32_t) * kW; GrSurfaceDesc baseDesc; baseDesc.fConfig = kRGBA_8888_GrPixelConfig; baseDesc.fWidth = kW; baseDesc.fHeight = kH; SkAutoTMalloc<uint32_t> srcPixels(kW * kH); for (int i = 0; i < kW * kH; ++i) { srcPixels.get()[i] = i; } SkAutoTMalloc<uint32_t> dstPixels(kW * kH); for (int i = 0; i < kW * kH; ++i) { dstPixels.get()[i] = ~i; } static const SkIRect kSrcRects[] { { 0, 0, kW , kH }, {-1, -1, kW+1, kH+1}, { 1, 1, kW-1, kH-1}, { 5, 5, 6 , 6 }, }; static const SkIPoint kDstPoints[] { { 0 , 0 }, { 1 , 1 }, { kW/2, kH/4}, { kW-1, kH-1}, { kW , kH }, { kW+1, kH+2}, {-1 , -1 }, }; SkAutoTMalloc<uint32_t> read(kW * kH); for (auto sOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { for (auto dOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { for (auto sFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) { for (auto dFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) { for (auto srcRect : kSrcRects) { for (auto dstPoint : kDstPoints) { GrSurfaceDesc srcDesc = baseDesc; srcDesc.fOrigin = sOrigin; srcDesc.fFlags = sFlags; GrSurfaceDesc dstDesc = baseDesc; dstDesc.fOrigin = dOrigin; dstDesc.fFlags = dFlags; SkAutoTUnref<GrTexture> src( context->textureProvider()->createTexture(srcDesc, SkBudgeted::kNo, srcPixels.get(), kRowBytes)); SkAutoTUnref<GrTexture> dst( context->textureProvider()->createTexture(dstDesc, SkBudgeted::kNo, dstPixels.get(), kRowBytes)); if (!src || !dst) { ERRORF(reporter, "Could not create surfaces for copy surface test."); continue; } bool result = context->copySurface(dst, src, srcRect, dstPoint); bool expectedResult = true; SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft, dstPoint.fY - srcRect.fTop }; SkIRect copiedDstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, srcRect.width(), srcRect.height()); SkIRect copiedSrcRect; if (!copiedSrcRect.intersect(srcRect, SkIRect::MakeWH(kW, kH))) { expectedResult = false; } else { // If the src rect was clipped, apply same clipping to each side of // copied dst rect. copiedDstRect.fLeft += copiedSrcRect.fLeft - srcRect.fLeft; copiedDstRect.fTop += copiedSrcRect.fTop - srcRect.fTop; copiedDstRect.fRight -= copiedSrcRect.fRight - srcRect.fRight; copiedDstRect.fBottom -= copiedSrcRect.fBottom - srcRect.fBottom; } if (copiedDstRect.isEmpty() || !copiedDstRect.intersect(SkIRect::MakeWH(kW, kH))) { expectedResult = false; } // To make the copied src rect correct we would apply any dst clipping // back to the src rect, but we don't use it again so don't bother. if (expectedResult != result) { ERRORF(reporter, "Expected return value %d from copySurface, got " "%d.", expectedResult, result); continue; } if (!expectedResult || !result) { continue; } sk_memset32(read.get(), 0, kW * kH); if (!dst->readPixels(0, 0, kW, kH, baseDesc.fConfig, read.get(), kRowBytes)) { ERRORF(reporter, "Error calling readPixels"); continue; } bool abort = false; // Validate that pixels inside copiedDstRect received the correct value // from src and that those outside were not modified. for (int y = 0; y < kH && !abort; ++y) { for (int x = 0; x < kW; ++x) { uint32_t r = read.get()[y * kW + x]; if (copiedDstRect.contains(x, y)) { int sx = x - dstOffset.fX; int sy = y - dstOffset.fY; uint32_t s = srcPixels.get()[sy * kW + sx]; if (s != r) { ERRORF(reporter, "Expected dst %d,%d to contain " "0x%08x copied from src location %d,%d. Got " "0x%08x", x, y, s, sx, sy, r); abort = true; break; } } else { uint32_t d = dstPixels.get()[y * kW + x]; if (d != r) { ERRORF(reporter, "Expected dst %d,%d to be unmodified (" "0x%08x). Got 0x%08x", x, y, d, r); abort = true; break; } } } } } } } } } } }