blob: 74f2001138a0b143cd84bc5b7e29b588233dd805 [file] [edit]
/*
* 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