| /* | |
| * Copyright 2026 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef skgpu_graphite_AlphaAtlasManager_DEFINED | |
| #define skgpu_graphite_AlphaAtlasManager_DEFINED | |
| #include "include/core/SkColorType.h" | |
| #include "include/private/SkTDArray.h" | |
| #include "src/gpu/graphite/TextureProxy.h" | |
| #include "src/gpu/graphite/geom/EndCaps.h" | |
| #include "src/gpu/graphite/sparse_strips/SparseStripsConfig.h" | |
| #include <cstdint> | |
| #include <optional> | |
| #include <utility> | |
| namespace skgpu::graphite { | |
| class DrawContext; | |
| class Recorder; | |
| // Holds the cpu-side copy of alpha values generated by sparse strips rasterization, and handles | |
| // their allocation and upload into a texture. | |
| // | |
| // Currently, the manager uses a two page system; when the first page is exhausted, the second page | |
| // is created with double the allocation. Uploads occur when the drawContext flushes; if two pages | |
| // are active at flush time, the older page is retired, and the larger page takes its place as the | |
| // initial page. | |
| class AlphaAtlasManager { | |
| public: | |
| AlphaAtlasManager(Recorder* recorder); | |
| // TODO(thomsmit): bitpack the texture page into the alpha index? | |
| // Returns a pointer to memory on the active texture page for writing `numBytes` alphas. | |
| // Overallocates the active page's buffer if the write exceeds fCapacityBytes. | |
| uint8_t* requestAlphaSpace(int32_t numBytes); | |
| // Finalizes the current boundary run. | |
| // If the run straddled pages, allocates the next page and copies the overrun data. Returns a | |
| // pair of {alphaIndex, texPage} on success, which indicate the page-local byte offset and atlas | |
| // page index (0 or 1), respectively. | |
| std::optional<std::pair<int32_t, uint16_t>> finalizeRun(); | |
| static constexpr uint16_t kNullSlot = SparseStripConfig::kMaxTexturePages; | |
| void recordUploads(DrawContext* dc); | |
| void freeGpuResources(); | |
| bool resolveNullCaps(EndCaps* ends); | |
| size_t numPages() const { | |
| size_t numPages = 0; | |
| for (int32_t i = 0; i < SparseStripConfig::kMaxTexturePages; ++i) { | |
| if (fPages[i].isValid()) { | |
| numPages++; | |
| } | |
| } | |
| return numPages; | |
| } | |
| int32_t activePageRowCount() const { | |
| return (fActiveSlot < SparseStripConfig::kMaxTexturePages && fPages[fActiveSlot].isValid()) | |
| ? fPages[fActiveSlot].fRowCount | |
| : 0; | |
| } | |
| sk_sp<TextureProxy> getPageProxy(size_t index) const { | |
| return (index < SparseStripConfig::kMaxTexturePages && fPages[index].isValid()) ? | |
| fPages[index].fTexture : nullptr; | |
| } | |
| const uint8_t* getPageData(size_t index) const { | |
| if (index == kNullSlot) { | |
| return fNullBuffer.data(); | |
| } | |
| return (index < SparseStripConfig::kMaxTexturePages && fPages[index].isValid()) ? | |
| fPages[index].fAlphaBuffer.data() : nullptr; | |
| } | |
| int activeSlot() const { return fActiveSlot; } | |
| bool hasNullBuffer() const { return !fNullBuffer.empty(); } | |
| void populateProxies(EndCaps* ends) const; | |
| private: | |
| static constexpr int32_t kInvalidSlot = -1; | |
| // The struct backing the manager's pages. Note, the struct contains fAlphaBuffer, which holds | |
| // the cpu-side data for the page (e.g. a single upload). | |
| struct TexturePage { | |
| sk_sp<TextureProxy> fTexture; | |
| int32_t fRowCount = 0; | |
| int32_t fCapacityBytes = 0; | |
| int32_t fUsedBytes = 0; | |
| int32_t fUploadedRows = 0; | |
| SkTDArray<uint8_t> fAlphaBuffer; | |
| bool isValid() const { return fTexture != nullptr; } | |
| void reset() { | |
| fTexture = nullptr; | |
| fRowCount = 0; | |
| fCapacityBytes = 0; | |
| fUsedBytes = 0; | |
| fUploadedRows = 0; | |
| fAlphaBuffer.clear(); | |
| } | |
| }; | |
| bool createPageInSlot(int slot, int32_t minRequiredBytes = 0); | |
| Recorder* fRecorder; | |
| TexturePage fPages[SparseStripConfig::kMaxTexturePages]; | |
| // Spillover memory and tracking for when all texture pages are exhausted. Only used when on the | |
| // kNullSlot. | |
| SkTDArray<uint8_t> fNullBuffer; | |
| int32_t fNullBufferUsedBytes; | |
| // The slot of the texture page whose alpha buffer is currently being appended to. Should always | |
| // be n < kMaxTexturePages | |
| int32_t fActiveSlot; | |
| // The slot of the last non-null texture page which was appended to | |
| int32_t fLastGpuSlot; | |
| // The slot of the last non-null texture page which was retired. Is only populated in null caps | |
| // scenario, invalid otherwise. | |
| int32_t fLastRetiredSlot; | |
| int32_t fNextPageRowCount; | |
| }; | |
| } // namespace skgpu::graphite | |
| #endif // skgpu_graphite_AlphaAtlasManager_DEFINED |