blob: 1eed5653312f98a3e9948804e9573599a2abe497 [file] [log] [blame]
Robert Phillips96601082018-05-29 16:13:26 -04001/*
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#if SK_SUPPORT_GPU
11
12#include "GrContext.h"
13#include "GrContextPriv.h"
14#include "GrGpu.h"
15#include "SkDeferredDisplayListRecorder.h"
16
17DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
18 GrGpu* gpu = fContext->contextPriv().getGpu();
19
20 if (fBackendTexture.isValid()) {
21 gpu->deleteTestingOnlyBackendTexture(fBackendTexture);
22 }
23}
24
25///////////////////////////////////////////////////////////////////////////////////////////////////
26
27sk_sp<SkData> DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) {
28 SkSerialProcs procs;
29
30 procs.fImageCtx = this;
31 procs.fImageProc = [](SkImage* image, void* ctx) -> sk_sp<SkData> {
32 auto helper = static_cast<DDLPromiseImageHelper*>(ctx);
33
34 int id = helper->findOrDefineImage(image);
35 if (id >= 0) {
36 SkASSERT(helper->isValidID(id));
37 return SkData::MakeWithCopy(&id, sizeof(id));
38 }
39
40 return nullptr;
41 };
42
43 return inputPicture->serialize(&procs);
44}
45
46void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
47 GrGpu* gpu = context->contextPriv().getGpu();
48 SkASSERT(gpu);
49
50 for (int i = 0; i < fImageInfo.count(); ++i) {
51 sk_sp<PromiseImageCallbackContext> callbackContext(
52 new PromiseImageCallbackContext(context));
53
54 const PromiseImageInfo& info = fImageInfo[i];
55
56 // DDL TODO: how can we tell if we need mipmapping!
57 callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture(
58 info.fBitmap.getPixels(),
59 info.fBitmap.width(),
60 info.fBitmap.height(),
61 info.fBitmap.colorType(),
62 info.fBitmap.colorSpace(),
63 false, GrMipMapped::kNo));
64 // The GMs sometimes request too large an image
65 //SkAssertResult(callbackContext->backendTexture().isValid());
66
67 // The fImageInfo array gets the creation ref
68 fImageInfo[i].fCallbackContext = std::move(callbackContext);
69 }
70}
71
72sk_sp<SkPicture> DDLPromiseImageHelper::reinflateSKP(
73 SkDeferredDisplayListRecorder* recorder,
74 SkData* compressedPictureData,
75 SkTArray<sk_sp<SkImage>>* promiseImages) const {
76 PerRecorderContext perRecorderContext { recorder, this, promiseImages };
77
78 SkDeserialProcs procs;
79 procs.fImageCtx = (void*) &perRecorderContext;
80 procs.fImageProc = PromiseImageCreator;
81
82 return SkPicture::MakeFromData(compressedPictureData, &procs);
83}
84
85// This generates promise images to replace the indices in the compressed picture. This
86// reconstitution is performed separately in each thread so we end up with multiple
87// promise images referring to the same GrBackendTexture.
88sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
89 size_t length, void* ctxIn) {
90 PerRecorderContext* perRecorderContext = static_cast<PerRecorderContext*>(ctxIn);
91 const DDLPromiseImageHelper* helper = perRecorderContext->fHelper;
92 SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder;
93
94 SkASSERT(length == sizeof(int));
95
96 const int* indexPtr = static_cast<const int*>(rawData);
97 SkASSERT(helper->isValidID(*indexPtr));
98
99 const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr);
100
101 if (!curImage.fCallbackContext->backendTexture().isValid()) {
102 // We weren't able to make a backend texture for this SkImage. In this case we create
103 // a separate bitmap-backed image for each thread.
104 // Note: we would like to share the same bitmap between all the threads but
105 // SkBitmap is not thread-safe.
106 return SkImage::MakeRasterCopy(curImage.fBitmap.pixmap());
107 }
108 SkASSERT(curImage.fIndex == *indexPtr);
109
110 GrBackendFormat backendFormat = curImage.fCallbackContext->backendTexture().format();
111
112 // Each DDL recorder gets its own ref on the promise callback context for the
113 // promise images it creates.
114 // DDL TODO: sort out mipmapping
115 sk_sp<SkImage> image = recorder->makePromiseTexture(
116 backendFormat,
117 curImage.fBitmap.width(),
118 curImage.fBitmap.height(),
119 GrMipMapped::kNo,
120 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
121 curImage.fBitmap.colorType(),
122 curImage.fBitmap.alphaType(),
123 curImage.fBitmap.refColorSpace(),
124 DDLPromiseImageHelper::PromiseImageFulfillProc,
125 DDLPromiseImageHelper::PromiseImageReleaseProc,
126 DDLPromiseImageHelper::PromiseImageDoneProc,
127 (void*) SkSafeRef(curImage.fCallbackContext.get()));
128 perRecorderContext->fPromiseImages->push_back(image);
129 SkASSERT(image);
130 return image;
131}
132
133int DDLPromiseImageHelper::findImage(SkImage* image) const {
134 for (int i = 0; i < fImageInfo.count(); ++i) {
135 if (fImageInfo[i].fOriginalUniqueID == image->uniqueID()) { // trying to dedup here
136 SkASSERT(fImageInfo[i].fIndex == i);
137 SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].fIndex));
138 return i;
139 }
140 }
141 return -1;
142}
143
144int DDLPromiseImageHelper::addImage(SkImage* image) {
145 sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
146
147 SkImageInfo ii = SkImageInfo::Make(rasterImage->width(), rasterImage->height(),
148 rasterImage->colorType(), rasterImage->alphaType(),
149 rasterImage->refColorSpace());
150
151 SkBitmap bm;
152 bm.allocPixels(ii);
153
154 if (!rasterImage->readPixels(bm.pixmap(), 0, 0)) {
155 return -1;
156 }
157
158 bm.setImmutable();
159
160 PromiseImageInfo& newImageInfo = fImageInfo.push_back();
161 newImageInfo.fIndex = fImageInfo.count()-1;
162 newImageInfo.fOriginalUniqueID = image->uniqueID();
163 newImageInfo.fBitmap = bm;
164 /* fCallbackContext is filled in by uploadAllToGPU */
165
166 return fImageInfo.count()-1;
167}
168
169int DDLPromiseImageHelper::findOrDefineImage(SkImage* image) {
170 int preExistingID = this->findImage(image);
171 if (preExistingID >= 0) {
172 SkASSERT(this->isValidID(preExistingID));
173 return preExistingID;
174 }
175
176 int newID = this->addImage(image);
177 SkASSERT(this->isValidID(newID));
178 return newID;
179}
180
181#endif