blob: 2bc2790e7778f09ac090803c74fead7cb22e49f5 [file]
/*
* 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