1 /*
2  * Copyright 2015 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 "src/gpu/GrResourceProvider.h"
9 
10 #include "include/gpu/GrBackendSemaphore.h"
11 #include "include/gpu/GrContext.h"
12 #include "include/private/GrResourceKey.h"
13 #include "include/private/GrSingleOwner.h"
14 #include "src/core/SkConvertPixels.h"
15 #include "src/core/SkMathPriv.h"
16 #include "src/gpu/GrCaps.h"
17 #include "src/gpu/GrContextPriv.h"
18 #include "src/gpu/GrGpu.h"
19 #include "src/gpu/GrGpuBuffer.h"
20 #include "src/gpu/GrImageInfo.h"
21 #include "src/gpu/GrPath.h"
22 #include "src/gpu/GrPathRendering.h"
23 #include "src/gpu/GrProxyProvider.h"
24 #include "src/gpu/GrRenderTargetPriv.h"
25 #include "src/gpu/GrResourceCache.h"
26 #include "src/gpu/GrSemaphore.h"
27 #include "src/gpu/GrStencilAttachment.h"
28 #include "src/gpu/GrTexturePriv.h"
29 #include "src/gpu/SkGr.h"
30 
31 const uint32_t GrResourceProvider::kMinScratchTextureSize = 16;
32 
33 #define ASSERT_SINGLE_OWNER \
34     SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);)
35 
GrResourceProvider(GrGpu * gpu,GrResourceCache * cache,GrSingleOwner * owner)36 GrResourceProvider::GrResourceProvider(GrGpu* gpu, GrResourceCache* cache, GrSingleOwner* owner)
37         : fCache(cache)
38         , fGpu(gpu)
39 #ifdef SK_DEBUG
40         , fSingleOwner(owner)
41 #endif
42 {
43     fCaps = sk_ref_sp(fGpu->caps());
44 }
45 
createTexture(const GrSurfaceDesc & desc,const GrBackendFormat & format,GrColorType colorType,GrRenderable renderable,int renderTargetSampleCnt,SkBudgeted budgeted,GrProtected isProtected,const GrMipLevel texels[],int mipLevelCount)46 sk_sp<GrTexture> GrResourceProvider::createTexture(const GrSurfaceDesc& desc,
47                                                    const GrBackendFormat& format,
48                                                    GrColorType colorType,
49                                                    GrRenderable renderable,
50                                                    int renderTargetSampleCnt,
51                                                    SkBudgeted budgeted,
52                                                    GrProtected isProtected,
53                                                    const GrMipLevel texels[],
54                                                    int mipLevelCount) {
55     ASSERT_SINGLE_OWNER
56 
57     SkASSERT(mipLevelCount > 0);
58 
59     if (this->isAbandoned()) {
60         return nullptr;
61     }
62 
63     GrMipMapped mipMapped = mipLevelCount > 1 ? GrMipMapped::kYes : GrMipMapped::kNo;
64     if (!fCaps->validateSurfaceParams({desc.fWidth, desc.fHeight}, format, desc.fConfig, renderable,
65                                       renderTargetSampleCnt, mipMapped)) {
66         return nullptr;
67     }
68     // Current rule is that you can provide no level data, just the base, or all the levels.
69     bool hasPixels = mipLevelCount && texels[0].fPixels;
70     auto scratch = this->getExactScratch(desc, format, renderable, renderTargetSampleCnt, budgeted,
71                                          mipMapped, isProtected);
72     if (scratch) {
73         if (!hasPixels) {
74             return scratch;
75         }
76         return this->writePixels(std::move(scratch), colorType, {desc.fWidth, desc.fHeight}, texels,
77                                  mipLevelCount);
78     }
79     SkAutoSTMalloc<14, GrMipLevel> tmpTexels;
80     SkAutoSTArray<14, std::unique_ptr<char[]>> tmpDatas;
81     GrColorType tempColorType = GrColorType::kUnknown;
82     if (hasPixels) {
83         tempColorType = this->prepareLevels(format, colorType, {desc.fWidth, desc.fHeight}, texels,
84                                             mipLevelCount, &tmpTexels, &tmpDatas);
85         if (tempColorType == GrColorType::kUnknown) {
86             return nullptr;
87         }
88     }
89     return fGpu->createTexture(desc, format, renderable, renderTargetSampleCnt, budgeted,
90                                isProtected, colorType, tempColorType, tmpTexels.get(),
91                                mipLevelCount);
92 }
93 
getExactScratch(const GrSurfaceDesc & desc,const GrBackendFormat & format,GrRenderable renderable,int renderTargetSampleCnt,SkBudgeted budgeted,GrMipMapped mipMapped,GrProtected isProtected)94 sk_sp<GrTexture> GrResourceProvider::getExactScratch(const GrSurfaceDesc& desc,
95                                                      const GrBackendFormat& format,
96                                                      GrRenderable renderable,
97                                                      int renderTargetSampleCnt,
98                                                      SkBudgeted budgeted,
99                                                      GrMipMapped mipMapped,
100                                                      GrProtected isProtected) {
101     sk_sp<GrTexture> tex(this->refScratchTexture(desc, format, renderable, renderTargetSampleCnt,
102                                                  mipMapped, isProtected));
103     if (tex && SkBudgeted::kNo == budgeted) {
104         tex->resourcePriv().makeUnbudgeted();
105     }
106 
107     return tex;
108 }
109 
createTexture(const GrSurfaceDesc & desc,const GrBackendFormat & format,GrColorType colorType,GrRenderable renderable,int renderTargetSampleCnt,SkBudgeted budgeted,SkBackingFit fit,GrProtected isProtected,const GrMipLevel & mipLevel)110 sk_sp<GrTexture> GrResourceProvider::createTexture(const GrSurfaceDesc& desc,
111                                                    const GrBackendFormat& format,
112                                                    GrColorType colorType,
113                                                    GrRenderable renderable,
114                                                    int renderTargetSampleCnt,
115                                                    SkBudgeted budgeted,
116                                                    SkBackingFit fit,
117                                                    GrProtected isProtected,
118                                                    const GrMipLevel& mipLevel) {
119     ASSERT_SINGLE_OWNER
120 
121     if (!mipLevel.fPixels) {
122         return nullptr;
123     }
124 
125     if (SkBackingFit::kApprox == fit) {
126         if (this->isAbandoned()) {
127             return nullptr;
128         }
129         if (!fCaps->validateSurfaceParams({desc.fWidth, desc.fHeight}, format, desc.fConfig,
130                                           renderable, renderTargetSampleCnt, GrMipMapped::kNo)) {
131             return nullptr;
132         }
133 
134         auto tex = this->createApproxTexture(desc, format, renderable, renderTargetSampleCnt,
135                                              isProtected);
136         if (!tex) {
137             return nullptr;
138         }
139         return this->writePixels(std::move(tex), colorType, {desc.fWidth, desc.fHeight}, &mipLevel,
140                                  1);
141     } else {
142         return this->createTexture(desc, format, colorType, renderable, renderTargetSampleCnt,
143                                    budgeted, isProtected, &mipLevel, 1);
144     }
145 }
146 
createCompressedTexture(int width,int height,const GrBackendFormat & format,SkImage::CompressionType compression,SkBudgeted budgeted,SkData * data)147 sk_sp<GrTexture> GrResourceProvider::createCompressedTexture(int width, int height,
148                                                              const GrBackendFormat& format,
149                                                              SkImage::CompressionType compression,
150                                                              SkBudgeted budgeted, SkData* data) {
151     ASSERT_SINGLE_OWNER
152     if (this->isAbandoned()) {
153         return nullptr;
154     }
155     return fGpu->createCompressedTexture(width, height, format, compression, budgeted, data->data(),
156                                          data->size());
157 }
158 
createTexture(const GrSurfaceDesc & desc,const GrBackendFormat & format,GrRenderable renderable,int renderTargetSampleCnt,GrMipMapped mipMapped,SkBudgeted budgeted,GrProtected isProtected)159 sk_sp<GrTexture> GrResourceProvider::createTexture(const GrSurfaceDesc& desc,
160                                                    const GrBackendFormat& format,
161                                                    GrRenderable renderable,
162                                                    int renderTargetSampleCnt,
163                                                    GrMipMapped mipMapped,
164                                                    SkBudgeted budgeted,
165                                                    GrProtected isProtected) {
166     ASSERT_SINGLE_OWNER
167     if (this->isAbandoned()) {
168         return nullptr;
169     }
170 
171     if (!fCaps->validateSurfaceParams({desc.fWidth, desc.fHeight}, format, desc.fConfig, renderable,
172                                       renderTargetSampleCnt, mipMapped)) {
173         return nullptr;
174     }
175 
176     // Compressed textures are read-only so they don't support re-use for scratch.
177     // TODO: Support GrMipMapped::kYes in scratch texture lookup here.
178     if (!GrPixelConfigIsCompressed(desc.fConfig)) {
179         sk_sp<GrTexture> tex = this->getExactScratch(
180                 desc, format, renderable, renderTargetSampleCnt, budgeted, mipMapped, isProtected);
181         if (tex) {
182             return tex;
183         }
184     }
185 
186     return fGpu->createTexture(desc, format, renderable, renderTargetSampleCnt, mipMapped, budgeted,
187                                isProtected);
188 }
189 
190 // Map 'value' to a larger multiple of 2. Values <= 'kMagicTol' will pop up to
191 // the next power of 2. Those above 'kMagicTol' will only go up half the floor power of 2.
MakeApprox(uint32_t value)192 uint32_t GrResourceProvider::MakeApprox(uint32_t value) {
193     static const int kMagicTol = 1024;
194 
195     value = SkTMax(kMinScratchTextureSize, value);
196 
197     if (SkIsPow2(value)) {
198         return value;
199     }
200 
201     uint32_t ceilPow2 = GrNextPow2(value);
202     if (value <= kMagicTol) {
203         return ceilPow2;
204     }
205 
206     uint32_t floorPow2 = ceilPow2 >> 1;
207     uint32_t mid = floorPow2 + (floorPow2 >> 1);
208 
209     if (value <= mid) {
210         return mid;
211     }
212 
213     return ceilPow2;
214 }
215 
createApproxTexture(const GrSurfaceDesc & desc,const GrBackendFormat & format,GrRenderable renderable,int renderTargetSampleCnt,GrProtected isProtected)216 sk_sp<GrTexture> GrResourceProvider::createApproxTexture(const GrSurfaceDesc& desc,
217                                                          const GrBackendFormat& format,
218                                                          GrRenderable renderable,
219                                                          int renderTargetSampleCnt,
220                                                          GrProtected isProtected) {
221     ASSERT_SINGLE_OWNER
222 
223     if (this->isAbandoned()) {
224         return nullptr;
225     }
226 
227     // Currently we don't recycle compressed textures as scratch.
228     if (GrPixelConfigIsCompressed(desc.fConfig)) {
229         return nullptr;
230     }
231 
232     if (!fCaps->validateSurfaceParams({desc.fWidth, desc.fHeight}, format, desc.fConfig, renderable,
233                                       renderTargetSampleCnt, GrMipMapped::kNo)) {
234         return nullptr;
235     }
236 
237     // bin by some multiple or power of 2 with a reasonable min
238     GrSurfaceDesc copyDesc(desc);
239     copyDesc.fWidth = MakeApprox(desc.fWidth);
240     copyDesc.fHeight = MakeApprox(desc.fHeight);
241 
242     if (auto tex = this->refScratchTexture(copyDesc, format, renderable, renderTargetSampleCnt,
243                                            GrMipMapped::kNo, isProtected)) {
244         return tex;
245     }
246 
247     return fGpu->createTexture(copyDesc, format, renderable, renderTargetSampleCnt,
248                                GrMipMapped::kNo, SkBudgeted::kYes, isProtected);
249 }
250 
refScratchTexture(const GrSurfaceDesc & desc,const GrBackendFormat & format,GrRenderable renderable,int renderTargetSampleCnt,GrMipMapped mipMapped,GrProtected isProtected)251 sk_sp<GrTexture> GrResourceProvider::refScratchTexture(const GrSurfaceDesc& desc,
252                                                        const GrBackendFormat& format,
253                                                        GrRenderable renderable,
254                                                        int renderTargetSampleCnt,
255                                                        GrMipMapped mipMapped,
256                                                        GrProtected isProtected) {
257     ASSERT_SINGLE_OWNER
258     SkASSERT(!this->isAbandoned());
259     SkASSERT(!GrPixelConfigIsCompressed(desc.fConfig));
260     SkASSERT(fCaps->validateSurfaceParams({desc.fWidth, desc.fHeight}, format, desc.fConfig,
261                                           renderable, renderTargetSampleCnt, GrMipMapped::kNo));
262 
263     // We could make initial clears work with scratch textures but it is a rare case so we just opt
264     // to fall back to making a new texture.
265     if (fGpu->caps()->reuseScratchTextures() || renderable == GrRenderable::kYes) {
266         GrScratchKey key;
267         GrTexturePriv::ComputeScratchKey(desc.fConfig, desc.fWidth, desc.fHeight, renderable,
268                                          renderTargetSampleCnt, mipMapped, isProtected, &key);
269         GrGpuResource* resource = fCache->findAndRefScratchResource(key);
270         if (resource) {
271             fGpu->stats()->incNumScratchTexturesReused();
272             GrSurface* surface = static_cast<GrSurface*>(resource);
273             return sk_sp<GrTexture>(surface->asTexture());
274         }
275     }
276 
277     return nullptr;
278 }
279 
wrapBackendTexture(const GrBackendTexture & tex,GrColorType colorType,GrWrapOwnership ownership,GrWrapCacheable cacheable,GrIOType ioType)280 sk_sp<GrTexture> GrResourceProvider::wrapBackendTexture(const GrBackendTexture& tex,
281                                                         GrColorType colorType,
282                                                         GrWrapOwnership ownership,
283                                                         GrWrapCacheable cacheable,
284                                                         GrIOType ioType) {
285     ASSERT_SINGLE_OWNER
286     if (this->isAbandoned()) {
287         return nullptr;
288     }
289     return fGpu->wrapBackendTexture(tex, colorType, ownership, cacheable, ioType);
290 }
291 
wrapRenderableBackendTexture(const GrBackendTexture & tex,int sampleCnt,GrColorType colorType,GrWrapOwnership ownership,GrWrapCacheable cacheable)292 sk_sp<GrTexture> GrResourceProvider::wrapRenderableBackendTexture(const GrBackendTexture& tex,
293                                                                   int sampleCnt,
294                                                                   GrColorType colorType,
295                                                                   GrWrapOwnership ownership,
296                                                                   GrWrapCacheable cacheable) {
297     ASSERT_SINGLE_OWNER
298     if (this->isAbandoned()) {
299         return nullptr;
300     }
301     return fGpu->wrapRenderableBackendTexture(tex, sampleCnt, colorType, ownership, cacheable);
302 }
303 
wrapBackendRenderTarget(const GrBackendRenderTarget & backendRT,GrColorType colorType)304 sk_sp<GrRenderTarget> GrResourceProvider::wrapBackendRenderTarget(
305         const GrBackendRenderTarget& backendRT, GrColorType colorType)
306 {
307     ASSERT_SINGLE_OWNER
308     return this->isAbandoned() ? nullptr : fGpu->wrapBackendRenderTarget(backendRT, colorType);
309 }
310 
wrapVulkanSecondaryCBAsRenderTarget(const SkImageInfo & imageInfo,const GrVkDrawableInfo & vkInfo)311 sk_sp<GrRenderTarget> GrResourceProvider::wrapVulkanSecondaryCBAsRenderTarget(
312         const SkImageInfo& imageInfo, const GrVkDrawableInfo& vkInfo) {
313     ASSERT_SINGLE_OWNER
314     return this->isAbandoned() ? nullptr : fGpu->wrapVulkanSecondaryCBAsRenderTarget(imageInfo,
315                                                                                      vkInfo);
316 
317 }
318 
assignUniqueKeyToResource(const GrUniqueKey & key,GrGpuResource * resource)319 void GrResourceProvider::assignUniqueKeyToResource(const GrUniqueKey& key,
320                                                    GrGpuResource* resource) {
321     ASSERT_SINGLE_OWNER
322     if (this->isAbandoned() || !resource) {
323         return;
324     }
325     resource->resourcePriv().setUniqueKey(key);
326 }
327 
findResourceByUniqueKey(const GrUniqueKey & key)328 sk_sp<GrGpuResource> GrResourceProvider::findResourceByUniqueKey(const GrUniqueKey& key) {
329     ASSERT_SINGLE_OWNER
330     return this->isAbandoned() ? nullptr
331                                : sk_sp<GrGpuResource>(fCache->findAndRefUniqueResource(key));
332 }
333 
findOrMakeStaticBuffer(GrGpuBufferType intendedType,size_t size,const void * data,const GrUniqueKey & key)334 sk_sp<const GrGpuBuffer> GrResourceProvider::findOrMakeStaticBuffer(GrGpuBufferType intendedType,
335                                                                     size_t size,
336                                                                     const void* data,
337                                                                     const GrUniqueKey& key) {
338     if (auto buffer = this->findByUniqueKey<GrGpuBuffer>(key)) {
339         return buffer;
340     }
341     if (auto buffer = this->createBuffer(size, intendedType, kStatic_GrAccessPattern, data)) {
342         // We shouldn't bin and/or cache static buffers.
343         SkASSERT(buffer->size() == size);
344         SkASSERT(!buffer->resourcePriv().getScratchKey().isValid());
345         buffer->resourcePriv().setUniqueKey(key);
346         return sk_sp<const GrGpuBuffer>(buffer);
347     }
348     return nullptr;
349 }
350 
createPatternedIndexBuffer(const uint16_t * pattern,int patternSize,int reps,int vertCount,const GrUniqueKey * key)351 sk_sp<const GrGpuBuffer> GrResourceProvider::createPatternedIndexBuffer(const uint16_t* pattern,
352                                                                         int patternSize,
353                                                                         int reps,
354                                                                         int vertCount,
355                                                                         const GrUniqueKey* key) {
356     size_t bufferSize = patternSize * reps * sizeof(uint16_t);
357 
358     sk_sp<GrGpuBuffer> buffer(
359             this->createBuffer(bufferSize, GrGpuBufferType::kIndex, kStatic_GrAccessPattern));
360     if (!buffer) {
361         return nullptr;
362     }
363     uint16_t* data = (uint16_t*) buffer->map();
364     SkAutoTArray<uint16_t> temp;
365     if (!data) {
366         temp.reset(reps * patternSize);
367         data = temp.get();
368     }
369     for (int i = 0; i < reps; ++i) {
370         int baseIdx = i * patternSize;
371         uint16_t baseVert = (uint16_t)(i * vertCount);
372         for (int j = 0; j < patternSize; ++j) {
373             data[baseIdx+j] = baseVert + pattern[j];
374         }
375     }
376     if (temp.get()) {
377         if (!buffer->updateData(data, bufferSize)) {
378             return nullptr;
379         }
380     } else {
381         buffer->unmap();
382     }
383     if (key) {
384         SkASSERT(key->isValid());
385         this->assignUniqueKeyToResource(*key, buffer.get());
386     }
387     return buffer;
388 }
389 
390 static constexpr int kMaxQuads = 1 << 12;  // max possible: (1 << 14) - 1;
391 
createQuadIndexBuffer()392 sk_sp<const GrGpuBuffer> GrResourceProvider::createQuadIndexBuffer() {
393     GR_STATIC_ASSERT(4 * kMaxQuads <= 65535);
394     static const uint16_t kPattern[] = { 0, 1, 2, 2, 1, 3 };
395     return this->createPatternedIndexBuffer(kPattern, 6, kMaxQuads, 4, nullptr);
396 }
397 
QuadCountOfQuadBuffer()398 int GrResourceProvider::QuadCountOfQuadBuffer() { return kMaxQuads; }
399 
createPath(const SkPath & path,const GrStyle & style)400 sk_sp<GrPath> GrResourceProvider::createPath(const SkPath& path, const GrStyle& style) {
401     if (this->isAbandoned()) {
402         return nullptr;
403     }
404 
405     SkASSERT(this->gpu()->pathRendering());
406     return this->gpu()->pathRendering()->createPath(path, style);
407 }
408 
createBuffer(size_t size,GrGpuBufferType intendedType,GrAccessPattern accessPattern,const void * data)409 sk_sp<GrGpuBuffer> GrResourceProvider::createBuffer(size_t size, GrGpuBufferType intendedType,
410                                                     GrAccessPattern accessPattern,
411                                                     const void* data) {
412     if (this->isAbandoned()) {
413         return nullptr;
414     }
415     if (kDynamic_GrAccessPattern != accessPattern) {
416         return this->gpu()->createBuffer(size, intendedType, accessPattern, data);
417     }
418     // bin by pow2 with a reasonable min
419     static const size_t MIN_SIZE = 1 << 12;
420     size_t allocSize = SkTMax(MIN_SIZE, GrNextSizePow2(size));
421 
422     GrScratchKey key;
423     GrGpuBuffer::ComputeScratchKeyForDynamicVBO(allocSize, intendedType, &key);
424     auto buffer =
425             sk_sp<GrGpuBuffer>(static_cast<GrGpuBuffer*>(this->cache()->findAndRefScratchResource(
426                     key)));
427     if (!buffer) {
428         buffer = this->gpu()->createBuffer(allocSize, intendedType, kDynamic_GrAccessPattern);
429         if (!buffer) {
430             return nullptr;
431         }
432     }
433     if (data) {
434         buffer->updateData(data, size);
435     }
436     return buffer;
437 }
438 
attachStencilAttachment(GrRenderTarget * rt,int minStencilSampleCount)439 bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt, int minStencilSampleCount) {
440     SkASSERT(rt);
441     GrStencilAttachment* stencil = rt->renderTargetPriv().getStencilAttachment();
442     if (stencil && stencil->numSamples() >= minStencilSampleCount) {
443         return true;
444     }
445 
446     if (!rt->wasDestroyed() && rt->canAttemptStencilAttachment()) {
447         GrUniqueKey sbKey;
448 
449         int width = rt->width();
450         int height = rt->height();
451 #if 0
452         if (this->caps()->oversizedStencilSupport()) {
453             width  = SkNextPow2(width);
454             height = SkNextPow2(height);
455         }
456 #endif
457         GrStencilAttachment::ComputeSharedStencilAttachmentKey(
458                 width, height, minStencilSampleCount, &sbKey);
459         auto stencil = this->findByUniqueKey<GrStencilAttachment>(sbKey);
460         if (!stencil) {
461             // Need to try and create a new stencil
462             stencil.reset(this->gpu()->createStencilAttachmentForRenderTarget(
463                     rt, width, height, minStencilSampleCount));
464             if (!stencil) {
465                 return false;
466             }
467             this->assignUniqueKeyToResource(sbKey, stencil.get());
468         }
469         rt->renderTargetPriv().attachStencilAttachment(std::move(stencil));
470     }
471 
472     if (GrStencilAttachment* stencil = rt->renderTargetPriv().getStencilAttachment()) {
473         return stencil->numSamples() >= minStencilSampleCount;
474     }
475     return false;
476 }
477 
wrapBackendTextureAsRenderTarget(const GrBackendTexture & tex,int sampleCnt,GrColorType colorType)478 sk_sp<GrRenderTarget> GrResourceProvider::wrapBackendTextureAsRenderTarget(
479         const GrBackendTexture& tex, int sampleCnt, GrColorType colorType)
480 {
481     if (this->isAbandoned()) {
482         return nullptr;
483     }
484     return fGpu->wrapBackendTextureAsRenderTarget(tex, sampleCnt, colorType);
485 }
486 
makeSemaphore(bool isOwned)487 sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrResourceProvider::makeSemaphore(bool isOwned) {
488     return fGpu->makeSemaphore(isOwned);
489 }
490 
wrapBackendSemaphore(const GrBackendSemaphore & semaphore,SemaphoreWrapType wrapType,GrWrapOwnership ownership)491 sk_sp<GrSemaphore> GrResourceProvider::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
492                                                             SemaphoreWrapType wrapType,
493                                                             GrWrapOwnership ownership) {
494     ASSERT_SINGLE_OWNER
495     return this->isAbandoned() ? nullptr : fGpu->wrapBackendSemaphore(semaphore,
496                                                                       wrapType,
497                                                                       ownership);
498 }
499 
500 // Ensures the row bytes are populated (not 0) and makes a copy to a temporary
501 // to make the row bytes tight if necessary. Returns false if the input row bytes are invalid.
prepare_level(const GrMipLevel & inLevel,const SkISize & size,bool rowBytesSupport,GrColorType origColorType,GrColorType allowedColorType,GrMipLevel * outLevel,std::unique_ptr<char[]> * data)502 static bool prepare_level(const GrMipLevel& inLevel,
503                           const SkISize& size,
504                           bool rowBytesSupport,
505                           GrColorType origColorType,
506                           GrColorType allowedColorType,
507                           GrMipLevel* outLevel,
508                           std::unique_ptr<char[]>* data) {
509     if (!inLevel.fPixels) {
510         outLevel->fPixels = nullptr;
511         outLevel->fRowBytes = 0;
512         return true;
513     }
514     size_t minRB = size.fWidth * GrColorTypeBytesPerPixel(origColorType);
515     size_t actualRB = inLevel.fRowBytes ? inLevel.fRowBytes : minRB;
516     if (actualRB < minRB) {
517         return false;
518     }
519     if (origColorType == allowedColorType && (actualRB == minRB || rowBytesSupport)) {
520         outLevel->fRowBytes = actualRB;
521         outLevel->fPixels = inLevel.fPixels;
522         return true;
523     }
524     auto tempRB = size.fWidth * GrColorTypeBytesPerPixel(allowedColorType);
525     data->reset(new char[tempRB * size.fHeight]);
526     outLevel->fPixels = data->get();
527     outLevel->fRowBytes = tempRB;
528     GrImageInfo srcInfo(origColorType,    kUnpremul_SkAlphaType, nullptr, size);
529     GrImageInfo dstInfo(allowedColorType, kUnpremul_SkAlphaType, nullptr, size);
530     return GrConvertPixels(dstInfo, data->get(), tempRB, srcInfo, inLevel.fPixels, actualRB);
531 }
532 
prepareLevels(const GrBackendFormat & format,GrColorType colorType,const SkISize & baseSize,const GrMipLevel texels[],int mipLevelCount,TempLevels * tempLevels,TempLevelDatas * tempLevelDatas) const533 GrColorType GrResourceProvider::prepareLevels(const GrBackendFormat& format,
534                                               GrColorType colorType,
535                                               const SkISize& baseSize,
536                                               const GrMipLevel texels[],
537                                               int mipLevelCount,
538                                               TempLevels* tempLevels,
539                                               TempLevelDatas* tempLevelDatas) const {
540     SkASSERT(mipLevelCount && texels && texels[0].fPixels);
541 
542     auto allowedColorType =
543             this->caps()->supportedWritePixelsColorType(colorType, format, colorType).fColorType;
544     if (allowedColorType == GrColorType::kUnknown) {
545         return GrColorType::kUnknown;
546     }
547     bool rowBytesSupport = this->caps()->writePixelsRowBytesSupport();
548     tempLevels->reset(mipLevelCount);
549     tempLevelDatas->reset(mipLevelCount);
550     auto size = baseSize;
551     for (int i = 0; i < mipLevelCount; ++i) {
552         if (!prepare_level(texels[i], size, rowBytesSupport, colorType, allowedColorType,
553                            &(*tempLevels)[i], &(*tempLevelDatas)[i])) {
554             return GrColorType::kUnknown;
555         }
556         size = {std::max(size.fWidth / 2, 1), std::max(size.fHeight / 2, 1)};
557     }
558     return allowedColorType;
559 }
560 
writePixels(sk_sp<GrTexture> texture,GrColorType colorType,const SkISize & baseSize,const GrMipLevel texels[],int mipLevelCount) const561 sk_sp<GrTexture> GrResourceProvider::writePixels(sk_sp<GrTexture> texture,
562                                                  GrColorType colorType,
563                                                  const SkISize& baseSize,
564                                                  const GrMipLevel texels[],
565                                                  int mipLevelCount) const {
566     SkASSERT(!this->isAbandoned());
567     SkASSERT(texture);
568     SkASSERT(colorType != GrColorType::kUnknown);
569     SkASSERT(mipLevelCount && texels && texels[0].fPixels);
570 
571     SkAutoSTMalloc<14, GrMipLevel> tmpTexels;
572     SkAutoSTArray<14, std::unique_ptr<char[]>> tmpDatas;
573     auto tempColorType = this->prepareLevels(texture->backendFormat(), colorType, baseSize, texels,
574                                              mipLevelCount, &tmpTexels, &tmpDatas);
575     if (tempColorType == GrColorType::kUnknown) {
576         return nullptr;
577     }
578     SkAssertResult(fGpu->writePixels(texture.get(), 0, 0, baseSize.fWidth, baseSize.fHeight,
579                                      colorType, tempColorType, tmpTexels.get(), mipLevelCount));
580     return texture;
581 }
582