| /* |
| * 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 |