/* * 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/ganesh/GrResourceProvider.h" #include "include/core/SkAlphaType.h" #include "include/core/SkColorSpace.h" #include "include/core/SkData.h" #include "include/core/SkRect.h" #include "include/core/SkSize.h" #include "include/gpu/GpuTypes.h" #include "include/gpu/ganesh/GrBackendSurface.h" #include "include/gpu/ganesh/GrTypes.h" #include "include/private/base/SingleOwner.h" #include "include/private/base/SkTemplates.h" #include "src/base/SkMathPriv.h" #include "src/core/SkMipmap.h" #include "src/gpu/BufferWriter.h" #include "src/gpu/ResourceKey.h" #include "src/gpu/SkBackingFit.h" #include "src/gpu/ganesh/GrAttachment.h" #include "src/gpu/ganesh/GrCaps.h" #include "src/gpu/ganesh/GrDataUtils.h" #include "src/gpu/ganesh/GrGpu.h" #include "src/gpu/ganesh/GrGpuBuffer.h" #include "src/gpu/ganesh/GrGpuResourcePriv.h" #include "src/gpu/ganesh/GrImageInfo.h" #include "src/gpu/ganesh/GrPixmap.h" #include "src/gpu/ganesh/GrRenderTarget.h" #include "src/gpu/ganesh/GrResourceCache.h" #include "src/gpu/ganesh/GrSemaphore.h" #include "src/gpu/ganesh/GrSurface.h" #include "src/gpu/ganesh/GrTexture.h" #include #include struct SkImageInfo; using namespace skia_private; #define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(fSingleOwner) GrResourceProvider::GrResourceProvider(GrGpu* gpu, GrResourceCache* cache, skgpu::SingleOwner* owner) : fCache(cache) , fGpu(gpu) #ifdef SK_DEBUG , fSingleOwner(owner) #endif { fCaps = sk_ref_sp(fGpu->caps()); } sk_sp GrResourceProvider::createTexture(SkISize dimensions, const GrBackendFormat& format, GrTextureType textureType, GrColorType colorType, GrRenderable renderable, int renderTargetSampleCnt, skgpu::Budgeted budgeted, skgpu::Mipmapped mipmapped, GrProtected isProtected, const GrMipLevel texels[], std::string_view label) { ASSERT_SINGLE_OWNER if (this->isAbandoned()) { return nullptr; } int numMipLevels = 1; if (mipmapped == skgpu::Mipmapped::kYes) { numMipLevels = SkMipmap::ComputeLevelCount(dimensions.fWidth, dimensions.fHeight) + 1; } if (!fCaps->validateSurfaceParams(dimensions, format, renderable, renderTargetSampleCnt, mipmapped, textureType)) { 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, textureType, renderable, renderTargetSampleCnt, budgeted, mipmapped, isProtected, label); if (scratch) { if (!hasPixels) { return scratch; } return this->writePixels(std::move(scratch), colorType, dimensions, texels, numMipLevels); } AutoSTArray<14, GrMipLevel> tmpTexels; AutoSTArray<14, std::unique_ptr> 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, textureType, renderable, renderTargetSampleCnt, budgeted, isProtected, colorType, tempColorType, tmpTexels.get(), numMipLevels, label); } sk_sp GrResourceProvider::getExactScratch(SkISize dimensions, const GrBackendFormat& format, GrTextureType textureType, GrRenderable renderable, int renderTargetSampleCnt, skgpu::Budgeted budgeted, skgpu::Mipmapped mipmapped, GrProtected isProtected, std::string_view label) { sk_sp tex(this->findAndRefScratchTexture(dimensions, format, textureType, renderable, renderTargetSampleCnt, mipmapped, isProtected, label)); if (tex && skgpu::Budgeted::kNo == budgeted) { tex->resourcePriv().makeUnbudgeted(); } return tex; } sk_sp GrResourceProvider::createTexture(SkISize dimensions, const GrBackendFormat& format, GrTextureType textureType, GrColorType colorType, GrRenderable renderable, int renderTargetSampleCnt, skgpu::Budgeted budgeted, SkBackingFit fit, GrProtected isProtected, const GrMipLevel& mipLevel, std::string_view label) { ASSERT_SINGLE_OWNER if (!mipLevel.fPixels) { return nullptr; } if (SkBackingFit::kApprox == fit) { if (this->isAbandoned()) { return nullptr; } if (!fCaps->validateSurfaceParams(dimensions, format, renderable, renderTargetSampleCnt, skgpu::Mipmapped::kNo, textureType)) { return nullptr; } auto tex = this->createApproxTexture(dimensions, format, textureType, renderable, renderTargetSampleCnt, isProtected, label); if (!tex) { return nullptr; } return this->writePixels(std::move(tex), colorType, dimensions, &mipLevel, 1); } else { return this->createTexture(dimensions, format, textureType, colorType, renderable, renderTargetSampleCnt, budgeted, skgpu::Mipmapped::kNo, isProtected, &mipLevel, label); } } sk_sp GrResourceProvider::createCompressedTexture(SkISize dimensions, const GrBackendFormat& format, skgpu::Budgeted budgeted, skgpu::Mipmapped mipmapped, GrProtected isProtected, SkData* data, std::string_view label) { ASSERT_SINGLE_OWNER if (this->isAbandoned()) { return nullptr; } return fGpu->createCompressedTexture(dimensions, format, budgeted, mipmapped, isProtected, data->data(), data->size()); } sk_sp GrResourceProvider::createTexture(SkISize dimensions, const GrBackendFormat& format, GrTextureType textureType, GrRenderable renderable, int renderTargetSampleCnt, skgpu::Mipmapped mipmapped, skgpu::Budgeted budgeted, GrProtected isProtected, std::string_view label) { ASSERT_SINGLE_OWNER if (this->isAbandoned()) { return nullptr; } if (!fCaps->validateSurfaceParams(dimensions, format, renderable, renderTargetSampleCnt, mipmapped, textureType)) { 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 skgpu::Mipmapped::kYes in scratch texture lookup here. sk_sp tex = this->getExactScratch(dimensions, format, textureType, renderable, renderTargetSampleCnt, budgeted, mipmapped, isProtected, label); if (tex) { return tex; } return fGpu->createTexture(dimensions, format, textureType, renderable, renderTargetSampleCnt, mipmapped, budgeted, isProtected, label); } sk_sp GrResourceProvider::createApproxTexture(SkISize dimensions, const GrBackendFormat& format, GrTextureType textureType, GrRenderable renderable, int renderTargetSampleCnt, GrProtected isProtected, std::string_view label) { 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, skgpu::Mipmapped::kNo, textureType)) { return nullptr; } auto copyDimensions = skgpu::GetApproxSize(dimensions); if (auto tex = this->findAndRefScratchTexture(copyDimensions, format, textureType, renderable, renderTargetSampleCnt, skgpu::Mipmapped::kNo, isProtected, label)) { return tex; } return fGpu->createTexture(copyDimensions, format, textureType, renderable, renderTargetSampleCnt, skgpu::Mipmapped::kNo, skgpu::Budgeted::kYes, isProtected, label); } sk_sp GrResourceProvider::findAndRefScratchTexture(const skgpu::ScratchKey& key, std::string_view label) { ASSERT_SINGLE_OWNER SkASSERT(!this->isAbandoned()); SkASSERT(key.isValid()); if (GrGpuResource* resource = fCache->findAndRefScratchResource(key)) { fGpu->stats()->incNumScratchTexturesReused(); GrSurface* surface = static_cast(resource); resource->setLabel(std::move(label)); return sk_sp(surface->asTexture()); } return nullptr; } sk_sp GrResourceProvider::findAndRefScratchTexture(SkISize dimensions, const GrBackendFormat& format, GrTextureType textureType, GrRenderable renderable, int renderTargetSampleCnt, skgpu::Mipmapped mipmapped, GrProtected isProtected, std::string_view label) { ASSERT_SINGLE_OWNER SkASSERT(!this->isAbandoned()); SkASSERT(!this->caps()->isFormatCompressed(format)); SkASSERT(fCaps->validateSurfaceParams(dimensions, format, renderable, renderTargetSampleCnt, skgpu::Mipmapped::kNo, textureType)); // 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) { skgpu::ScratchKey key; GrTexture::ComputeScratchKey(*this->caps(), format, dimensions, renderable, renderTargetSampleCnt, mipmapped, isProtected, &key); return this->findAndRefScratchTexture(key, label); } return nullptr; } sk_sp 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 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 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 GrResourceProvider::wrapBackendRenderTarget( const GrBackendRenderTarget& backendRT) { ASSERT_SINGLE_OWNER return this->isAbandoned() ? nullptr : fGpu->wrapBackendRenderTarget(backendRT); } sk_sp GrResourceProvider::wrapVulkanSecondaryCBAsRenderTarget( const SkImageInfo& imageInfo, const GrVkDrawableInfo& vkInfo) { ASSERT_SINGLE_OWNER return this->isAbandoned() ? nullptr : fGpu->wrapVulkanSecondaryCBAsRenderTarget(imageInfo, vkInfo); } void GrResourceProvider::assignUniqueKeyToResource(const skgpu::UniqueKey& key, GrGpuResource* resource) { ASSERT_SINGLE_OWNER if (this->isAbandoned() || !resource) { return; } resource->resourcePriv().setUniqueKey(key); } sk_sp GrResourceProvider::findResourceByUniqueKey(const skgpu::UniqueKey& key) { ASSERT_SINGLE_OWNER return this->isAbandoned() ? nullptr : sk_sp(fCache->findAndRefUniqueResource(key)); } sk_sp GrResourceProvider::findOrMakeStaticBuffer(GrGpuBufferType intendedType, size_t size, const void* staticData, const skgpu::UniqueKey& key) { if (auto buffer = this->findByUniqueKey(key)) { return buffer; } auto buffer = this->createBuffer(staticData, size, intendedType, kStatic_GrAccessPattern); if (!buffer) { return nullptr; } // We shouldn't bin and/or cache static buffers. SkASSERT(buffer->size() == size); SkASSERT(!buffer->resourcePriv().getScratchKey().isValid()); buffer->resourcePriv().setUniqueKey(key); return buffer; } sk_sp GrResourceProvider::findOrMakeStaticBuffer( GrGpuBufferType intendedType, size_t size, const skgpu::UniqueKey& uniqueKey, InitializeBufferFn initializeBufferFn) { if (auto buffer = this->findByUniqueKey(uniqueKey)) { return buffer; } auto buffer = this->createBuffer(size, intendedType, kStatic_GrAccessPattern, ZeroInit::kNo); if (!buffer) { return nullptr; } // We shouldn't bin and/or cache static buffers. SkASSERT(buffer->size() == size); SkASSERT(!buffer->resourcePriv().getScratchKey().isValid()); buffer->resourcePriv().setUniqueKey(uniqueKey); // Map the buffer. Use a staging buffer on the heap if mapping isn't supported. skgpu::VertexWriter vertexWriter = {buffer->map(), size}; AutoTMalloc stagingBuffer; if (!vertexWriter) { SkASSERT(!buffer->isMapped()); vertexWriter = {stagingBuffer.reset(size), size}; } initializeBufferFn(std::move(vertexWriter), size); if (buffer->isMapped()) { buffer->unmap(); } else { buffer->updateData(stagingBuffer, /*offset=*/0, size, /*preserve=*/false); } return buffer; } sk_sp GrResourceProvider::createPatternedIndexBuffer( const uint16_t* pattern, int patternSize, int reps, int vertCount, const skgpu::UniqueKey* key) { size_t bufferSize = patternSize * reps * sizeof(uint16_t); sk_sp buffer = this->createBuffer(bufferSize, GrGpuBufferType::kIndex, kStatic_GrAccessPattern, ZeroInit::kNo); if (!buffer) { return nullptr; } uint16_t* data = (uint16_t*) buffer->map(); AutoTArray 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, /*offset=*/0, bufferSize, /*preserve=*/false)) { return nullptr; } } else { buffer->unmap(); } if (key) { SkASSERT(key->isValid()); this->assignUniqueKeyToResource(*key, buffer.get()); } return buffer; } /////////////////////////////////////////////////////////////////////////////////////////////////// static constexpr int kMaxNumNonAAQuads = 1 << 12; // max possible: (1 << 14) - 1; static const int kVertsPerNonAAQuad = 4; static const int kIndicesPerNonAAQuad = 6; sk_sp 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(std::size(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 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(std::size(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 GrResourceProvider::createBuffer(size_t size, GrGpuBufferType intendedType, GrAccessPattern accessPattern, ZeroInit zeroInit) { if (this->isAbandoned()) { return nullptr; } if (kDynamic_GrAccessPattern != accessPattern) { if (this->caps()->buffersAreInitiallyZero()) { zeroInit = ZeroInit::kNo; } sk_sp buffer = this->gpu()->createBuffer(size, intendedType, accessPattern); if (buffer && zeroInit == ZeroInit::kYes && !buffer->clearToZero()) { return nullptr; } return buffer; } // 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 = SkNextSizePow2(allocSize); size_t floorPow2 = ceilPow2 >> 1; size_t mid = floorPow2 + (floorPow2 >> 1); allocSize = (allocSize <= mid) ? mid : ceilPow2; skgpu::ScratchKey key; GrGpuBuffer::ComputeScratchKeyForDynamicBuffer(allocSize, intendedType, &key); auto buffer = sk_sp(static_cast(this->cache()->findAndRefScratchResource( key))); if (!buffer) { if (this->caps()->buffersAreInitiallyZero()) { zeroInit = ZeroInit::kNo; } buffer = this->gpu()->createBuffer(allocSize, intendedType, kDynamic_GrAccessPattern); } if (buffer && zeroInit == ZeroInit::kYes && !buffer->clearToZero()) { return nullptr; } return buffer; } sk_sp GrResourceProvider::createBuffer(const void* data, size_t size, GrGpuBufferType type, GrAccessPattern pattern) { SkASSERT(data); auto buffer = this->createBuffer(size, type, pattern, ZeroInit::kNo); if (!buffer) { return nullptr; } if (!buffer->updateData(data, /*offset=*/0, size, /*preserve=*/false)) { return nullptr; } return buffer; } static int num_stencil_samples(const GrRenderTarget* rt, bool useMSAASurface, const GrCaps& caps) { int numSamples = rt->numSamples(); if (numSamples == 1 && useMSAASurface) { // Are we using dynamic msaa? numSamples = caps.internalMultisampleCount(rt->backendFormat()); SkASSERT(numSamples > 1); // Caller must ensure dmsaa is supported before trying to use it. } return numSamples; } bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt, bool useMSAASurface) { SkASSERT(rt); SkASSERT(!this->caps()->avoidStencilBuffers()); GrAttachment* stencil = rt->getStencilAttachment(useMSAASurface); if (stencil) { SkASSERT(stencil->numSamples() == num_stencil_samples(rt, useMSAASurface, *this->caps())); return true; } if (!rt->wasDestroyed() && rt->canAttemptStencilAttachment(useMSAASurface)) { skgpu::UniqueKey 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; int numStencilSamples = num_stencil_samples(rt, useMSAASurface, *this->caps()); GrAttachment::ComputeSharedAttachmentUniqueKey(*this->caps(), stencilFormat, rt->dimensions(), GrAttachment::UsageFlags::kStencilAttachment, numStencilSamples, skgpu::Mipmapped::kNo, isProtected, GrMemoryless::kNo, &sbKey); auto keyedStencil = this->findByUniqueKey(sbKey); if (!keyedStencil) { // Need to try and create a new stencil keyedStencil = this->gpu()->makeStencilAttachment(rt->backendFormat(), rt->dimensions(), numStencilSamples); if (!keyedStencil) { return false; } this->assignUniqueKeyToResource(sbKey, keyedStencil.get()); } rt->attachStencilAttachment(std::move(keyedStencil), useMSAASurface); } stencil = rt->getStencilAttachment(useMSAASurface); SkASSERT(!stencil || stencil->numSamples() == num_stencil_samples(rt, useMSAASurface, *this->caps())); return stencil != nullptr; } sk_sp GrResourceProvider::getDiscardableMSAAAttachment(SkISize dimensions, const GrBackendFormat& format, int sampleCnt, GrProtected isProtected, GrMemoryless memoryless) { ASSERT_SINGLE_OWNER SkASSERT(sampleCnt > 1); if (this->isAbandoned()) { return nullptr; } if (!fCaps->validateSurfaceParams(dimensions, format, GrRenderable::kYes, sampleCnt, skgpu::Mipmapped::kNo, GrTextureType::kNone)) { return nullptr; } skgpu::UniqueKey key; GrAttachment::ComputeSharedAttachmentUniqueKey(*this->caps(), format, dimensions, GrAttachment::UsageFlags::kColorAttachment, sampleCnt, skgpu::Mipmapped::kNo, isProtected, memoryless, &key); auto msaaAttachment = this->findByUniqueKey(key); if (msaaAttachment) { return msaaAttachment; } msaaAttachment = this->makeMSAAAttachment(dimensions, format, sampleCnt, isProtected, memoryless); if (msaaAttachment) { this->assignUniqueKeyToResource(key, msaaAttachment.get()); } return msaaAttachment; } sk_sp GrResourceProvider::makeMSAAAttachment(SkISize dimensions, const GrBackendFormat& format, int sampleCnt, GrProtected isProtected, GrMemoryless memoryless) { ASSERT_SINGLE_OWNER SkASSERT(sampleCnt > 1); if (this->isAbandoned()) { return nullptr; } if (!fCaps->validateSurfaceParams(dimensions, format, GrRenderable::kYes, sampleCnt, skgpu::Mipmapped::kNo, GrTextureType::kNone)) { return nullptr; } auto scratch = this->refScratchMSAAAttachment(dimensions, format, sampleCnt, isProtected, memoryless, /*label=*/"MakeMSAAAttachment"); if (scratch) { return scratch; } return fGpu->makeMSAAAttachment(dimensions, format, sampleCnt, isProtected, memoryless); } sk_sp GrResourceProvider::refScratchMSAAAttachment(SkISize dimensions, const GrBackendFormat& format, int sampleCnt, GrProtected isProtected, GrMemoryless memoryless, std::string_view label) { ASSERT_SINGLE_OWNER SkASSERT(!this->isAbandoned()); SkASSERT(!this->caps()->isFormatCompressed(format)); SkASSERT(fCaps->validateSurfaceParams(dimensions, format, GrRenderable::kYes, sampleCnt, skgpu::Mipmapped::kNo, GrTextureType::kNone)); skgpu::ScratchKey key; GrAttachment::ComputeScratchKey(*this->caps(), format, dimensions, GrAttachment::UsageFlags::kColorAttachment, sampleCnt, skgpu::Mipmapped::kNo, isProtected, memoryless, &key); GrGpuResource* resource = fCache->findAndRefScratchResource(key); if (resource) { fGpu->stats()->incNumScratchMSAAAttachmentsReused(); GrAttachment* attachment = static_cast(resource); resource->setLabel(std::move(label)); return sk_sp(attachment); } return nullptr; } [[nodiscard]] std::unique_ptr GrResourceProvider::makeSemaphore(bool isOwned) { return this->isAbandoned() ? nullptr : fGpu->makeSemaphore(isOwned); } std::unique_ptr GrResourceProvider::wrapBackendSemaphore( const GrBackendSemaphore& semaphore, GrSemaphoreWrapType 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* 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( GrPixmap(dstInfo, data->get(), tempRB), GrCPixmap(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 GrResourceProvider::writePixels(sk_sp 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); AutoSTArray<14, GrMipLevel> tmpTexels; AutoSTArray<14, std::unique_ptr> tmpDatas; auto tempColorType = this->prepareLevels(texture->backendFormat(), colorType, baseSize, texels, mipLevelCount, &tmpTexels, &tmpDatas); if (tempColorType == GrColorType::kUnknown) { return nullptr; } SkAssertResult(fGpu->writePixels(texture.get(), SkIRect::MakeSize(baseSize), colorType, tempColorType, tmpTexels.get(), mipLevelCount)); return texture; }