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 | |
30 | const int GrResourceProvider::kMinScratchTextureSize = 16; |
31 | |
32 | #define ASSERT_SINGLE_OWNER GR_ASSERT_SINGLE_OWNER(fSingleOwner) |
33 | |
34 | GrResourceProvider::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 | |
44 | sk_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 | |
91 | sk_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 | |
107 | sk_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 | |
143 | sk_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 | |
157 | sk_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. |
192 | SkISize 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 | |
219 | sk_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 | |
250 | sk_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 | |
279 | sk_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 | |
290 | sk_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 | |
302 | sk_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 | |
313 | sk_sp<GrRenderTarget> GrResourceProvider::wrapBackendRenderTarget( |
314 | const GrBackendRenderTarget& backendRT) { |
315 | ASSERT_SINGLE_OWNER |
316 | return this->isAbandoned() ? nullptr : fGpu->wrapBackendRenderTarget(backendRT); |
317 | } |
318 | |
319 | sk_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 | |
327 | void 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 | |
336 | sk_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 | |
342 | sk_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 | |
359 | sk_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 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
399 | static constexpr int kMaxNumNonAAQuads = 1 << 12; // max possible: (1 << 14) - 1; |
400 | static const int kVertsPerNonAAQuad = 4; |
401 | static const int kIndicesPerNonAAQuad = 6; |
402 | |
403 | sk_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 | |
416 | int GrResourceProvider::MaxNumNonAAQuads() { return kMaxNumNonAAQuads; } |
417 | int GrResourceProvider::NumVertsPerNonAAQuad() { return kVertsPerNonAAQuad; } |
418 | int GrResourceProvider::NumIndicesPerNonAAQuad() { return kIndicesPerNonAAQuad; } |
419 | |
420 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
421 | static constexpr int kMaxNumAAQuads = 1 << 9; // max possible: (1 << 13) - 1; |
422 | static const int kVertsPerAAQuad = 8; |
423 | static const int kIndicesPerAAQuad = 30; |
424 | |
425 | sk_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 | |
444 | int GrResourceProvider::MaxNumAAQuads() { return kMaxNumAAQuads; } |
445 | int GrResourceProvider::NumVertsPerAAQuad() { return kVertsPerAAQuad; } |
446 | int GrResourceProvider::NumIndicesPerAAQuad() { return kIndicesPerAAQuad; } |
447 | |
448 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
449 | sk_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 | |
458 | sk_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 | |
488 | bool 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 | |
527 | sk_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 | |
535 | std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT GrResourceProvider::makeSemaphore( |
536 | bool isOwned) { |
537 | return this->isAbandoned() ? nullptr : fGpu->makeSemaphore(isOwned); |
538 | } |
539 | |
540 | std::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. |
552 | static 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 | |
583 | GrColorType 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 | |
611 | sk_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 | |