| /* | |
| * 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 <algorithm> | |
| #include <utility> | |
| 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<GrTexture> 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) + 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<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, | |
| textureType, | |
| renderable, | |
| renderTargetSampleCnt, | |
| budgeted, | |
| isProtected, | |
| colorType, | |
| tempColorType, | |
| tmpTexels.get(), | |
| numMipLevels, | |
| label); | |
| } | |
| sk_sp<GrTexture> 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<GrTexture> tex(this->findAndRefScratchTexture(dimensions, | |
| format, | |
| textureType, | |
| renderable, | |
| renderTargetSampleCnt, | |
| mipmapped, | |
| isProtected, | |
| label)); | |
| if (tex && skgpu::Budgeted::kNo == budgeted) { | |
| tex->resourcePriv().makeUnbudgeted(); | |
| } | |
| return tex; | |
| } | |
| sk_sp<GrTexture> 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<GrTexture> 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<GrTexture> 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<GrTexture> 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<GrTexture> 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<GrTexture> 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<GrSurface*>(resource); | |
| resource->setLabel(std::move(label)); | |
| return sk_sp<GrTexture>(surface->asTexture()); | |
| } | |
| return nullptr; | |
| } | |
| sk_sp<GrTexture> 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<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 skgpu::UniqueKey& key, | |
| GrGpuResource* resource) { | |
| ASSERT_SINGLE_OWNER | |
| if (this->isAbandoned() || !resource) { | |
| return; | |
| } | |
| resource->resourcePriv().setUniqueKey(key); | |
| } | |
| sk_sp<GrGpuResource> GrResourceProvider::findResourceByUniqueKey(const skgpu::UniqueKey& 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* staticData, | |
| const skgpu::UniqueKey& key) { | |
| if (auto buffer = this->findByUniqueKey<GrGpuBuffer>(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<const GrGpuBuffer> GrResourceProvider::findOrMakeStaticBuffer( | |
| GrGpuBufferType intendedType, | |
| size_t size, | |
| const skgpu::UniqueKey& uniqueKey, | |
| InitializeBufferFn initializeBufferFn) { | |
| if (auto buffer = this->findByUniqueKey<GrGpuBuffer>(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<char> stagingBuffer; | |
| if (!vertexWriter) { | |
| SkASSERT(!buffer->isMapped()); | |
| vertexWriter = skgpu::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<const GrGpuBuffer> 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<GrGpuBuffer> buffer = this->createBuffer(bufferSize, | |
| GrGpuBufferType::kIndex, | |
| kStatic_GrAccessPattern, | |
| ZeroInit::kNo); | |
| if (!buffer) { | |
| return nullptr; | |
| } | |
| uint16_t* data = (uint16_t*) buffer->map(); | |
| AutoTArray<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, /*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<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(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<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(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<GrGpuBuffer> 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<GrGpuBuffer> 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<GrGpuBuffer>(static_cast<GrGpuBuffer*>(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<GrGpuBuffer> 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<GrAttachment>(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<GrAttachment> 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<GrAttachment>(key); | |
| if (msaaAttachment) { | |
| return msaaAttachment; | |
| } | |
| msaaAttachment = this->makeMSAAAttachment(dimensions, format, sampleCnt, isProtected, | |
| memoryless); | |
| if (msaaAttachment) { | |
| this->assignUniqueKeyToResource(key, msaaAttachment.get()); | |
| } | |
| return msaaAttachment; | |
| } | |
| sk_sp<GrAttachment> 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<GrAttachment> 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<GrAttachment*>(resource); | |
| resource->setLabel(std::move(label)); | |
| return sk_sp<GrAttachment>(attachment); | |
| } | |
| return nullptr; | |
| } | |
| [[nodiscard]] std::unique_ptr<GrSemaphore> GrResourceProvider::makeSemaphore(bool isOwned) { | |
| return this->isAbandoned() ? nullptr : fGpu->makeSemaphore(isOwned); | |
| } | |
| std::unique_ptr<GrSemaphore> 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<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( 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<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); | |
| AutoSTArray<14, GrMipLevel> tmpTexels; | |
| AutoSTArray<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(), | |
| SkIRect::MakeSize(baseSize), | |
| colorType, | |
| tempColorType, | |
| tmpTexels.get(), | |
| mipLevelCount)); | |
| return texture; | |
| } |