blob: f306cb25c3228bc8178369a2e61306bf1a4689d0 [file] [log] [blame]
/*
* 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/GrBackendSurface.h"
#include "include/gpu/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.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<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 = {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 = GrNextSizePow2(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;
}