blob: 8bb53212e6724af6ce35e71531995e5a6afdede0 [file] [log] [blame]
/*
* Copyright 2023 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_RasterPathAtlas_DEFINED
#define skgpu_graphite_RasterPathAtlas_DEFINED
#include "src/base/SkTInternalLList.h"
#include "src/core/SkTHash.h"
#include "src/gpu/ResourceKey.h"
#include "src/gpu/graphite/PathAtlas.h"
namespace skgpu::graphite {
/**
* PathAtlas class that rasterizes coverage masks on the CPU.
*
* When a new shape gets added, its path is rasterized in preparation for upload. These
* uploads are recorded by `recordUploads()` and subsequently added to an UploadTask.
*
* After a successful call to `recordUploads()`, the client is free to call `reset()` and start
* adding new shapes for a future atlas render.
* TODO: We should cache Shapes for future frames to avoid the cost of raster pipeline rendering.
*/
class RasterPathAtlas : public PathAtlas {
public:
RasterPathAtlas();
~RasterPathAtlas() override {}
void recordUploads(DrawContext*, Recorder*);
protected:
const TextureProxy* onAddShape(Recorder* recorder,
const Shape&,
const Transform& transform,
const SkStrokeRec&,
skvx::float2 atlasSize,
skvx::int2 deviceOffset,
skvx::half2* outPos) override;
const TextureProxy* addRect(Recorder* recorder,
skvx::float2 atlasSize,
SkIPoint16* outPos);
private:
// TODO: select atlas size dynamically? Take ContextOptions::fMaxTextureAtlasSize into account?
static constexpr int kDefaultAtlasDim = 4096;
struct Page {
bool initializeTextureIfNeeded(Recorder* recorder, uint16_t identifier);
// A Page lazily requests a texture from the AtlasProvider when the first shape gets added
// to it and references the same texture for the duration of its lifetime. A reference to
// this texture is stored here, which is used by CoverageMaskRenderStep when encoding the
// render pass.
sk_sp<TextureProxy> fTexture;
// Tracks placement of paths in a Page
skgpu::RectanizerSkyline fRectanizer = {kDefaultAtlasDim, kDefaultAtlasDim};
// Rendered data that gets uploaded
SkAutoPixmapStorage fPixels;
// Area that's needed to be uploaded
SkIRect fDirtyRect;
// Tracks whether a path is already in this Page, and its location in the atlas
struct UniqueKeyHash {
uint32_t operator()(const skgpu::UniqueKey& key) const { return key.hash(); }
};
skia_private::THashMap<skgpu::UniqueKey, skvx::half2, UniqueKeyHash> fCachedShapes;
// Set to true to clear data for new usage
bool fNeedsReset = false;
uint16_t fIdentifier;
SK_DECLARE_INTERNAL_LLIST_INTERFACE(Page);
};
void makeMRU(Page*);
// Free up atlas allocations, if necessary. After this call the atlas can be considered
// available for new shape insertions. However this method does not have any bearing on the
// contents of any atlas textures themselves, which may be in use by GPU commands that are
// in-flight or yet to be submitted.
void reset();
// Moving from one cached page to two gives improved results on some of our more complex SKPs.
// Any higher uses more memory seemly without much of an overall perf gain. More investigation
// will have to be done to tune this value.
static constexpr int kMaxPages = 2;
typedef SkTInternalLList<Page> PageList;
// LRU list of Pages (MRU at head - LRU at tail)
PageList fPageList;
// Allocated array of pages (backing data for list)
Page fPageArray[kMaxPages];
};
} // namespace skgpu::graphite
#endif // skgpu_graphite_RasterPathAtlas_DEFINED