/* * 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" #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); } } const GrCaps* DDLPromiseImageHelper::PromiseImageCallbackContext::caps() const { return fContext->contextPriv().caps(); } /////////////////////////////////////////////////////////////////////////////////////////////////// sk_sp DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) { SkSerialProcs procs; procs.fImageCtx = this; procs.fImageProc = [](SkImage* image, void* ctx) -> sk_sp { auto helper = static_cast(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 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(), 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 DDLPromiseImageHelper::reinflateSKP( SkDeferredDisplayListRecorder* recorder, SkData* compressedPictureData, SkTArray>* 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 DDLPromiseImageHelper::PromiseImageCreator(const void* rawData, size_t length, void* ctxIn) { PerRecorderContext* perRecorderContext = static_cast(ctxIn); const DDLPromiseImageHelper* helper = perRecorderContext->fHelper; SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder; SkASSERT(length == sizeof(int)); const int* indexPtr = static_cast(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); const GrCaps* caps = curImage.fCallbackContext->caps(); const GrBackendTexture& backendTex = curImage.fCallbackContext->backendTexture(); GrBackendFormat backendFormat = caps->createFormatFromBackendTexture(backendTex); // 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 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 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; }