| /* | |
| * Copyright 2023 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/AtlasProvider.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/gpu/GpuTypes.h" | |
| #include "include/gpu/graphite/Recorder.h" | |
| #include "include/gpu/graphite/TextureInfo.h" | |
| #include "include/private/SkLog.h" | |
| #include "src/gpu/graphite/Caps.h" | |
| #include "src/gpu/graphite/ClipAtlasManager.h" | |
| #include "src/gpu/graphite/ComputePathAtlas.h" | |
| #include "src/gpu/graphite/RasterPathAtlas.h" | |
| #include "src/gpu/graphite/RecorderPriv.h" | |
| #include "src/gpu/graphite/RendererProvider.h" | |
| #include "src/gpu/graphite/TextureProxy.h" | |
| #include "src/gpu/graphite/text/TextAtlasManager.h" | |
| #if defined(SK_ENABLE_SPARSE_STRIPS) | |
| #include "src/gpu/graphite/sparse_strips/AlphaAtlasManager.h" | |
| #endif | |
| #include <utility> | |
| namespace skgpu::graphite { | |
| static bool use_clip_atlas(const Recorder* recorder) { | |
| // Currently only the raster atlas strategy utilizes the clip atlas. | |
| return recorder->priv().rendererProvider()->pathRendererStrategy() == | |
| PathRendererStrategy::kRasterAtlas; | |
| } | |
| AtlasProvider::AtlasProvider(Recorder* recorder) | |
| : fTextAtlasManager(std::make_unique<TextAtlasManager>(recorder)) | |
| , fRasterPathAtlas(std::make_unique<RasterPathAtlas>(recorder)) | |
| , fClipAtlasManager(use_clip_atlas(recorder) ? std::make_unique<ClipAtlasManager>(recorder) | |
| : nullptr) | |
| #if defined(SK_ENABLE_SPARSE_STRIPS) | |
| , fAlphaAtlasManager(std::make_unique<AlphaAtlasManager>(recorder)) | |
| #endif | |
| {} | |
| AtlasProvider::~AtlasProvider() = default; | |
| std::unique_ptr<ComputePathAtlas> AtlasProvider::createComputePathAtlas(Recorder* recorder) const { | |
| if (recorder->priv().caps()->computeSupport()) { | |
| return ComputePathAtlas::CreateDefault(recorder); | |
| } | |
| return nullptr; | |
| } | |
| RasterPathAtlas* AtlasProvider::getRasterPathAtlas() const { return fRasterPathAtlas.get(); } | |
| ClipAtlasManager* AtlasProvider::getClipAtlasManager() const { return fClipAtlasManager.get(); } | |
| sk_sp<TextureProxy> AtlasProvider::getAtlasTexture(Recorder* recorder, | |
| uint16_t width, | |
| uint16_t height, | |
| SkColorType colorType, | |
| uint16_t identifier, | |
| bool requireStorageUsage) { | |
| uint64_t key = static_cast<uint64_t>(width) << 48 | | |
| static_cast<uint64_t>(height) << 32 | | |
| static_cast<uint64_t>(colorType) << 16 | | |
| static_cast<uint64_t>(identifier); | |
| auto iter = fTexturePool.find(key); | |
| if (iter != fTexturePool.end()) { | |
| return iter->second; | |
| } | |
| // We currently only make the distinction between a storage texture (written by a | |
| // compute pass) and a plain sampleable texture (written via upload) that won't be | |
| // used as a render attachment. | |
| const Caps* caps = recorder->priv().caps(); | |
| auto textureInfo = requireStorageUsage | |
| ? caps->getDefaultStorageTextureInfo(colorType) | |
| : caps->getDefaultSampledTextureInfo(colorType, | |
| Mipmapped::kNo, | |
| recorder->priv().isProtected(), | |
| Renderable::kNo); | |
| sk_sp<TextureProxy> proxy = TextureProxy::Make(caps, | |
| recorder->priv().resourceProvider(), | |
| SkISize::Make((int32_t)width, (int32_t)height), | |
| textureInfo, | |
| Budgeted::kYes, | |
| "AtlasProviderTexture"); | |
| if (!proxy) { | |
| return nullptr; | |
| } | |
| fTexturePool[key] = proxy; | |
| return proxy; | |
| } | |
| void AtlasProvider::freeGpuResources() { | |
| // Clear out any pages not in use or needed for any pending work on the Recorder. | |
| // In the event this is called right after a snap(), all pages would be eligible | |
| // for cleanup anyways. | |
| fTextAtlasManager->freeGpuResources(); | |
| if (fRasterPathAtlas) { | |
| fRasterPathAtlas->freeGpuResources(); | |
| } | |
| if (fClipAtlasManager) { | |
| fClipAtlasManager->freeGpuResources(); | |
| } | |
| #if defined(SK_ENABLE_SPARSE_STRIPS) | |
| if (fAlphaAtlasManager) { | |
| fAlphaAtlasManager->freeGpuResources(); | |
| } | |
| #endif | |
| // Release any textures held directly by the provider. These textures are used by transient | |
| // ComputePathAtlases that are reset every time a DrawContext snaps a DrawTask so there is no | |
| // need to reset those atlases explicitly here. Since the AtlasProvider gives out refs to the | |
| // TextureProxies in the pool, it should be safe to clear the pool in the middle of Recording. | |
| // Draws that use the previous TextureProxies will have refs on them. | |
| fTexturePool.clear(); | |
| } | |
| void AtlasProvider::recordUploads(DrawContext* dc) { | |
| if (!fTextAtlasManager->recordUploads(dc)) { | |
| SKIA_LOG_E("TextAtlasManager uploads have failed -- may see invalid results."); | |
| } | |
| if (fRasterPathAtlas) { | |
| fRasterPathAtlas->recordUploads(dc); | |
| } | |
| if (fClipAtlasManager) { | |
| fClipAtlasManager->recordUploads(dc); | |
| } | |
| #if defined(SK_ENABLE_SPARSE_STRIPS) | |
| if (fAlphaAtlasManager) { | |
| fAlphaAtlasManager->recordUploads(dc); | |
| } | |
| #endif | |
| } | |
| void AtlasProvider::compact() { | |
| fTextAtlasManager->compact(); | |
| if (fRasterPathAtlas) { | |
| fRasterPathAtlas->compact(); | |
| } | |
| if (fClipAtlasManager) { | |
| fClipAtlasManager->compact(); | |
| } | |
| } | |
| void AtlasProvider::invalidateAtlases() { | |
| // We must also evict atlases on a failure. The failed tasks can include uploads that the | |
| // atlas was depending on for its caches. Failing to prepare means they will never run so | |
| // future "successful" Recorder snaps would otherwise reference atlas pages that had stale | |
| // contents. | |
| fTextAtlasManager->evictAtlases(); | |
| if (fRasterPathAtlas) { | |
| fRasterPathAtlas->evictAtlases(); | |
| } | |
| if (fClipAtlasManager) { | |
| fClipAtlasManager->evictAtlases(); | |
| } | |
| } | |
| } // namespace skgpu::graphite |