diff options
Diffstat (limited to 'tools/DDLPromiseImageHelper.cpp')
-rw-r--r-- | tools/DDLPromiseImageHelper.cpp | 181 |
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 |