| /* |
| * 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 <algorithm> |
| #include <cstdint> |
| #include <optional> |
| #include <vector> |
| |
| 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? |
| struct AlphaAllocation { |
| uint8_t* fWritePtr = nullptr; // The ptr into the AlphaAtlasManager's cpu storage |
| int32_t fAlphaIndex = 0; // Page-local byte offset |
| uint16_t fTexPage = 0; // Atlas page index (0 or 1) |
| }; |
| std::optional<AlphaAllocation> requestAlphaSpace(int32_t numBytes); |
| |
| void recordUploads(DrawContext* dc); |
| void freeGpuResources(); |
| |
| 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 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; |
| } |
| int activeSlot() const { return fActiveSlot; } |
| |
| void populateProxies(EndCaps* ends) const { |
| for (int32_t i = 0; i < SparseStripConfig::kMaxTexturePages; ++i) { |
| if (fPages[i].isValid()) { |
| ends->addProxy(fPages[i].fTexture); |
| } |
| } |
| } |
| |
| private: |
| // 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]; |
| int fActiveSlot; |
| int32_t fNextPageRowCount; |
| }; |
| |
| } // namespace skgpu::graphite |
| |
| #endif // skgpu_graphite_AlphaAtlasManager_DEFINED |