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