|  | /* | 
|  | * Copyright 2015 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "src/gpu/GrResourceProvider.h" | 
|  |  | 
|  | #include "include/gpu/GrBackendSemaphore.h" | 
|  | #include "include/private/GrResourceKey.h" | 
|  | #include "include/private/GrSingleOwner.h" | 
|  | #include "src/core/SkConvertPixels.h" | 
|  | #include "src/core/SkMathPriv.h" | 
|  | #include "src/core/SkMipmap.h" | 
|  | #include "src/gpu/GrAttachment.h" | 
|  | #include "src/gpu/GrCaps.h" | 
|  | #include "src/gpu/GrDataUtils.h" | 
|  | #include "src/gpu/GrGpu.h" | 
|  | #include "src/gpu/GrGpuBuffer.h" | 
|  | #include "src/gpu/GrImageInfo.h" | 
|  | #include "src/gpu/GrPath.h" | 
|  | #include "src/gpu/GrPathRendering.h" | 
|  | #include "src/gpu/GrProxyProvider.h" | 
|  | #include "src/gpu/GrRenderTarget.h" | 
|  | #include "src/gpu/GrResourceCache.h" | 
|  | #include "src/gpu/GrSemaphore.h" | 
|  | #include "src/gpu/GrTexture.h" | 
|  | #include "src/gpu/SkGr.h" | 
|  |  | 
|  | const int GrResourceProvider::kMinScratchTextureSize = 16; | 
|  |  | 
|  | #define ASSERT_SINGLE_OWNER GR_ASSERT_SINGLE_OWNER(fSingleOwner) | 
|  |  | 
|  | GrResourceProvider::GrResourceProvider(GrGpu* gpu, GrResourceCache* cache, GrSingleOwner* owner) | 
|  | : fCache(cache) | 
|  | , fGpu(gpu) | 
|  | #ifdef SK_DEBUG | 
|  | , fSingleOwner(owner) | 
|  | #endif | 
|  | { | 
|  | fCaps = sk_ref_sp(fGpu->caps()); | 
|  | } | 
|  |  | 
|  | sk_sp<GrTexture> GrResourceProvider::createTexture(SkISize dimensions, | 
|  | const GrBackendFormat& format, | 
|  | GrColorType colorType, | 
|  | GrRenderable renderable, | 
|  | int renderTargetSampleCnt, | 
|  | SkBudgeted budgeted, | 
|  | GrMipMapped mipMapped, | 
|  | GrProtected isProtected, | 
|  | const GrMipLevel texels[]) { | 
|  | ASSERT_SINGLE_OWNER | 
|  |  | 
|  | if (this->isAbandoned()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | int numMipLevels = 1; | 
|  | if (mipMapped == GrMipMapped::kYes) { | 
|  | numMipLevels = SkMipmap::ComputeLevelCount(dimensions.fWidth, dimensions.fHeight) + 1; | 
|  | } | 
|  |  | 
|  | if (!fCaps->validateSurfaceParams(dimensions, format, renderable, renderTargetSampleCnt, | 
|  | mipMapped)) { | 
|  | return nullptr; | 
|  | } | 
|  | // Current rule is that you can provide no level data, just the base, or all the levels. | 
|  | bool hasPixels = texels[0].fPixels; | 
|  | auto scratch = this->getExactScratch(dimensions, format, renderable, renderTargetSampleCnt, | 
|  | budgeted, mipMapped, isProtected); | 
|  | if (scratch) { | 
|  | if (!hasPixels) { | 
|  | return scratch; | 
|  | } | 
|  | return this->writePixels(std::move(scratch), colorType, dimensions, texels, numMipLevels); | 
|  | } | 
|  | SkAutoSTMalloc<14, GrMipLevel> tmpTexels; | 
|  | SkAutoSTArray<14, std::unique_ptr<char[]>> tmpDatas; | 
|  | GrColorType tempColorType = GrColorType::kUnknown; | 
|  | if (hasPixels) { | 
|  | tempColorType = this->prepareLevels(format, colorType, dimensions, texels, numMipLevels, | 
|  | &tmpTexels, &tmpDatas); | 
|  | if (tempColorType == GrColorType::kUnknown) { | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  | return fGpu->createTexture(dimensions, format, renderable, renderTargetSampleCnt, budgeted, | 
|  | isProtected, colorType, tempColorType, tmpTexels.get(), | 
|  | numMipLevels); | 
|  | } | 
|  |  | 
|  | sk_sp<GrTexture> GrResourceProvider::getExactScratch(SkISize dimensions, | 
|  | const GrBackendFormat& format, | 
|  | GrRenderable renderable, | 
|  | int renderTargetSampleCnt, | 
|  | SkBudgeted budgeted, | 
|  | GrMipmapped mipMapped, | 
|  | GrProtected isProtected) { | 
|  | sk_sp<GrTexture> tex(this->refScratchTexture(dimensions, format, renderable, | 
|  | renderTargetSampleCnt, mipMapped, isProtected)); | 
|  | if (tex && SkBudgeted::kNo == budgeted) { | 
|  | tex->resourcePriv().makeUnbudgeted(); | 
|  | } | 
|  |  | 
|  | return tex; | 
|  | } | 
|  |  | 
|  | sk_sp<GrTexture> GrResourceProvider::createTexture(SkISize dimensions, | 
|  | const GrBackendFormat& format, | 
|  | GrColorType colorType, | 
|  | GrRenderable renderable, | 
|  | int renderTargetSampleCnt, | 
|  | SkBudgeted budgeted, | 
|  | SkBackingFit fit, | 
|  | GrProtected isProtected, | 
|  | const GrMipLevel& mipLevel) { | 
|  | ASSERT_SINGLE_OWNER | 
|  |  | 
|  | if (!mipLevel.fPixels) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | if (SkBackingFit::kApprox == fit) { | 
|  | if (this->isAbandoned()) { | 
|  | return nullptr; | 
|  | } | 
|  | if (!fCaps->validateSurfaceParams(dimensions, format, renderable, renderTargetSampleCnt, | 
|  | GrMipmapped::kNo)) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | auto tex = this->createApproxTexture(dimensions, format, renderable, renderTargetSampleCnt, | 
|  | isProtected); | 
|  | if (!tex) { | 
|  | return nullptr; | 
|  | } | 
|  | return this->writePixels(std::move(tex), colorType, dimensions, &mipLevel, 1); | 
|  | } else { | 
|  | return this->createTexture(dimensions, format, colorType, renderable, renderTargetSampleCnt, | 
|  | budgeted, GrMipMapped::kNo, isProtected, &mipLevel); | 
|  | } | 
|  | } | 
|  |  | 
|  | sk_sp<GrTexture> GrResourceProvider::createCompressedTexture(SkISize dimensions, | 
|  | const GrBackendFormat& format, | 
|  | SkBudgeted budgeted, | 
|  | GrMipmapped mipMapped, | 
|  | GrProtected isProtected, | 
|  | SkData* data) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | if (this->isAbandoned()) { | 
|  | return nullptr; | 
|  | } | 
|  | return fGpu->createCompressedTexture(dimensions, format, budgeted, mipMapped, | 
|  | isProtected, data->data(), data->size()); | 
|  | } | 
|  |  | 
|  | sk_sp<GrTexture> GrResourceProvider::createTexture(SkISize dimensions, | 
|  | const GrBackendFormat& format, | 
|  | GrRenderable renderable, | 
|  | int renderTargetSampleCnt, | 
|  | GrMipmapped mipMapped, | 
|  | SkBudgeted budgeted, | 
|  | GrProtected isProtected) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | if (this->isAbandoned()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | if (!fCaps->validateSurfaceParams(dimensions, format, renderable, renderTargetSampleCnt, | 
|  | mipMapped)) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // Currently we don't recycle compressed textures as scratch. Additionally all compressed | 
|  | // textures should be created through the createCompressedTexture function. | 
|  | SkASSERT(!this->caps()->isFormatCompressed(format)); | 
|  |  | 
|  | // TODO: Support GrMipmapped::kYes in scratch texture lookup here. | 
|  | sk_sp<GrTexture> tex = | 
|  | this->getExactScratch(dimensions, format, renderable, renderTargetSampleCnt, budgeted, | 
|  | mipMapped, isProtected); | 
|  | if (tex) { | 
|  | return tex; | 
|  | } | 
|  |  | 
|  | return fGpu->createTexture(dimensions, format, renderable, renderTargetSampleCnt, mipMapped, | 
|  | budgeted, isProtected); | 
|  | } | 
|  |  | 
|  | // Map 'value' to a larger multiple of 2. Values <= 'kMagicTol' will pop up to | 
|  | // the next power of 2. Those above 'kMagicTol' will only go up half the floor power of 2. | 
|  | SkISize GrResourceProvider::MakeApprox(SkISize dimensions) { | 
|  | auto adjust = [](int value) { | 
|  | static const int kMagicTol = 1024; | 
|  |  | 
|  | value = std::max(kMinScratchTextureSize, value); | 
|  |  | 
|  | if (SkIsPow2(value)) { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | int ceilPow2 = SkNextPow2(value); | 
|  | if (value <= kMagicTol) { | 
|  | return ceilPow2; | 
|  | } | 
|  |  | 
|  | int floorPow2 = ceilPow2 >> 1; | 
|  | int mid = floorPow2 + (floorPow2 >> 1); | 
|  |  | 
|  | if (value <= mid) { | 
|  | return mid; | 
|  | } | 
|  | return ceilPow2; | 
|  | }; | 
|  |  | 
|  | return {adjust(dimensions.width()), adjust(dimensions.height())}; | 
|  | } | 
|  |  | 
|  | sk_sp<GrTexture> GrResourceProvider::createApproxTexture(SkISize dimensions, | 
|  | const GrBackendFormat& format, | 
|  | GrRenderable renderable, | 
|  | int renderTargetSampleCnt, | 
|  | GrProtected isProtected) { | 
|  | ASSERT_SINGLE_OWNER | 
|  |  | 
|  | if (this->isAbandoned()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // Currently we don't recycle compressed textures as scratch. Additionally all compressed | 
|  | // textures should be created through the createCompressedTexture function. | 
|  | SkASSERT(!this->caps()->isFormatCompressed(format)); | 
|  |  | 
|  | if (!fCaps->validateSurfaceParams(dimensions, format, renderable, renderTargetSampleCnt, | 
|  | GrMipmapped::kNo)) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | auto copyDimensions = MakeApprox(dimensions); | 
|  |  | 
|  | if (auto tex = this->refScratchTexture(copyDimensions, format, renderable, | 
|  | renderTargetSampleCnt, GrMipmapped::kNo, isProtected)) { | 
|  | return tex; | 
|  | } | 
|  |  | 
|  | return fGpu->createTexture(copyDimensions, format, renderable, renderTargetSampleCnt, | 
|  | GrMipmapped::kNo, SkBudgeted::kYes, isProtected); | 
|  | } | 
|  |  | 
|  | sk_sp<GrTexture> GrResourceProvider::refScratchTexture(SkISize dimensions, | 
|  | const GrBackendFormat& format, | 
|  | GrRenderable renderable, | 
|  | int renderTargetSampleCnt, | 
|  | GrMipmapped mipMapped, | 
|  | GrProtected isProtected) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | SkASSERT(!this->isAbandoned()); | 
|  | SkASSERT(!this->caps()->isFormatCompressed(format)); | 
|  | SkASSERT(fCaps->validateSurfaceParams(dimensions, format, renderable, renderTargetSampleCnt, | 
|  | GrMipmapped::kNo)); | 
|  |  | 
|  | // We could make initial clears work with scratch textures but it is a rare case so we just opt | 
|  | // to fall back to making a new texture. | 
|  | if (fGpu->caps()->reuseScratchTextures() || renderable == GrRenderable::kYes) { | 
|  | GrScratchKey key; | 
|  | GrTexture::ComputeScratchKey(*this->caps(), format, dimensions, renderable, | 
|  | renderTargetSampleCnt, mipMapped, isProtected, &key); | 
|  | GrGpuResource* resource = fCache->findAndRefScratchResource(key); | 
|  | if (resource) { | 
|  | fGpu->stats()->incNumScratchTexturesReused(); | 
|  | GrSurface* surface = static_cast<GrSurface*>(resource); | 
|  | return sk_sp<GrTexture>(surface->asTexture()); | 
|  | } | 
|  | } | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | sk_sp<GrTexture> GrResourceProvider::wrapBackendTexture(const GrBackendTexture& tex, | 
|  | GrWrapOwnership ownership, | 
|  | GrWrapCacheable cacheable, | 
|  | GrIOType ioType) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | if (this->isAbandoned()) { | 
|  | return nullptr; | 
|  | } | 
|  | return fGpu->wrapBackendTexture(tex, ownership, cacheable, ioType); | 
|  | } | 
|  |  | 
|  | sk_sp<GrTexture> GrResourceProvider::wrapCompressedBackendTexture(const GrBackendTexture& tex, | 
|  | GrWrapOwnership ownership, | 
|  | GrWrapCacheable cacheable) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | if (this->isAbandoned()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return fGpu->wrapCompressedBackendTexture(tex, ownership, cacheable); | 
|  | } | 
|  |  | 
|  |  | 
|  | sk_sp<GrTexture> GrResourceProvider::wrapRenderableBackendTexture(const GrBackendTexture& tex, | 
|  | int sampleCnt, | 
|  | GrWrapOwnership ownership, | 
|  | GrWrapCacheable cacheable) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | if (this->isAbandoned()) { | 
|  | return nullptr; | 
|  | } | 
|  | return fGpu->wrapRenderableBackendTexture(tex, sampleCnt, ownership, cacheable); | 
|  | } | 
|  |  | 
|  | sk_sp<GrRenderTarget> GrResourceProvider::wrapBackendRenderTarget( | 
|  | const GrBackendRenderTarget& backendRT) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | return this->isAbandoned() ? nullptr : fGpu->wrapBackendRenderTarget(backendRT); | 
|  | } | 
|  |  | 
|  | sk_sp<GrRenderTarget> GrResourceProvider::wrapVulkanSecondaryCBAsRenderTarget( | 
|  | const SkImageInfo& imageInfo, const GrVkDrawableInfo& vkInfo) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | return this->isAbandoned() ? nullptr : fGpu->wrapVulkanSecondaryCBAsRenderTarget(imageInfo, | 
|  | vkInfo); | 
|  |  | 
|  | } | 
|  |  | 
|  | void GrResourceProvider::assignUniqueKeyToResource(const GrUniqueKey& key, | 
|  | GrGpuResource* resource) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | if (this->isAbandoned() || !resource) { | 
|  | return; | 
|  | } | 
|  | resource->resourcePriv().setUniqueKey(key); | 
|  | } | 
|  |  | 
|  | sk_sp<GrGpuResource> GrResourceProvider::findResourceByUniqueKey(const GrUniqueKey& key) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | return this->isAbandoned() ? nullptr | 
|  | : sk_sp<GrGpuResource>(fCache->findAndRefUniqueResource(key)); | 
|  | } | 
|  |  | 
|  | sk_sp<const GrGpuBuffer> GrResourceProvider::findOrMakeStaticBuffer(GrGpuBufferType intendedType, | 
|  | size_t size, | 
|  | const void* data, | 
|  | const GrUniqueKey& key) { | 
|  | if (auto buffer = this->findByUniqueKey<GrGpuBuffer>(key)) { | 
|  | return std::move(buffer); | 
|  | } | 
|  | if (auto buffer = this->createBuffer(size, intendedType, kStatic_GrAccessPattern, data)) { | 
|  | // We shouldn't bin and/or cache static buffers. | 
|  | SkASSERT(buffer->size() == size); | 
|  | SkASSERT(!buffer->resourcePriv().getScratchKey().isValid()); | 
|  | buffer->resourcePriv().setUniqueKey(key); | 
|  | return sk_sp<const GrGpuBuffer>(buffer); | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | sk_sp<const GrGpuBuffer> GrResourceProvider::createPatternedIndexBuffer(const uint16_t* pattern, | 
|  | int patternSize, | 
|  | int reps, | 
|  | int vertCount, | 
|  | const GrUniqueKey* key) { | 
|  | size_t bufferSize = patternSize * reps * sizeof(uint16_t); | 
|  |  | 
|  | sk_sp<GrGpuBuffer> buffer( | 
|  | this->createBuffer(bufferSize, GrGpuBufferType::kIndex, kStatic_GrAccessPattern)); | 
|  | if (!buffer) { | 
|  | return nullptr; | 
|  | } | 
|  | uint16_t* data = (uint16_t*) buffer->map(); | 
|  | SkAutoTArray<uint16_t> temp; | 
|  | if (!data) { | 
|  | temp.reset(reps * patternSize); | 
|  | data = temp.get(); | 
|  | } | 
|  | for (int i = 0; i < reps; ++i) { | 
|  | int baseIdx = i * patternSize; | 
|  | uint16_t baseVert = (uint16_t)(i * vertCount); | 
|  | for (int j = 0; j < patternSize; ++j) { | 
|  | data[baseIdx+j] = baseVert + pattern[j]; | 
|  | } | 
|  | } | 
|  | if (temp.get()) { | 
|  | if (!buffer->updateData(data, bufferSize)) { | 
|  | return nullptr; | 
|  | } | 
|  | } else { | 
|  | buffer->unmap(); | 
|  | } | 
|  | if (key) { | 
|  | SkASSERT(key->isValid()); | 
|  | this->assignUniqueKeyToResource(*key, buffer.get()); | 
|  | } | 
|  | return std::move(buffer); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////////////////////////// | 
|  | static constexpr int kMaxNumNonAAQuads = 1 << 12;  // max possible: (1 << 14) - 1; | 
|  | static const int kVertsPerNonAAQuad = 4; | 
|  | static const int kIndicesPerNonAAQuad = 6; | 
|  |  | 
|  | sk_sp<const GrGpuBuffer> GrResourceProvider::createNonAAQuadIndexBuffer() { | 
|  | static_assert(kVertsPerNonAAQuad * kMaxNumNonAAQuads <= 65535);  // indices fit in a uint16_t | 
|  |  | 
|  | static const uint16_t kNonAAQuadIndexPattern[] = { | 
|  | 0, 1, 2, 2, 1, 3 | 
|  | }; | 
|  |  | 
|  | static_assert(SK_ARRAY_COUNT(kNonAAQuadIndexPattern) == kIndicesPerNonAAQuad); | 
|  |  | 
|  | return this->createPatternedIndexBuffer(kNonAAQuadIndexPattern, kIndicesPerNonAAQuad, | 
|  | kMaxNumNonAAQuads, kVertsPerNonAAQuad, nullptr); | 
|  | } | 
|  |  | 
|  | int GrResourceProvider::MaxNumNonAAQuads() { return kMaxNumNonAAQuads; } | 
|  | int GrResourceProvider::NumVertsPerNonAAQuad() { return kVertsPerNonAAQuad; } | 
|  | int GrResourceProvider::NumIndicesPerNonAAQuad() { return kIndicesPerNonAAQuad; } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////////////////////////// | 
|  | static constexpr int kMaxNumAAQuads = 1 << 9;  // max possible: (1 << 13) - 1; | 
|  | static const int kVertsPerAAQuad = 8; | 
|  | static const int kIndicesPerAAQuad = 30; | 
|  |  | 
|  | sk_sp<const GrGpuBuffer> GrResourceProvider::createAAQuadIndexBuffer() { | 
|  | static_assert(kVertsPerAAQuad * kMaxNumAAQuads <= 65535);  // indices fit in a uint16_t | 
|  |  | 
|  | // clang-format off | 
|  | static const uint16_t kAAQuadIndexPattern[] = { | 
|  | 0, 1, 2, 1, 3, 2, | 
|  | 0, 4, 1, 4, 5, 1, | 
|  | 0, 6, 4, 0, 2, 6, | 
|  | 2, 3, 6, 3, 7, 6, | 
|  | 1, 5, 3, 3, 5, 7, | 
|  | }; | 
|  | // clang-format on | 
|  |  | 
|  | static_assert(SK_ARRAY_COUNT(kAAQuadIndexPattern) == kIndicesPerAAQuad); | 
|  |  | 
|  | return this->createPatternedIndexBuffer(kAAQuadIndexPattern, kIndicesPerAAQuad, | 
|  | kMaxNumAAQuads, kVertsPerAAQuad, nullptr); | 
|  | } | 
|  |  | 
|  | int GrResourceProvider::MaxNumAAQuads() { return kMaxNumAAQuads; } | 
|  | int GrResourceProvider::NumVertsPerAAQuad() { return kVertsPerAAQuad; } | 
|  | int GrResourceProvider::NumIndicesPerAAQuad() { return kIndicesPerAAQuad; } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////////////////////////// | 
|  | sk_sp<GrPath> GrResourceProvider::createPath(const SkPath& path, const GrStyle& style) { | 
|  | if (this->isAbandoned()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | SkASSERT(this->gpu()->pathRendering()); | 
|  | return this->gpu()->pathRendering()->createPath(path, style); | 
|  | } | 
|  |  | 
|  | sk_sp<GrGpuBuffer> GrResourceProvider::createBuffer(size_t size, GrGpuBufferType intendedType, | 
|  | GrAccessPattern accessPattern, | 
|  | const void* data) { | 
|  | if (this->isAbandoned()) { | 
|  | return nullptr; | 
|  | } | 
|  | if (kDynamic_GrAccessPattern != accessPattern) { | 
|  | return this->gpu()->createBuffer(size, intendedType, accessPattern, data); | 
|  | } | 
|  | // bin by pow2+midpoint with a reasonable min | 
|  | static const size_t MIN_SIZE = 1 << 12; | 
|  | static const size_t MIN_UNIFORM_SIZE = 1 << 7; | 
|  | size_t allocSize = intendedType == GrGpuBufferType::kUniform ? std::max(size, MIN_UNIFORM_SIZE) | 
|  | : std::max(size, MIN_SIZE); | 
|  | size_t ceilPow2 = GrNextSizePow2(allocSize); | 
|  | size_t floorPow2 = ceilPow2 >> 1; | 
|  | size_t mid = floorPow2 + (floorPow2 >> 1); | 
|  | allocSize = (allocSize <= mid) ? mid : ceilPow2; | 
|  |  | 
|  | GrScratchKey key; | 
|  | GrGpuBuffer::ComputeScratchKeyForDynamicBuffer(allocSize, intendedType, &key); | 
|  | auto buffer = | 
|  | sk_sp<GrGpuBuffer>(static_cast<GrGpuBuffer*>(this->cache()->findAndRefScratchResource( | 
|  | key))); | 
|  | if (!buffer) { | 
|  | buffer = this->gpu()->createBuffer(allocSize, intendedType, kDynamic_GrAccessPattern); | 
|  | if (!buffer) { | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  | if (data) { | 
|  | buffer->updateData(data, size); | 
|  | } | 
|  | return buffer; | 
|  | } | 
|  |  | 
|  | bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt, int numStencilSamples) { | 
|  | SkASSERT(rt); | 
|  | GrAttachment* stencil = rt->getStencilAttachment(); | 
|  | if (stencil && stencil->numSamples() == numStencilSamples) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (!rt->wasDestroyed() && rt->canAttemptStencilAttachment()) { | 
|  | GrUniqueKey sbKey; | 
|  |  | 
|  | #if 0 | 
|  | if (this->caps()->oversizedStencilSupport()) { | 
|  | width  = SkNextPow2(width); | 
|  | height = SkNextPow2(height); | 
|  | } | 
|  | #endif | 
|  | GrBackendFormat stencilFormat = | 
|  | this->gpu()->getPreferredStencilFormat(rt->backendFormat()); | 
|  | if (!stencilFormat.isValid()) { | 
|  | return false; | 
|  | } | 
|  | GrProtected isProtected = rt->isProtected() ? GrProtected::kYes : GrProtected::kNo; | 
|  | GrAttachment::ComputeSharedAttachmentUniqueKey( | 
|  | *this->caps(), stencilFormat, rt->dimensions(), | 
|  | GrAttachment::UsageFlags::kStencilAttachment, numStencilSamples, isProtected, | 
|  | &sbKey); | 
|  | auto stencil = this->findByUniqueKey<GrAttachment>(sbKey); | 
|  | if (!stencil) { | 
|  | // Need to try and create a new stencil | 
|  | stencil = this->gpu()->makeStencilAttachmentForRenderTarget(rt, rt->dimensions(), | 
|  | numStencilSamples); | 
|  | if (!stencil) { | 
|  | return false; | 
|  | } | 
|  | this->assignUniqueKeyToResource(sbKey, stencil.get()); | 
|  | } | 
|  | rt->attachStencilAttachment(std::move(stencil)); | 
|  | } | 
|  |  | 
|  | if (GrAttachment* stencil = rt->getStencilAttachment()) { | 
|  | return stencil->numSamples() == numStencilSamples; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | sk_sp<GrAttachment> GrResourceProvider::makeMSAAAttachment(SkISize dimensions, | 
|  | const GrBackendFormat& format, | 
|  | int sampleCnt, | 
|  | GrProtected isProtected) { | 
|  | ASSERT_SINGLE_OWNER | 
|  |  | 
|  | SkASSERT(sampleCnt > 1); | 
|  |  | 
|  | if (this->isAbandoned()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | if (!fCaps->validateSurfaceParams(dimensions, format, GrRenderable::kYes, sampleCnt, | 
|  | GrMipMapped::kNo)) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | auto scratch = this->refScratchMSAAAttachment(dimensions, format, sampleCnt, isProtected); | 
|  | if (scratch) { | 
|  | return scratch; | 
|  | } | 
|  |  | 
|  | return fGpu->makeMSAAAttachment(dimensions, format, sampleCnt, isProtected); | 
|  | } | 
|  |  | 
|  | sk_sp<GrAttachment> GrResourceProvider::refScratchMSAAAttachment(SkISize dimensions, | 
|  | const GrBackendFormat& format, | 
|  | int sampleCnt, | 
|  | GrProtected isProtected) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | SkASSERT(!this->isAbandoned()); | 
|  | SkASSERT(!this->caps()->isFormatCompressed(format)); | 
|  | SkASSERT(fCaps->validateSurfaceParams(dimensions, format, GrRenderable::kYes, sampleCnt, | 
|  | GrMipmapped::kNo)); | 
|  |  | 
|  | GrScratchKey key; | 
|  | GrAttachment::ComputeScratchKey(*this->caps(), format, dimensions, | 
|  | GrAttachment::UsageFlags::kColorAttachment, sampleCnt, | 
|  | isProtected, &key); | 
|  | GrGpuResource* resource = fCache->findAndRefScratchResource(key); | 
|  | if (resource) { | 
|  | fGpu->stats()->incNumScratchMSAAAttachmentsReused(); | 
|  | GrAttachment* attachment = static_cast<GrAttachment*>(resource); | 
|  | return sk_sp<GrAttachment>(attachment); | 
|  | } | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT GrResourceProvider::makeSemaphore( | 
|  | bool isOwned) { | 
|  | return this->isAbandoned() ? nullptr : fGpu->makeSemaphore(isOwned); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<GrSemaphore> GrResourceProvider::wrapBackendSemaphore( | 
|  | const GrBackendSemaphore& semaphore, | 
|  | SemaphoreWrapType wrapType, | 
|  | GrWrapOwnership ownership) { | 
|  | ASSERT_SINGLE_OWNER | 
|  | return this->isAbandoned() ? nullptr : fGpu->wrapBackendSemaphore(semaphore, | 
|  | wrapType, | 
|  | ownership); | 
|  | } | 
|  |  | 
|  | // Ensures the row bytes are populated (not 0) and makes a copy to a temporary | 
|  | // to make the row bytes tight if necessary. Returns false if the input row bytes are invalid. | 
|  | static bool prepare_level(const GrMipLevel& inLevel, | 
|  | SkISize dimensions, | 
|  | bool rowBytesSupport, | 
|  | GrColorType origColorType, | 
|  | GrColorType allowedColorType, | 
|  | GrMipLevel* outLevel, | 
|  | std::unique_ptr<char[]>* data) { | 
|  | if (!inLevel.fPixels) { | 
|  | outLevel->fPixels = nullptr; | 
|  | outLevel->fRowBytes = 0; | 
|  | return true; | 
|  | } | 
|  | size_t minRB = dimensions.fWidth * GrColorTypeBytesPerPixel(origColorType); | 
|  | size_t actualRB = inLevel.fRowBytes ? inLevel.fRowBytes : minRB; | 
|  | if (actualRB < minRB) { | 
|  | return false; | 
|  | } | 
|  | if (origColorType == allowedColorType && (actualRB == minRB || rowBytesSupport)) { | 
|  | outLevel->fRowBytes = actualRB; | 
|  | outLevel->fPixels = inLevel.fPixels; | 
|  | return true; | 
|  | } | 
|  | auto tempRB = dimensions.fWidth * GrColorTypeBytesPerPixel(allowedColorType); | 
|  | data->reset(new char[tempRB * dimensions.fHeight]); | 
|  | outLevel->fPixels = data->get(); | 
|  | outLevel->fRowBytes = tempRB; | 
|  | GrImageInfo srcInfo(origColorType, kUnpremul_SkAlphaType, nullptr, dimensions); | 
|  | GrImageInfo dstInfo(allowedColorType, kUnpremul_SkAlphaType, nullptr, dimensions); | 
|  | return GrConvertPixels(dstInfo, data->get(), tempRB, srcInfo, inLevel.fPixels, actualRB); | 
|  | } | 
|  |  | 
|  | GrColorType GrResourceProvider::prepareLevels(const GrBackendFormat& format, | 
|  | GrColorType colorType, | 
|  | SkISize baseSize, | 
|  | const GrMipLevel texels[], | 
|  | int mipLevelCount, | 
|  | TempLevels* tempLevels, | 
|  | TempLevelDatas* tempLevelDatas) const { | 
|  | SkASSERT(mipLevelCount && texels && texels[0].fPixels); | 
|  |  | 
|  | auto allowedColorType = | 
|  | this->caps()->supportedWritePixelsColorType(colorType, format, colorType).fColorType; | 
|  | if (allowedColorType == GrColorType::kUnknown) { | 
|  | return GrColorType::kUnknown; | 
|  | } | 
|  | bool rowBytesSupport = this->caps()->writePixelsRowBytesSupport(); | 
|  | tempLevels->reset(mipLevelCount); | 
|  | tempLevelDatas->reset(mipLevelCount); | 
|  | auto size = baseSize; | 
|  | for (int i = 0; i < mipLevelCount; ++i) { | 
|  | if (!prepare_level(texels[i], size, rowBytesSupport, colorType, allowedColorType, | 
|  | &(*tempLevels)[i], &(*tempLevelDatas)[i])) { | 
|  | return GrColorType::kUnknown; | 
|  | } | 
|  | size = {std::max(size.fWidth / 2, 1), std::max(size.fHeight / 2, 1)}; | 
|  | } | 
|  | return allowedColorType; | 
|  | } | 
|  |  | 
|  | sk_sp<GrTexture> GrResourceProvider::writePixels(sk_sp<GrTexture> texture, | 
|  | GrColorType colorType, | 
|  | SkISize baseSize, | 
|  | const GrMipLevel texels[], | 
|  | int mipLevelCount) const { | 
|  | SkASSERT(!this->isAbandoned()); | 
|  | SkASSERT(texture); | 
|  | SkASSERT(colorType != GrColorType::kUnknown); | 
|  | SkASSERT(mipLevelCount && texels && texels[0].fPixels); | 
|  |  | 
|  | SkAutoSTMalloc<14, GrMipLevel> tmpTexels; | 
|  | SkAutoSTArray<14, std::unique_ptr<char[]>> tmpDatas; | 
|  | auto tempColorType = this->prepareLevels(texture->backendFormat(), colorType, baseSize, texels, | 
|  | mipLevelCount, &tmpTexels, &tmpDatas); | 
|  | if (tempColorType == GrColorType::kUnknown) { | 
|  | return nullptr; | 
|  | } | 
|  | SkAssertResult(fGpu->writePixels(texture.get(), 0, 0, baseSize.fWidth, baseSize.fHeight, | 
|  | colorType, tempColorType, tmpTexels.get(), mipLevelCount)); | 
|  | return texture; | 
|  | } |