aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/DDLPromiseImageHelper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/DDLPromiseImageHelper.cpp')
-rw-r--r--tools/DDLPromiseImageHelper.cpp181
1 files changed, 181 insertions, 0 deletions
diff --git a/tools/DDLPromiseImageHelper.cpp b/tools/DDLPromiseImageHelper.cpp
new file mode 100644
index 0000000000..1eed565331
--- /dev/null
+++ b/tools/DDLPromiseImageHelper.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "DDLPromiseImageHelper.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrGpu.h"
+#include "SkDeferredDisplayListRecorder.h"
+
+DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
+ GrGpu* gpu = fContext->contextPriv().getGpu();
+
+ if (fBackendTexture.isValid()) {
+ gpu->deleteTestingOnlyBackendTexture(fBackendTexture);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkData> DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) {
+ SkSerialProcs procs;
+
+ procs.fImageCtx = this;
+ procs.fImageProc = [](SkImage* image, void* ctx) -> sk_sp<SkData> {
+ auto helper = static_cast<DDLPromiseImageHelper*>(ctx);
+
+ int id = helper->findOrDefineImage(image);
+ if (id >= 0) {
+ SkASSERT(helper->isValidID(id));
+ return SkData::MakeWithCopy(&id, sizeof(id));
+ }
+
+ return nullptr;
+ };
+
+ return inputPicture->serialize(&procs);
+}
+
+void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
+ GrGpu* gpu = context->contextPriv().getGpu();
+ SkASSERT(gpu);
+
+ for (int i = 0; i < fImageInfo.count(); ++i) {
+ sk_sp<PromiseImageCallbackContext> callbackContext(
+ new PromiseImageCallbackContext(context));
+
+ const PromiseImageInfo& info = fImageInfo[i];
+
+ // DDL TODO: how can we tell if we need mipmapping!
+ callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture(
+ info.fBitmap.getPixels(),
+ info.fBitmap.width(),
+ info.fBitmap.height(),
+ info.fBitmap.colorType(),
+ info.fBitmap.colorSpace(),
+ false, GrMipMapped::kNo));
+ // The GMs sometimes request too large an image
+ //SkAssertResult(callbackContext->backendTexture().isValid());
+
+ // The fImageInfo array gets the creation ref
+ fImageInfo[i].fCallbackContext = std::move(callbackContext);
+ }
+}
+
+sk_sp<SkPicture> DDLPromiseImageHelper::reinflateSKP(
+ SkDeferredDisplayListRecorder* recorder,
+ SkData* compressedPictureData,
+ SkTArray<sk_sp<SkImage>>* promiseImages) const {
+ PerRecorderContext perRecorderContext { recorder, this, promiseImages };
+
+ SkDeserialProcs procs;
+ procs.fImageCtx = (void*) &perRecorderContext;
+ procs.fImageProc = PromiseImageCreator;
+
+ return SkPicture::MakeFromData(compressedPictureData, &procs);
+}
+
+// This generates promise images to replace the indices in the compressed picture. This
+// reconstitution is performed separately in each thread so we end up with multiple
+// promise images referring to the same GrBackendTexture.
+sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
+ size_t length, void* ctxIn) {
+ PerRecorderContext* perRecorderContext = static_cast<PerRecorderContext*>(ctxIn);
+ const DDLPromiseImageHelper* helper = perRecorderContext->fHelper;
+ SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder;
+
+ SkASSERT(length == sizeof(int));
+
+ const int* indexPtr = static_cast<const int*>(rawData);
+ SkASSERT(helper->isValidID(*indexPtr));
+
+ const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr);
+
+ if (!curImage.fCallbackContext->backendTexture().isValid()) {
+ // We weren't able to make a backend texture for this SkImage. In this case we create
+ // a separate bitmap-backed image for each thread.
+ // Note: we would like to share the same bitmap between all the threads but
+ // SkBitmap is not thread-safe.
+ return SkImage::MakeRasterCopy(curImage.fBitmap.pixmap());
+ }
+ SkASSERT(curImage.fIndex == *indexPtr);
+
+ GrBackendFormat backendFormat = curImage.fCallbackContext->backendTexture().format();
+
+ // Each DDL recorder gets its own ref on the promise callback context for the
+ // promise images it creates.
+ // DDL TODO: sort out mipmapping
+ sk_sp<SkImage> image = recorder->makePromiseTexture(
+ backendFormat,
+ curImage.fBitmap.width(),
+ curImage.fBitmap.height(),
+ GrMipMapped::kNo,
+ GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
+ curImage.fBitmap.colorType(),
+ curImage.fBitmap.alphaType(),
+ curImage.fBitmap.refColorSpace(),
+ DDLPromiseImageHelper::PromiseImageFulfillProc,
+ DDLPromiseImageHelper::PromiseImageReleaseProc,
+ DDLPromiseImageHelper::PromiseImageDoneProc,
+ (void*) SkSafeRef(curImage.fCallbackContext.get()));
+ perRecorderContext->fPromiseImages->push_back(image);
+ SkASSERT(image);
+ return image;
+}
+
+int DDLPromiseImageHelper::findImage(SkImage* image) const {
+ for (int i = 0; i < fImageInfo.count(); ++i) {
+ if (fImageInfo[i].fOriginalUniqueID == image->uniqueID()) { // trying to dedup here
+ SkASSERT(fImageInfo[i].fIndex == i);
+ SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].fIndex));
+ return i;
+ }
+ }
+ return -1;
+}
+
+int DDLPromiseImageHelper::addImage(SkImage* image) {
+ sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
+
+ SkImageInfo ii = SkImageInfo::Make(rasterImage->width(), rasterImage->height(),
+ rasterImage->colorType(), rasterImage->alphaType(),
+ rasterImage->refColorSpace());
+
+ SkBitmap bm;
+ bm.allocPixels(ii);
+
+ if (!rasterImage->readPixels(bm.pixmap(), 0, 0)) {
+ return -1;
+ }
+
+ bm.setImmutable();
+
+ PromiseImageInfo& newImageInfo = fImageInfo.push_back();
+ newImageInfo.fIndex = fImageInfo.count()-1;
+ newImageInfo.fOriginalUniqueID = image->uniqueID();
+ newImageInfo.fBitmap = bm;
+ /* fCallbackContext is filled in by uploadAllToGPU */
+
+ return fImageInfo.count()-1;
+}
+
+int DDLPromiseImageHelper::findOrDefineImage(SkImage* image) {
+ int preExistingID = this->findImage(image);
+ if (preExistingID >= 0) {
+ SkASSERT(this->isValidID(preExistingID));
+ return preExistingID;
+ }
+
+ int newID = this->addImage(image);
+ SkASSERT(this->isValidID(newID));
+ return newID;
+}
+
+#endif