Add MakeCrossContextFromPixmap
This operates just like MakeCrossContextFromEncoded, but starting from
raster data. This version is defensive (always uses copies if a raster
image needs to be made).
Bug: skia:
Change-Id: Ibc2b9a235c89a41fbbfd022d943f15ac212d0677
Reviewed-on: https://skia-review.googlesource.com/68205
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/gm/crosscontextimage.cpp b/gm/crosscontextimage.cpp
index cf973db..ff56a28 100644
--- a/gm/crosscontextimage.cpp
+++ b/gm/crosscontextimage.cpp
@@ -12,7 +12,7 @@
#include "GrContext.h"
#include "SkImage.h"
-DEF_SIMPLE_GM(cross_context_image, canvas, 512 + 512 + 30, 512 + 128 + 30) {
+DEF_SIMPLE_GM(cross_context_image, canvas, 512 * 3 + 60, 512 + 128 + 30) {
GrContext* context = canvas->getGrContext();
if (!context) {
skiagm::GM::DrawGpuOnlyMessage(canvas);
@@ -25,15 +25,26 @@
canvas->drawImage(encodedImage, 10, 10);
sk_sp<SkImage> crossContextImage = SkImage::MakeCrossContextFromEncoded(
- context, encodedData, false, canvas->imageInfo().colorSpace());
+ context, encodedData, false, canvas->imageInfo().colorSpace());
canvas->drawImage(crossContextImage, 512 + 30, 10);
+ SkBitmap bmp;
+ SkPixmap pixmap;
+ SkAssertResult(encodedImage->asLegacyBitmap(&bmp, SkImage::kRO_LegacyBitmapMode) &&
+ bmp.peekPixels(&pixmap));
+
+ sk_sp<SkImage> crossContextRaster = SkImage::MakeCrossContextFromPixmap(
+ context, pixmap, false, canvas->imageInfo().colorSpace());
+ canvas->drawImage(crossContextRaster, 512 + 512 + 60, 10);
+
SkIRect subset = SkIRect::MakeXYWH(256 - 64, 256 - 64, 128, 128);
sk_sp<SkImage> encodedSubset = encodedImage->makeSubset(subset);
sk_sp<SkImage> crossContextSubset = crossContextImage->makeSubset(subset);
+ sk_sp<SkImage> crossContextRasterSubset = crossContextRaster->makeSubset(subset);
canvas->drawImage(encodedSubset, 10, 512 + 30);
canvas->drawImage(crossContextSubset, 512 + 30, 512 + 30);
+ canvas->drawImage(crossContextRasterSubset, 512 + 512 + 60, 512 + 30);
}
#endif
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 540f099..1d725c2 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -136,8 +136,23 @@
* from the encoded data.
*/
static sk_sp<SkImage> MakeCrossContextFromEncoded(GrContext* context, sk_sp<SkData> data,
- bool buildMips,
- SkColorSpace* dstColorSpace);
+ bool buildMips, SkColorSpace* dstColorSpace);
+
+ /**
+ * Uploads the pixmap to a GPU backed image using the supplied GrContext.
+ * That image can be safely used by other GrContexts, across thread boundaries. The GrContext
+ * used here, and the ones used to draw this image later must be in the same GL share group,
+ * or otherwise be able to share resources.
+ *
+ * When the image's ref count reaches zero, the original GrContext will destroy the texture,
+ * asynchronously.
+ *
+ * The texture will be processed to be suitable for use with surfaces that have the
+ * supplied destination color space. The color space of the image itself will be determined
+ * from the pixmap.
+ */
+ static sk_sp<SkImage> MakeCrossContextFromPixmap(GrContext* context, const SkPixmap& pixmap,
+ bool buildMips, SkColorSpace* dstColorSpace);
/**
* Create a new image from the specified descriptor. Note - Skia will delete or recycle the
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index d279188..b829f64 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -508,6 +508,41 @@
return SkImage::MakeFromGenerator(std::move(gen));
}
+sk_sp<SkImage> SkImage::MakeCrossContextFromPixmap(GrContext* context, const SkPixmap& pixmap,
+ bool buildMips, SkColorSpace* dstColorSpace) {
+ // Some backends or drivers don't support (safely) moving resources between contexts
+ if (!context || !context->caps()->crossContextTextureSupport()) {
+ return SkImage::MakeRasterCopy(pixmap);
+ }
+
+ // Turn the pixmap into a GrTextureProxy
+ sk_sp<GrTextureProxy> proxy;
+ if (buildMips) {
+ SkBitmap bmp;
+ bmp.installPixels(pixmap);
+ proxy = GrGenerateMipMapsAndUploadToTextureProxy(context, bmp, dstColorSpace);
+ } else {
+ proxy = GrUploadPixmapToTextureProxy(context->resourceProvider(), pixmap, SkBudgeted::kYes,
+ dstColorSpace);
+ }
+
+ if (!proxy) {
+ return SkImage::MakeRasterCopy(pixmap);
+ }
+
+ sk_sp<GrTexture> texture = sk_ref_sp(proxy->priv().peekTexture());
+
+ // Flush any writes or uploads
+ context->contextPriv().prepareSurfaceForExternalIO(proxy.get());
+
+ sk_sp<GrSemaphore> sema = context->getGpu()->prepareTextureForCrossContextUsage(texture.get());
+
+ auto gen = GrBackendTextureImageGenerator::Make(std::move(texture), proxy->origin(),
+ std::move(sema), pixmap.alphaType(),
+ pixmap.info().refColorSpace());
+ return SkImage::MakeFromGenerator(std::move(gen));
+}
+
#if defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26
sk_sp<SkImage> SkImage::MakeFromAHardwareBuffer(AHardwareBuffer* graphicBuffer, SkAlphaType at,
sk_sp<SkColorSpace> cs) {
diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp
index 8eecbbd..49ef868 100644
--- a/tests/ImageTest.cpp
+++ b/tests/ImageTest.cpp
@@ -810,12 +810,10 @@
ctxInfo.grContext()->getGpu()->deleteTestingOnlyBackendTexture(backendTexHandle);
}
-DEF_GPUTEST(SkImage_MakeCrossContextRelease, reporter, /*factory*/) {
+static void test_cross_context_image(skiatest::Reporter* reporter,
+ std::function<sk_sp<SkImage>(GrContext*)> imageMaker) {
GrContextFactory testFactory;
- sk_sp<SkData> data = GetResourceAsData("mandrill_128.png");
- SkASSERT(data.get());
-
for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
ContextInfo ctxInfo = testFactory.getContextInfo(ctxType);
@@ -841,7 +839,7 @@
// Case #1: Create image, free image
{
- sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+ sk_sp<SkImage> refImg(imageMaker(ctx));
refImg.reset(nullptr); // force a release of the image
}
@@ -851,7 +849,7 @@
// Case #2: Create image, draw, flush, free image
{
- sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+ sk_sp<SkImage> refImg(imageMaker(ctx));
canvas->drawImage(refImg, 0, 0);
canvas->flush();
@@ -861,7 +859,7 @@
// Case #3: Create image, draw, free image, flush
{
- sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+ sk_sp<SkImage> refImg(imageMaker(ctx));
canvas->drawImage(refImg, 0, 0);
refImg.reset(nullptr); // force a release of the image
@@ -887,7 +885,7 @@
// Case #4: Create image, draw*, flush*, free image
{
testContext->makeCurrent();
- sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+ sk_sp<SkImage> refImg(imageMaker(ctx));
otherTestContext->makeCurrent();
canvas->drawImage(refImg, 0, 0);
@@ -900,7 +898,7 @@
// Case #5: Create image, draw*, free image, flush*
{
testContext->makeCurrent();
- sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+ sk_sp<SkImage> refImg(imageMaker(ctx));
otherTestContext->makeCurrent();
canvas->drawImage(refImg, 0, 0);
@@ -923,7 +921,7 @@
// Case #6: Verify that only one context can be using the image at a time
{
testContext->makeCurrent();
- sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+ sk_sp<SkImage> refImg(imageMaker(ctx));
// Any context should be able to borrow the texture at this point
sk_sp<SkColorSpace> texColorSpace;
@@ -960,6 +958,25 @@
}
}
+DEF_GPUTEST(SkImage_MakeCrossContextFromEncodedRelease, reporter, /*factory*/) {
+ sk_sp<SkData> data = GetResourceAsData("mandrill_128.png");
+ SkASSERT(data.get());
+
+ test_cross_context_image(reporter, [&data](GrContext* ctx) {
+ return SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr);
+ });
+}
+
+DEF_GPUTEST(SkImage_MakeCrossContextFromPixmapRelease, reporter, /*factory*/) {
+ SkBitmap bitmap;
+ SkPixmap pixmap;
+ SkAssertResult(GetResourceAsBitmap("mandrill_128.png", &bitmap) && bitmap.peekPixels(&pixmap));
+
+ test_cross_context_image(reporter, [&pixmap](GrContext* ctx) {
+ return SkImage::MakeCrossContextFromPixmap(ctx, pixmap, false, nullptr);
+ });
+}
+
static void check_images_same(skiatest::Reporter* reporter, const SkImage* a, const SkImage* b) {
if (a->width() != b->width() || a->height() != b->height()) {
ERRORF(reporter, "Images must have the same size");