class SkString;
class SkSurface;
class GrContext;
+class GrContextThreadSafeProxy;
class GrTexture;
#define SK_SUPPORT_LEGACY_IMAGEFACTORY
*/
sk_sp<SkImage> makeTextureImage(GrContext*) const;
+ /** Drawing params for which a deferred texture image data should be optimized. */
+ struct DeferredTextureImageUsageParams {
+ SkMatrix fMatrix;
+ SkFilterQuality fQuality;
+ };
+
+ /**
+ * This method allows clients to capture the data necessary to turn a SkImage into a texture-
+ * backed image. If the original image is codec-backed this will decode into a format optimized
+ * for the context represented by the proxy. This method is thread safe with respect to the
+ * GrContext whence the proxy came. Clients allocate and manage the storage of the deferred
+ * texture data and control its lifetime. No cleanup is required, thus it is safe to simply free
+ * the memory out from under the data.
+ *
+ * The same method is used both for getting the size necessary for pre-uploaded texture data
+ * and for retrieving the data. The params array represents the set of draws over which to
+ * optimize the pre-upload data.
+ *
+ * When called with a null buffer this returns the size that the client must allocate in order
+ * to create deferred texture data for this image (or zero if this is an inappropriate
+ * candidate). The buffer allocated by the client should be 8 byte aligned.
+ *
+ * When buffer is not null this fills in the deferred texture data for this image in the
+ * provided buffer (assuming this is an appropriate candidate image and the buffer is
+ * appropriately aligned). Upon success the size written is returned, otherwise 0.
+ */
+ size_t getDeferredTextureImageData(const GrContextThreadSafeProxy&,
+ const DeferredTextureImageUsageParams[],
+ int paramCnt,
+ void* buffer) const;
+
+ /**
+ * Returns a texture-backed image from data produced in SkImage::getDeferredTextureImageData.
+ * The context must be the context that provided the proxy passed to
+ * getDeferredTextureImageData.
+ */
+ static sk_sp<SkImage> MakeFromDeferredTextureImageData(GrContext*, const void*, SkBudgeted);
+
// Helper functions to convert to SkBitmap
enum LegacyBitmapMode {
static SkImage* NewFromPicture(const SkPicture*, const SkISize& dimensions,
const SkMatrix*, const SkPaint*);
static SkImage* NewTextureFromPixmap(GrContext*, const SkPixmap&, SkBudgeted budgeted);
+ static SkImage* NewFromDeferredTextureImageData(GrContext*, const void*, SkBudgeted);
SkImage* newSubset(const SkIRect& subset) const { return this->makeSubset(subset).release(); }
SkImage* newTextureImage(GrContext* ctx) const { return this->makeTextureImage(ctx).release(); }
void alloc(const SkImageInfo&);
/**
+ * Gets the size and optionally the rowBytes that would be allocated by SkAutoPixmapStorage if
+ * alloc/tryAlloc was called.
+ */
+ static size_t AllocSize(const SkImageInfo& info, size_t* rowBytes);
+
+ /**
* Returns an SkData object wrapping the allocated pixels memory, and resets the pixmap.
* If the storage hasn't been allocated, the result is NULL.
*/
struct GrBatchAtlasConfig;
class GrBatchFontCache;
struct GrContextOptions;
+class GrContextThreadSafeProxy;
class GrDrawingManager;
class GrDrawContext;
class GrDrawTarget;
virtual ~GrContext();
+ GrContextThreadSafeProxy* threadSafeProxy();
+
/**
* The GrContext normally assumes that no outsider is setting state
* within the underlying 3D API's context/device/whatever. This call informs
SkDEBUGCODE(GrSingleOwner* debugSingleOwner() const { return &fSingleOwner; } )
private:
- GrGpu* fGpu;
- const GrCaps* fCaps;
- GrResourceCache* fResourceCache;
+ GrGpu* fGpu;
+ const GrCaps* fCaps;
+ GrResourceCache* fResourceCache;
// this union exists because the inheritance of GrTextureProvider->GrResourceProvider
// is in a private header.
union {
- GrResourceProvider* fResourceProvider;
- GrTextureProvider* fTextureProvider;
+ GrResourceProvider* fResourceProvider;
+ GrTextureProvider* fTextureProvider;
};
- GrBatchFontCache* fBatchFontCache;
- SkAutoTDelete<GrLayerCache> fLayerCache;
- SkAutoTDelete<GrTextBlobCache> fTextBlobCache;
+ SkAutoTUnref<GrContextThreadSafeProxy> fThreadSafeProxy;
+
+ GrBatchFontCache* fBatchFontCache;
+ SkAutoTDelete<GrLayerCache> fLayerCache;
+ SkAutoTDelete<GrTextBlobCache> fTextBlobCache;
// Set by OverbudgetCB() to request that GrContext flush before exiting a draw.
- bool fFlushToReduceCacheSize;
- bool fDidTestPMConversions;
- int fPMToUPMConversion;
- int fUPMToPMConversion;
+ bool fFlushToReduceCacheSize;
+ bool fDidTestPMConversions;
+ int fPMToUPMConversion;
+ int fUPMToPMConversion;
// The sw backend may call GrContext::readSurfacePixels on multiple threads
// We may transfer the responsibilty for using a mutex to the sw backend
// when there are fewer code paths that lead to a readSurfacePixels call
// readSurfacePixels proceeds to grab it.
// TODO: Stop pretending to make GrContext thread-safe for sw rasterization and provide
// a mechanism to make a SkPicture safe for multithreaded sw rasterization.
- SkMutex fReadPixelsMutex;
- SkMutex fTestPMConversionsMutex;
+ SkMutex fReadPixelsMutex;
+ SkMutex fTestPMConversionsMutex;
// In debug builds we guard against improper thread handling
// This guard is passed to the GrDrawingManager and, from there to all the
// GrDrawContexts. It is also passed to the GrTextureProvider and SkGpuDevice.
- mutable GrSingleOwner fSingleOwner;
+ mutable GrSingleOwner fSingleOwner;
struct CleanUpData {
PFCleanUpFunc fFunc;
void* fInfo;
};
- SkTDArray<CleanUpData> fCleanUpData;
+ SkTDArray<CleanUpData> fCleanUpData;
- const uint32_t fUniqueID;
+ const uint32_t fUniqueID;
- SkAutoTDelete<GrDrawingManager> fDrawingManager;
+ SkAutoTDelete<GrDrawingManager> fDrawingManager;
- GrAuditTrail fAuditTrail;
+ GrAuditTrail fAuditTrail;
// TODO: have the CMM use drawContexts and rm this friending
friend class GrClipMaskManager; // the CMM is friended just so it can call 'drawingManager'
typedef SkRefCnt INHERITED;
};
+/**
+ * Can be used to perform actions related to the generating GrContext in a thread safe manner. The
+ * proxy does not access the 3D API (e.g. OpenGL) that backs the generating GrContext.
+ */
+class GrContextThreadSafeProxy : public SkRefCnt {
+private:
+ GrContextThreadSafeProxy(const GrCaps* caps, uint32_t uniqueID)
+ : fCaps(SkRef(caps))
+ , fContextUniqueID(uniqueID) {}
+
+ SkAutoTUnref<const GrCaps> fCaps;
+ uint32_t fContextUniqueID;
+
+ friend class GrContext;
+ friend class SkImage;
+
+ typedef SkRefCnt INHERITED;
+};
+
#endif
this->freeStorage();
}
+size_t SkAutoPixmapStorage::AllocSize(const SkImageInfo& info, size_t* rowBytes) {
+ size_t rb = info.minRowBytes();
+ if (rowBytes) {
+ *rowBytes = rb;
+ }
+ return info.getSafeSize(rb);
+}
+
bool SkAutoPixmapStorage::tryAlloc(const SkImageInfo& info) {
this->freeStorage();
-
- size_t rb = info.minRowBytes();
- size_t size = info.getSafeSize(rb);
+
+ size_t rb;
+ size_t size = AllocSize(info, &rb);
if (0 == size) {
return false;
}
fCaps->unref();
}
+GrContextThreadSafeProxy* GrContext::threadSafeProxy() {
+ if (!fThreadSafeProxy) {
+ fThreadSafeProxy.reset(new GrContextThreadSafeProxy(fCaps, this->uniqueID()));
+ }
+ return SkRef(fThreadSafeProxy.get());
+}
+
void GrContext::abandonContext() {
ASSERT_SINGLE_OWNER
return nullptr;
}
+size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy&,
+ const DeferredTextureImageUsageParams[],
+ int paramCnt, void* buffer) const {
+ return 0;
+}
+
+sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void*,
+ SkBudgeted) {
+ return nullptr;
+}
+
sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext*, const GrBackendTextureDesc&,
SkAlphaType) {
return nullptr;
SkImage* SkImage::NewTextureFromPixmap(GrContext* ctx, const SkPixmap& pmap, SkBudgeted budgeted) {
return MakeTextureFromPixmap(ctx, pmap, budgeted).release();
}
+
+SkImage* SkImage::NewFromDeferredTextureImageData(GrContext* ctx, const void* data,
+ SkBudgeted budgeted) {
+ return MakeFromDeferredTextureImageData(ctx, data, budgeted).release();
+}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
+class DeferredTextureImage {
+public:
+ SkImage* newImage(GrContext* context, SkBudgeted) const;
+
+private:
+ uint32_t fContextUniqueID;
+ struct Data {
+ SkImageInfo fInfo;
+ void* fPixelData;
+ size_t fRowBytes;
+ int fColorTableCnt;
+ uint32_t* fColorTableData;
+ };
+ Data fData;
+
+ friend class SkImage;
+};
+
+size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy,
+ const DeferredTextureImageUsageParams[],
+ int paramCnt, void* buffer) const {
+ const bool fillMode = SkToBool(buffer);
+ if (fillMode && !SkIsAlign8(reinterpret_cast<intptr_t>(buffer))) {
+ return 0;
+ }
+
+ SkAutoPixmapStorage pixmap;
+ SkImageInfo info;
+ size_t pixelSize = 0;
+ size_t ctSize = 0;
+ int ctCount = 0;
+ if (this->peekPixels(&pixmap)) {
+ info = pixmap.info();
+ pixelSize = SkAlign8(pixmap.getSafeSize());
+ if (pixmap.ctable()) {
+ ctCount = pixmap.ctable()->count();
+ ctSize = SkAlign8(pixmap.ctable()->count() * 4);
+ }
+ } else {
+ // Here we're just using presence of data to know whether there is a codec behind the image.
+ // In the future we will access the cacherator and get the exact data that we want to (e.g.
+ // yuv planes) upload.
+ SkAutoTUnref<SkData> data(this->refEncoded());
+ if (!data) {
+ return 0;
+ }
+ SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
+ info = SkImageInfo::MakeN32(this->width(), this->height(), at);
+ pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr));
+ if (fillMode) {
+ pixmap.alloc(info);
+ if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
+ return 0;
+ }
+ SkASSERT(!pixmap.ctable());
+ }
+ }
+ size_t size = 0;
+ size_t dtiSize = SkAlign8(sizeof(DeferredTextureImage));
+ size += dtiSize;
+ size_t pixelOffset = size;
+ size += pixelSize;
+ size_t ctOffset = size;
+ size += ctSize;
+ if (!fillMode) {
+ return size;
+ }
+ intptr_t bufferAsInt = reinterpret_cast<intptr_t>(buffer);
+ void* pixels = reinterpret_cast<void*>(bufferAsInt + pixelOffset);
+ SkPMColor* ct = nullptr;
+ if (ctSize) {
+ ct = reinterpret_cast<SkPMColor*>(bufferAsInt + ctOffset);
+ }
+
+ memcpy(pixels, pixmap.addr(), pixmap.getSafeSize());
+ if (ctSize) {
+ memcpy(ct, pixmap.ctable()->readColors(), ctSize);
+ }
+
+ SkASSERT(info == pixmap.info());
+ size_t rowBytes = pixmap.rowBytes();
+ DeferredTextureImage* dti = new (buffer) DeferredTextureImage();
+ dti->fContextUniqueID = proxy.fContextUniqueID;
+ dti->fData.fInfo = info;
+ dti->fData.fPixelData = pixels;
+ dti->fData.fRowBytes = rowBytes;
+ dti->fData.fColorTableCnt = ctCount;
+ dti->fData.fColorTableData = ct;
+ return size;
+}
+
+sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void* data,
+ SkBudgeted budgeted) {
+ if (!data) {
+ return nullptr;
+ }
+ const DeferredTextureImage* dti = reinterpret_cast<const DeferredTextureImage*>(data);
+
+ if (!context || context->uniqueID() != dti->fContextUniqueID) {
+ return nullptr;
+ }
+ SkAutoTUnref<SkColorTable> colorTable;
+ if (dti->fData.fColorTableCnt) {
+ SkASSERT(dti->fData.fColorTableData);
+ colorTable.reset(new SkColorTable(dti->fData.fColorTableData, dti->fData.fColorTableCnt));
+ }
+ SkPixmap pixmap;
+ pixmap.reset(dti->fData.fInfo, dti->fData.fPixelData, dti->fData.fRowBytes, colorTable.get());
+ return SkImage::MakeTextureFromPixmap(context, pixmap, budgeted);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
GrTexture* GrDeepCopyTexture(GrTexture* src, SkBudgeted budgeted) {
GrContext* ctx = src->getContext();
}
}
+DEF_GPUTEST_FOR_NATIVE_CONTEXT(DeferredTextureImage, reporter, context, glContext) {
+ SkAutoTUnref<GrContextThreadSafeProxy> proxy(context->threadSafeProxy());
+
+ GrContextFactory otherFactory;
+ GrContextFactory::ContextInfo otherContextInfo =
+ otherFactory.getContextInfo(GrContextFactory::kNative_GLContextType);
+
+ glContext->makeCurrent();
+ REPORTER_ASSERT(reporter, proxy);
+ struct {
+ std::function<SkImage *()> fImageFactory;
+ bool fExpectation;
+ } testCases[] = {
+ { create_image, true },
+ { create_codec_image, true },
+ { create_data_image, true },
+ { create_picture_image, false },
+ { [context] { return create_gpu_image(context); }, false },
+ // Create a texture image in a another GrContext.
+ { [glContext, otherContextInfo] {
+ otherContextInfo.fGLContext->makeCurrent();
+ SkImage *otherContextImage = create_gpu_image(otherContextInfo.fGrContext);
+ glContext->makeCurrent();
+ return otherContextImage;
+ }, false },
+ };
+
+
+ for (auto testCase : testCases) {
+ SkAutoTUnref<SkImage> image(testCase.fImageFactory());
+
+ // This isn't currently used in the implementation, just set any old values.
+ SkImage::DeferredTextureImageUsageParams params;
+ params.fQuality = kLow_SkFilterQuality;
+ params.fMatrix = SkMatrix::I();
+
+ size_t size = image->getDeferredTextureImageData(*proxy, ¶ms, 1, nullptr);
+
+ static const char *const kFS[] = { "fail", "succeed" };
+ if (SkToBool(size) != testCase.fExpectation) {
+ ERRORF(reporter, "This image was expected to %s but did not.",
+ kFS[testCase.fExpectation]);
+ }
+ if (size) {
+ void* buffer = sk_malloc_throw(size);
+ void* misaligned = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(buffer) + 3);
+ if (image->getDeferredTextureImageData(*proxy, ¶ms, 1, misaligned)) {
+ ERRORF(reporter, "Should fail when buffer is misaligned.");
+ }
+ if (!image->getDeferredTextureImageData(*proxy, ¶ms, 1, buffer)) {
+ ERRORF(reporter, "deferred image size succeeded but creation failed.");
+ } else {
+ for (auto budgeted : { SkBudgeted::kNo, SkBudgeted::kYes }) {
+ SkAutoTUnref<SkImage> newImage(
+ SkImage::NewFromDeferredTextureImageData(context, buffer, budgeted));
+ REPORTER_ASSERT(reporter, SkToBool(newImage));
+ if (newImage) {
+ check_images_same(reporter, image, newImage);
+ }
+ // The other context should not be able to create images from texture data
+ // created by the original context.
+ SkAutoTUnref<SkImage> newImage2(SkImage::NewFromDeferredTextureImageData(
+ otherContextInfo.fGrContext, buffer, budgeted));
+ REPORTER_ASSERT(reporter, !newImage2);
+ glContext->makeCurrent();
+ }
+ }
+ sk_free(buffer);
+ }
+ }
+}
#endif