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