blob: 897f62b44af609e087ba67e09e8aa43767b29699 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/graphite/ResourceProvider.h"
#include "include/core/SkSamplingOptions.h"
#include "include/core/SkTileMode.h"
#include "include/gpu/graphite/BackendTexture.h"
#include "src/gpu/graphite/Buffer.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/graphite/CommandBuffer.h"
#include "src/gpu/graphite/ComputePipeline.h"
#include "src/gpu/graphite/ContextPriv.h"
#include "src/gpu/graphite/ContextUtils.h"
#include "src/gpu/graphite/GlobalCache.h"
#include "src/gpu/graphite/GraphicsPipeline.h"
#include "src/gpu/graphite/GraphicsPipelineHandle.h"
#include "src/gpu/graphite/Log.h"
#include "src/gpu/graphite/PipelineCreationTask.h"
#include "src/gpu/graphite/PipelineManager.h"
#include "src/gpu/graphite/RenderPassDesc.h"
#include "src/gpu/graphite/RendererProvider.h"
#include "src/gpu/graphite/ResourceCache.h"
#include "src/gpu/graphite/RuntimeEffectDictionary.h"
#include "src/gpu/graphite/Sampler.h"
#include "src/gpu/graphite/SharedContext.h"
#include "src/gpu/graphite/Texture.h"
#include "src/sksl/SkSLCompiler.h"
namespace skgpu::graphite {
ResourceProvider::ResourceProvider(SharedContext* sharedContext,
SingleOwner* singleOwner,
uint32_t recorderID,
size_t resourceBudget)
: fSharedContext(sharedContext)
, fResourceCache(ResourceCache::Make(singleOwner, recorderID, resourceBudget)) {}
ResourceProvider::~ResourceProvider() {
fResourceCache->shutdown();
}
GraphicsPipelineHandle ResourceProvider::createGraphicsPipelineHandle(
const GraphicsPipelineDesc& pipelineDesc,
const RenderPassDesc& renderPassDesc,
SkEnumBitMask<PipelineCreationFlags> pipelineCreationFlags) {
PipelineManager* pipelineManager = fSharedContext->pipelineManager();
return pipelineManager->createHandle(fSharedContext,
pipelineDesc,
renderPassDesc,
pipelineCreationFlags);
}
void ResourceProvider::startPipelineCreationTask(sk_sp<const RuntimeEffectDictionary> runtimeDict,
const GraphicsPipelineHandle& handle) {
PipelineManager* pipelineManager = fSharedContext->pipelineManager();
pipelineManager->startPipelineCreationTask(fSharedContext,
std::move(runtimeDict),
handle);
}
sk_sp<GraphicsPipeline> ResourceProvider::resolveHandle(const GraphicsPipelineHandle& handle) {
PipelineManager* pipelineManager = fSharedContext->pipelineManager();
return pipelineManager->resolveHandle(handle);
}
sk_sp<ComputePipeline> ResourceProvider::findOrCreateComputePipeline(
const ComputePipelineDesc& pipelineDesc) {
auto globalCache = fSharedContext->globalCache();
UniqueKey pipelineKey = fSharedContext->caps()->makeComputePipelineKey(pipelineDesc);
sk_sp<ComputePipeline> pipeline = globalCache->findComputePipeline(pipelineKey);
if (!pipeline) {
pipeline = this->createComputePipeline(pipelineDesc);
if (pipeline) {
pipeline = globalCache->addComputePipeline(pipelineKey, std::move(pipeline));
}
}
return pipeline;
}
////////////////////////////////////////////////////////////////////////////////////////////////
sk_sp<Texture> ResourceProvider::findOrCreateShareableTexture(SkISize dimensions,
const TextureInfo& info,
std::string_view label) {
return this->findOrCreateTexture(dimensions,
info,
std::move(label),
Budgeted::kYes,
Shareable::kYes);
}
sk_sp<Texture> ResourceProvider::findOrCreateNonShareableTexture(SkISize dimensions,
const TextureInfo& info,
std::string_view label,
Budgeted budgeted) {
return this->findOrCreateTexture(dimensions,
info,
std::move(label),
budgeted,
Shareable::kNo);
}
sk_sp<Texture> ResourceProvider::findOrCreateScratchTexture(
SkISize dimensions,
const TextureInfo& info,
std::string_view label,
const ResourceCache::ScratchResourceSet& unavailable) {
return this->findOrCreateTexture(dimensions,
info,
std::move(label),
Budgeted::kYes,
Shareable::kScratch,
&unavailable);
}
sk_sp<Texture> ResourceProvider::findOrCreateTexture(
SkISize dimensions,
const TextureInfo& info,
std::string_view label,
Budgeted budgeted,
Shareable shareable,
const ResourceCache::ScratchResourceSet* unavailable) {
// If the resource is shareable it should be budgeted since it shouldn't be backing any client
// owned object.
SkASSERT(shareable == Shareable::kNo || budgeted == Budgeted::kYes);
SkASSERT(shareable != Shareable::kScratch || SkToBool(unavailable));
static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
if (!info.isValid()) {
// Checking for a valid TextureInfo here allows callers to consolidate error checking for
// both TextureInfo and Texture creation to checking for a null returned Texture.
return nullptr;
}
GraphiteResourceKey key;
fSharedContext->caps()->buildKeyForTexture(dimensions, info, kType, &key);
if (Resource* resource =
fResourceCache->findAndRefResource(key, budgeted, shareable, unavailable)) {
resource->setLabel(std::move(label));
return sk_sp<Texture>(static_cast<Texture*>(resource));
}
auto tex = this->createTexture(dimensions, info);
if (!tex) {
return nullptr;
}
tex->setLabel(std::move(label));
fResourceCache->insertResource(tex.get(), key, budgeted, shareable);
return tex;
}
sk_sp<Texture> ResourceProvider::createWrappedTexture(const BackendTexture& backendTexture,
std::string_view label) {
sk_sp<Texture> texture = this->onCreateWrappedTexture(backendTexture);
if (texture) {
texture->setLabel(std::move(label));
SkASSERT(texture->ownership() == Ownership::kWrapped);
}
return texture;
}
sk_sp<Sampler> ResourceProvider::findOrCreateCompatibleSampler(const SamplerDesc& samplerDesc) {
static constexpr Budgeted kBudgeted = Budgeted::kYes;
static constexpr Shareable kShareable = Shareable::kYes;
static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
GraphiteResourceKey key;
{
// The size of the returned span accurately captures the quantity of uint32s needed whether
// the sampler is immutable or not. Each backend will already have encoded any specific
// immutable sampler details into the SamplerDesc, so there is no need to delegate to Caps
// to create a specific key.
const SkSpan<const uint32_t>& samplerData = samplerDesc.asSpan();
GraphiteResourceKey::Builder builder(&key, kType, samplerData.size());
for (size_t i = 0; i < samplerData.size(); i++) {
builder[i] = samplerData[i];
}
}
if (Resource* resource = fResourceCache->findAndRefResource(key, kBudgeted, kShareable)) {
return sk_sp<Sampler>(static_cast<Sampler*>(resource));
}
sk_sp<Sampler> sampler = this->createSampler(samplerDesc);
if (!sampler) {
return nullptr;
}
fResourceCache->insertResource(sampler.get(), key, kBudgeted, kShareable);
return sampler;
}
sk_sp<Buffer> ResourceProvider::findOrCreateNonShareableBuffer(size_t size,
BufferType type,
AccessPattern accessPattern,
std::string_view label) {
return this->findOrCreateBuffer(size, type, accessPattern, label, Shareable::kNo);
}
sk_sp<Buffer> ResourceProvider::findOrCreateScratchBuffer(
size_t size,
BufferType type,
AccessPattern access,
std::string_view label,
const ResourceCache::ScratchResourceSet& unvailable) {
// Scratch buffers must be GPU only, mapped access makes it too difficult to scope their
// reads and writes within the actual command buffer execution.
SkASSERT(access != AccessPattern::kHostVisible);
return this->findOrCreateBuffer(size, type, access, label, Shareable::kScratch, &unvailable);
}
sk_sp<Buffer> ResourceProvider::findOrCreateBuffer(
size_t size,
BufferType type,
AccessPattern accessPattern,
std::string_view label,
Shareable shareable,
const ResourceCache::ScratchResourceSet* unavailable) {
static constexpr Budgeted kBudgeted = Budgeted::kYes;
static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
GraphiteResourceKey key;
{
// For the key we need ((sizeof(size_t) + (sizeof(uint32_t) - 1)) / (sizeof(uint32_t))
// uint32_t's for the size and one uint32_t for the rest.
static_assert(sizeof(uint32_t) == 4);
static const int kSizeKeyNum32DataCnt = (sizeof(size_t) + 3) / 4;
static const int kKeyNum32DataCnt = kSizeKeyNum32DataCnt + 1;
SkASSERT(static_cast<uint32_t>(type) < (1u << 4));
SkASSERT(static_cast<uint32_t>(accessPattern) < (1u << 2));
GraphiteResourceKey::Builder builder(&key, kType, kKeyNum32DataCnt);
builder[0] = (static_cast<uint32_t>(type) << 0) |
(static_cast<uint32_t>(accessPattern) << 4);
size_t szKey = size;
for (int i = 0; i < kSizeKeyNum32DataCnt; ++i) {
builder[i + 1] = (uint32_t) szKey;
// If size_t is 4 bytes, we cannot do a shift of 32 or else we get a warning/error that
// shift amount is >= width of the type.
if constexpr(kSizeKeyNum32DataCnt > 1) {
szKey = szKey >> 32;
}
}
}
if (Resource* resource =
fResourceCache->findAndRefResource(key, kBudgeted, shareable, unavailable)) {
resource->setLabel(std::move(label));
return sk_sp<Buffer>(static_cast<Buffer*>(resource));
}
auto buffer = this->createBuffer(size, type, accessPattern);
if (!buffer) {
return nullptr;
}
buffer->setLabel(std::move(label));
fResourceCache->insertResource(buffer.get(), key, kBudgeted, shareable);
return buffer;
}
namespace {
bool dimensions_are_valid(const int maxTextureSize, const SkISize& dimensions) {
if (dimensions.isEmpty() ||
dimensions.width() > maxTextureSize ||
dimensions.height() > maxTextureSize) {
SKGPU_LOG_W("Call to createBackendTexture has requested dimensions (%d, %d) larger than the"
" supported gpu max texture size: %d. Or the dimensions are empty.",
dimensions.fWidth, dimensions.fHeight, maxTextureSize);
return false;
}
return true;
}
}
BackendTexture ResourceProvider::createBackendTexture(SkISize dimensions, const TextureInfo& info) {
if (!dimensions_are_valid(fSharedContext->caps()->maxTextureSize(), dimensions)) {
return {};
}
return this->onCreateBackendTexture(dimensions, info);
}
#ifdef SK_BUILD_FOR_ANDROID
BackendTexture ResourceProvider::createBackendTexture(AHardwareBuffer* hardwareBuffer,
bool isRenderable,
bool isProtectedContent,
SkISize dimensions,
bool fromAndroidWindow) const {
if (!dimensions_are_valid(fSharedContext->caps()->maxTextureSize(), dimensions)) {
return {};
}
return this->onCreateBackendTexture(hardwareBuffer,
isRenderable,
isProtectedContent,
dimensions,
fromAndroidWindow);
}
BackendTexture ResourceProvider::onCreateBackendTexture(AHardwareBuffer*,
bool isRenderable,
bool isProtectedContent,
SkISize dimensions,
bool fromAndroidWindow) const {
return {};
}
#endif
void ResourceProvider::deleteBackendTexture(const BackendTexture& texture) {
this->onDeleteBackendTexture(texture);
}
void ResourceProvider::freeGpuResources() {
this->onFreeGpuResources();
// TODO: Are there Resources that are ref'd by the ResourceProvider or its subclasses that need
// be released? If we ever find that we're holding things directly on the ResourceProviders we
// call down into the subclasses to allow them to release things.
fResourceCache->purgeResources();
}
void ResourceProvider::purgeResourcesNotUsedSince(StdSteadyClock::time_point purgeTime) {
this->onPurgeResourcesNotUsedSince(purgeTime);
fResourceCache->purgeResourcesNotUsedSince(purgeTime);
}
const Caps* ResourceProvider::caps() const {
return fSharedContext->caps();
}
} // namespace skgpu::graphite