Home | History | Annotate | Download | only in tools
      1 /*
      2  * Copyright 2018 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "DDLPromiseImageHelper.h"
      9 
     10 #include "GrContext.h"
     11 #include "GrContextPriv.h"
     12 #include "GrGpu.h"
     13 #include "SkCachedData.h"
     14 #include "SkDeferredDisplayListRecorder.h"
     15 #include "SkImage_Base.h"
     16 #include "SkYUVAIndex.h"
     17 #include "SkYUVASizeInfo.h"
     18 
     19 DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
     20     SkASSERT(fDoneCnt == fNumImages);
     21     SkASSERT(!fUnreleasedFulfills);
     22     SkASSERT(fTotalReleases == fTotalFulfills);
     23     SkASSERT(!fTotalFulfills || fDoneCnt);
     24 
     25     if (fPromiseImageTexture) {
     26         GrGpu* gpu = fContext->contextPriv().getGpu();
     27         gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture->backendTexture());
     28     }
     29 }
     30 
     31 void DDLPromiseImageHelper::PromiseImageCallbackContext::setBackendTexture(
     32         const GrBackendTexture& backendTexture) {
     33     SkASSERT(!fUnreleasedFulfills);
     34     if (fPromiseImageTexture) {
     35         GrGpu* gpu = fContext->contextPriv().getGpu();
     36         gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture->backendTexture());
     37     }
     38     fPromiseImageTexture = SkPromiseImageTexture::Make(backendTexture);
     39 }
     40 
     41 ///////////////////////////////////////////////////////////////////////////////////////////////////
     42 
     43 DDLPromiseImageHelper::~DDLPromiseImageHelper() {}
     44 
     45 sk_sp<SkData> DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) {
     46     SkSerialProcs procs;
     47 
     48     procs.fImageCtx = this;
     49     procs.fImageProc = [](SkImage* image, void* ctx) -> sk_sp<SkData> {
     50         auto helper = static_cast<DDLPromiseImageHelper*>(ctx);
     51 
     52         int id = helper->findOrDefineImage(image);
     53         if (id >= 0) {
     54             SkASSERT(helper->isValidID(id));
     55             return SkData::MakeWithCopy(&id, sizeof(id));
     56         }
     57 
     58         return nullptr;
     59     };
     60 
     61     return inputPicture->serialize(&procs);
     62 }
     63 
     64 // needed until we have SkRG_88_ColorType;
     65 static GrBackendTexture create_yuva_texture(GrGpu* gpu, const SkPixmap& pm,
     66                                             const SkYUVAIndex yuvaIndices[4], int texIndex) {
     67     SkASSERT(texIndex >= 0 && texIndex <= 3);
     68     int channelCount = 0;
     69     for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
     70         if (yuvaIndices[i].fIndex == texIndex) {
     71             ++channelCount;
     72         }
     73     }
     74     // Need to create an RG texture for two-channel planes
     75     GrBackendTexture tex;
     76     if (2 == channelCount) {
     77         SkASSERT(kRGBA_8888_SkColorType == pm.colorType());
     78         SkAutoTMalloc<char> pixels(2 * pm.width()*pm.height());
     79         char* currPixel = pixels;
     80         for (int y = 0; y < pm.height(); ++y) {
     81             for (int x = 0; x < pm.width(); ++x) {
     82                 SkColor color = pm.getColor(x, y);
     83                 currPixel[0] = SkColorGetR(color);
     84                 currPixel[1] = SkColorGetG(color);
     85                 currPixel += 2;
     86             }
     87         }
     88         tex = gpu->createTestingOnlyBackendTexture(
     89             pixels,
     90             pm.width(),
     91             pm.height(),
     92             GrColorType::kRG_88,
     93             false,
     94             GrMipMapped::kNo,
     95             2 * pm.width());
     96     } else {
     97         tex = gpu->createTestingOnlyBackendTexture(
     98             pm.addr(),
     99             pm.width(),
    100             pm.height(),
    101             pm.colorType(),
    102             false,
    103             GrMipMapped::kNo,
    104             pm.rowBytes());
    105     }
    106     return tex;
    107 }
    108 
    109 void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
    110     GrGpu* gpu = context->contextPriv().getGpu();
    111     SkASSERT(gpu);
    112 
    113     for (int i = 0; i < fImageInfo.count(); ++i) {
    114         const PromiseImageInfo& info = fImageInfo[i];
    115 
    116         // DDL TODO: how can we tell if we need mipmapping!
    117         if (info.isYUV()) {
    118             int numPixmaps;
    119             SkAssertResult(SkYUVAIndex::AreValidIndices(info.yuvaIndices(), &numPixmaps));
    120             for (int j = 0; j < numPixmaps; ++j) {
    121                 const SkPixmap& yuvPixmap = info.yuvPixmap(j);
    122 
    123                 sk_sp<PromiseImageCallbackContext> callbackContext(
    124                                                         new PromiseImageCallbackContext(context));
    125 
    126                 callbackContext->setBackendTexture(create_yuva_texture(gpu, yuvPixmap,
    127                                                                        info.yuvaIndices(), j));
    128                 SkASSERT(callbackContext->promiseImageTexture());
    129 
    130                 fImageInfo[i].setCallbackContext(j, std::move(callbackContext));
    131             }
    132         } else {
    133             sk_sp<PromiseImageCallbackContext> callbackContext(
    134                                                     new PromiseImageCallbackContext(context));
    135 
    136             const SkBitmap& bm = info.normalBitmap();
    137 
    138             callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture(
    139                                                                 bm.getPixels(),
    140                                                                 bm.width(),
    141                                                                 bm.height(),
    142                                                                 bm.colorType(),
    143                                                                 false, GrMipMapped::kNo,
    144                                                                 bm.rowBytes()));
    145             // The GMs sometimes request too large an image
    146             //SkAssertResult(callbackContext->backendTexture().isValid());
    147 
    148             fImageInfo[i].setCallbackContext(0, std::move(callbackContext));
    149         }
    150     }
    151 }
    152 
    153 void DDLPromiseImageHelper::replaceEveryOtherPromiseTexture(GrContext* context) {
    154     GrGpu* gpu = context->contextPriv().getGpu();
    155     SkASSERT(gpu);
    156 
    157     for (int i = 0; i < fImageInfo.count(); i += 2) {
    158         PromiseImageInfo& info = fImageInfo[i];
    159 
    160         // DDL TODO: how can we tell if we need mipmapping!
    161         if (info.isYUV()) {
    162             int numPixmaps;
    163             SkAssertResult(SkYUVAIndex::AreValidIndices(info.yuvaIndices(), &numPixmaps));
    164             for (int j = 0; j < numPixmaps; ++j) {
    165                 const SkPixmap& yuvPixmap = info.yuvPixmap(j);
    166                 info.callbackContext(j)->setBackendTexture(
    167                         create_yuva_texture(gpu, yuvPixmap, info.yuvaIndices(), j));
    168                 SkASSERT(info.callbackContext(j)->promiseImageTexture());
    169             }
    170         } else {
    171             const SkBitmap& bm = info.normalBitmap();
    172             info.callbackContext(0)->setBackendTexture(gpu->createTestingOnlyBackendTexture(
    173                     bm.getPixels(), bm.width(), bm.height(), bm.colorType(), false,
    174                     GrMipMapped::kNo, bm.rowBytes()));
    175             // The GMs sometimes request too large an image
    176             // SkAssertResult(callbackContext->backendTexture().isValid());
    177         }
    178     }
    179 }
    180 
    181 sk_sp<SkPicture> DDLPromiseImageHelper::reinflateSKP(
    182                                                    SkDeferredDisplayListRecorder* recorder,
    183                                                    SkData* compressedPictureData,
    184                                                    SkTArray<sk_sp<SkImage>>* promiseImages) const {
    185     PerRecorderContext perRecorderContext { recorder, this, promiseImages };
    186 
    187     SkDeserialProcs procs;
    188     procs.fImageCtx = (void*) &perRecorderContext;
    189     procs.fImageProc = PromiseImageCreator;
    190 
    191     return SkPicture::MakeFromData(compressedPictureData, &procs);
    192 }
    193 
    194 // This generates promise images to replace the indices in the compressed picture. This
    195 // reconstitution is performed separately in each thread so we end up with multiple
    196 // promise images referring to the same GrBackendTexture.
    197 sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
    198                                                           size_t length, void* ctxIn) {
    199     PerRecorderContext* perRecorderContext = static_cast<PerRecorderContext*>(ctxIn);
    200     const DDLPromiseImageHelper* helper = perRecorderContext->fHelper;
    201     SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder;
    202 
    203     SkASSERT(length == sizeof(int));
    204 
    205     const int* indexPtr = static_cast<const int*>(rawData);
    206     SkASSERT(helper->isValidID(*indexPtr));
    207 
    208     const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr);
    209 
    210     if (!curImage.promiseTexture(0)) {
    211         SkASSERT(!curImage.isYUV());
    212         // We weren't able to make a backend texture for this SkImage. In this case we create
    213         // a separate bitmap-backed image for each thread.
    214         SkASSERT(curImage.normalBitmap().isImmutable());
    215         return SkImage::MakeFromBitmap(curImage.normalBitmap());
    216     }
    217     SkASSERT(curImage.index() == *indexPtr);
    218 
    219     sk_sp<SkImage> image;
    220     if (curImage.isYUV()) {
    221         GrBackendFormat backendFormats[SkYUVASizeInfo::kMaxCount];
    222         void* contexts[SkYUVASizeInfo::kMaxCount] = { nullptr, nullptr, nullptr, nullptr };
    223         SkISize sizes[SkYUVASizeInfo::kMaxCount];
    224         // TODO: store this value somewhere?
    225         int textureCount;
    226         SkAssertResult(SkYUVAIndex::AreValidIndices(curImage.yuvaIndices(), &textureCount));
    227         for (int i = 0; i < textureCount; ++i) {
    228             const GrBackendTexture& backendTex = curImage.promiseTexture(i)->backendTexture();
    229             backendFormats[i] = backendTex.getBackendFormat();
    230             SkASSERT(backendFormats[i].isValid());
    231             contexts[i] = curImage.refCallbackContext(i).release();
    232             sizes[i].set(curImage.yuvPixmap(i).width(), curImage.yuvPixmap(i).height());
    233         }
    234         for (int i = textureCount; i < SkYUVASizeInfo::kMaxCount; ++i) {
    235             sizes[i] = SkISize::MakeEmpty();
    236         }
    237 
    238         image = recorder->makeYUVAPromiseTexture(curImage.yuvColorSpace(),
    239                                                  backendFormats,
    240                                                  sizes,
    241                                                  curImage.yuvaIndices(),
    242                                                  curImage.overallWidth(),
    243                                                  curImage.overallHeight(),
    244                                                  GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
    245                                                  curImage.refOverallColorSpace(),
    246                                                  DDLPromiseImageHelper::PromiseImageFulfillProc,
    247                                                  DDLPromiseImageHelper::PromiseImageReleaseProc,
    248                                                  DDLPromiseImageHelper::PromiseImageDoneProc,
    249                                                  contexts,
    250                                                  helper->fDelayReleaseCallback);
    251         for (int i = 0; i < textureCount; ++i) {
    252             curImage.callbackContext(i)->wasAddedToImage();
    253         }
    254     } else {
    255         const GrBackendTexture& backendTex = curImage.promiseTexture(0)->backendTexture();
    256         GrBackendFormat backendFormat = backendTex.getBackendFormat();
    257         SkASSERT(backendFormat.isValid());
    258 
    259         // Each DDL recorder gets its own ref on the promise callback context for the
    260         // promise images it creates.
    261         // DDL TODO: sort out mipmapping
    262         image = recorder->makePromiseTexture(backendFormat,
    263                                              curImage.overallWidth(),
    264                                              curImage.overallHeight(),
    265                                              GrMipMapped::kNo,
    266                                              GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
    267                                              curImage.overallColorType(),
    268                                              curImage.overallAlphaType(),
    269                                              curImage.refOverallColorSpace(),
    270                                              DDLPromiseImageHelper::PromiseImageFulfillProc,
    271                                              DDLPromiseImageHelper::PromiseImageReleaseProc,
    272                                              DDLPromiseImageHelper::PromiseImageDoneProc,
    273                                              (void*)curImage.refCallbackContext(0).release(),
    274                                              helper->fDelayReleaseCallback);
    275         curImage.callbackContext(0)->wasAddedToImage();
    276     }
    277     perRecorderContext->fPromiseImages->push_back(image);
    278     SkASSERT(image);
    279     return image;
    280 }
    281 
    282 int DDLPromiseImageHelper::findImage(SkImage* image) const {
    283     for (int i = 0; i < fImageInfo.count(); ++i) {
    284         if (fImageInfo[i].originalUniqueID() == image->uniqueID()) { // trying to dedup here
    285             SkASSERT(fImageInfo[i].index() == i);
    286             SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].index()));
    287             return i;
    288         }
    289     }
    290     return -1;
    291 }
    292 
    293 int DDLPromiseImageHelper::addImage(SkImage* image) {
    294     SkImage_Base* ib = as_IB(image);
    295 
    296     SkImageInfo overallII = SkImageInfo::Make(image->width(), image->height(),
    297                                               image->colorType(), image->alphaType(),
    298                                               image->refColorSpace());
    299 
    300     PromiseImageInfo& newImageInfo = fImageInfo.emplace_back(fImageInfo.count(),
    301                                                              image->uniqueID(),
    302                                                              overallII);
    303 
    304     SkYUVASizeInfo yuvaSizeInfo;
    305     SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount];
    306     SkYUVColorSpace yuvColorSpace;
    307     const void* planes[SkYUVASizeInfo::kMaxCount];
    308     sk_sp<SkCachedData> yuvData = ib->getPlanes(&yuvaSizeInfo, yuvaIndices, &yuvColorSpace, planes);
    309     if (yuvData) {
    310         newImageInfo.setYUVData(std::move(yuvData), yuvaIndices, yuvColorSpace);
    311 
    312         // determine colortypes from index data
    313         // for testing we only ever use A8 or RGBA8888
    314         SkColorType colorTypes[SkYUVASizeInfo::kMaxCount] = {
    315             kUnknown_SkColorType, kUnknown_SkColorType,
    316             kUnknown_SkColorType, kUnknown_SkColorType
    317         };
    318         for (int yuvIndex = 0; yuvIndex < SkYUVAIndex::kIndexCount; ++yuvIndex) {
    319             int texIdx = yuvaIndices[yuvIndex].fIndex;
    320             if (texIdx < 0) {
    321                 SkASSERT(SkYUVAIndex::kA_Index == yuvIndex);
    322                 continue;
    323             }
    324             if (kUnknown_SkColorType == colorTypes[texIdx]) {
    325                 colorTypes[texIdx] = kAlpha_8_SkColorType;
    326             } else {
    327                 colorTypes[texIdx] = kRGBA_8888_SkColorType;
    328             }
    329         }
    330 
    331         for (int i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) {
    332             if (yuvaSizeInfo.fSizes[i].isEmpty()) {
    333                 SkASSERT(!yuvaSizeInfo.fWidthBytes[i] && kUnknown_SkColorType == colorTypes[i]);
    334                 continue;
    335             }
    336 
    337             SkImageInfo planeII = SkImageInfo::Make(yuvaSizeInfo.fSizes[i].fWidth,
    338                                                     yuvaSizeInfo.fSizes[i].fHeight,
    339                                                     colorTypes[i],
    340                                                     kUnpremul_SkAlphaType);
    341             newImageInfo.addYUVPlane(i, planeII, planes[i], yuvaSizeInfo.fWidthBytes[i]);
    342         }
    343     } else {
    344         sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
    345 
    346         SkBitmap tmp;
    347         tmp.allocPixels(overallII);
    348 
    349         if (!rasterImage->readPixels(tmp.pixmap(), 0, 0)) {
    350             return -1;
    351         }
    352 
    353         tmp.setImmutable();
    354         newImageInfo.setNormalBitmap(tmp);
    355     }
    356     // In either case newImageInfo's PromiseImageCallbackContext is filled in by uploadAllToGPU
    357 
    358     return fImageInfo.count()-1;
    359 }
    360 
    361 int DDLPromiseImageHelper::findOrDefineImage(SkImage* image) {
    362     int preExistingID = this->findImage(image);
    363     if (preExistingID >= 0) {
    364         SkASSERT(this->isValidID(preExistingID));
    365         return preExistingID;
    366     }
    367 
    368     int newID = this->addImage(image);
    369     SkASSERT(this->isValidID(newID));
    370     return newID;
    371 }
    372