blob: 68ff622d9c0164936daa353c8a5218572c60c9da [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.
*/
#include "include/gpu/graphite/Context.h"
#include "include/gpu/graphite/Recorder.h"
#include "src/gpu/graphite/sparse_strips/AlphaAtlasManager.h"
#include "src/gpu/graphite/sparse_strips/SparseStripsConfig.h"
#include "tests/CtsEnforcement.h"
#include "tests/Test.h"
namespace skgpu::graphite {
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(SparseStrips_AtlasDimensionsAndStride,
reporter,
context,
CtsEnforcement::kNever) {
auto recorder = context->makeRecorder();
AlphaAtlasManager atlasManager(recorder.get());
// Initial allocation (e.g. 512 bytes = 8 tiles of 8x8)
auto alloc1 = atlasManager.requestAlphaSpace(512);
REPORTER_ASSERT(reporter, alloc1.has_value());
REPORTER_ASSERT(reporter, alloc1->fAlphaIndex == 0);
REPORTER_ASSERT(reporter, alloc1->fTexPage == 0);
REPORTER_ASSERT(reporter, atlasManager.numPages() == 1);
// Initial page dimensions: 8192 x 1
sk_sp<TextureProxy> proxy = atlasManager.getPageProxy(0);
REPORTER_ASSERT(reporter, proxy != nullptr);
REPORTER_ASSERT(reporter, proxy->dimensions().width() == SparseStripConfig::kAtlasWidth);
REPORTER_ASSERT(reporter, proxy->dimensions().height() == 1);
// Verify row byte stride alignment (32,768 bytes/row = 8192 pixels * 4 bytes/pixel)
int32_t strideBytes = SparseStripConfig::kAtlasWidthBytes;
REPORTER_ASSERT(reporter, strideBytes == 32768);
// Subsequent allocation on same row/page
auto alloc2 = atlasManager.requestAlphaSpace(1024);
REPORTER_ASSERT(reporter, alloc2.has_value());
REPORTER_ASSERT(reporter, alloc2->fAlphaIndex == 512);
REPORTER_ASSERT(reporter, alloc2->fTexPage == 0);
REPORTER_ASSERT(reporter, atlasManager.numPages() == 1);
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(SparseStrips_AtlasDynamicGrowthAndDoubling,
reporter,
context,
CtsEnforcement::kNever) {
auto recorder = context->makeRecorder();
AlphaAtlasManager atlasManager(recorder.get());
// Page 0: allocate exactly 1 row (32,768 bytes)
auto alloc1 = atlasManager.requestAlphaSpace(SparseStripConfig::kAtlasWidthBytes);
REPORTER_ASSERT(reporter, alloc1.has_value());
REPORTER_ASSERT(reporter, alloc1->fTexPage == 0);
REPORTER_ASSERT(reporter, atlasManager.getPageProxy(0)->dimensions().height() == 1);
// Page 1: allocating 1 more tile exceeds Page 0, triggers doubling -> 2 rows (65,536 bytes)
auto alloc2 = atlasManager.requestAlphaSpace(64);
REPORTER_ASSERT(reporter, alloc2.has_value());
REPORTER_ASSERT(reporter, alloc2->fAlphaIndex == 0); // Atomic on new page, starts at 0
REPORTER_ASSERT(reporter, alloc2->fTexPage == 1);
REPORTER_ASSERT(reporter, atlasManager.numPages() == 2);
REPORTER_ASSERT(reporter, atlasManager.getPageProxy(1)->dimensions().height() == 2);
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(SparseStrips_AtlasStrictTwoPageLimit,
reporter,
context,
CtsEnforcement::kNever) {
auto recorder = context->makeRecorder();
AlphaAtlasManager atlasManager(recorder.get());
// Page 0: 1 row
auto alloc1 = atlasManager.requestAlphaSpace(SparseStripConfig::kAtlasWidthBytes);
REPORTER_ASSERT(reporter, alloc1.has_value());
REPORTER_ASSERT(reporter, atlasManager.numPages() == 1);
// Page 1: 2 rows
auto alloc2 = atlasManager.requestAlphaSpace(SparseStripConfig::kAtlasWidthBytes * 2);
REPORTER_ASSERT(reporter, alloc2.has_value());
REPORTER_ASSERT(reporter, atlasManager.numPages() == 2);
// Attempting to exceed Page 1: we only ever allow 2 pages globally across all endcaps.
// Should fail gracefully!
auto alloc3 = atlasManager.requestAlphaSpace(64);
REPORTER_ASSERT(reporter, !alloc3.has_value());
REPORTER_ASSERT(reporter, atlasManager.numPages() == 2);
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(SparseStrips_AtlasEndCapAtomicity,
reporter,
context,
CtsEnforcement::kNever) {
auto recorder = context->makeRecorder();
AlphaAtlasManager atlasManager(recorder.get());
// Allocate almost all of Page 0, leaving 64 bytes
auto alloc1 = atlasManager.requestAlphaSpace(SparseStripConfig::kAtlasWidthBytes - 64);
REPORTER_ASSERT(reporter, alloc1.has_value());
REPORTER_ASSERT(reporter, alloc1->fTexPage == 0);
REPORTER_ASSERT(reporter, alloc1->fAlphaIndex == 0);
// Request 128 bytes (which cannot fit in the remaining 64 bytes of Page 0).
// The allocation must be atomic to Page 1: entire 128 bytes at offset 0 of Page 1.
auto alloc2 = atlasManager.requestAlphaSpace(128);
REPORTER_ASSERT(reporter, alloc2.has_value());
REPORTER_ASSERT(reporter, alloc2->fTexPage == 1);
REPORTER_ASSERT(reporter, alloc2->fAlphaIndex == 0);
REPORTER_ASSERT(reporter, atlasManager.numPages() == 2);
}
DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(SparseStrips_AtlasFlipFlopAtFlush,
reporter,
context,
CtsEnforcement::kNever) {
auto recorder = context->makeRecorder();
AlphaAtlasManager atlasManager(recorder.get());
// Slot 0: 1 row (32,768 bytes)
auto alloc1 = atlasManager.requestAlphaSpace(SparseStripConfig::kAtlasWidthBytes);
REPORTER_ASSERT(reporter, alloc1.has_value());
REPORTER_ASSERT(reporter, alloc1->fTexPage == 0);
REPORTER_ASSERT(reporter, atlasManager.activeSlot() == 0);
sk_sp<TextureProxy> initialSlot0 = atlasManager.getPageProxy(0);
REPORTER_ASSERT(reporter, atlasManager.numPages() == 1);
// Slot 1: 2 rows (65,536 bytes) via XOR flip-flop (0 ^ 1 = 1)
auto alloc2 = atlasManager.requestAlphaSpace(64);
REPORTER_ASSERT(reporter, alloc2.has_value());
REPORTER_ASSERT(reporter, alloc2->fTexPage == 1);
REPORTER_ASSERT(reporter, atlasManager.activeSlot() == 1);
sk_sp<TextureProxy> initialSlot1 = atlasManager.getPageProxy(1);
REPORTER_ASSERT(reporter, atlasManager.numPages() == 2);
// Flush: recordUploads(nullptr) retires older slot 0 (1 ^ 1 = 0).
// Slot 1 stays right in place in slot 1 (no moving needed)!
atlasManager.recordUploads(nullptr);
REPORTER_ASSERT(reporter, atlasManager.numPages() == 1);
REPORTER_ASSERT(reporter, atlasManager.getPageProxy(0) == nullptr);
REPORTER_ASSERT(reporter, atlasManager.getPageProxy(1) == initialSlot1);
REPORTER_ASSERT(reporter, atlasManager.activeSlot() == 1);
// Continue allocating in current active slot (Slot 1):
auto alloc3 = atlasManager.requestAlphaSpace(64);
REPORTER_ASSERT(reporter, alloc3.has_value());
REPORTER_ASSERT(reporter, alloc3->fTexPage == 1);
REPORTER_ASSERT(reporter, atlasManager.numPages() == 1);
// Fill Slot 1 and trigger flip-flop to Slot 0 (1 ^ 1 = 0) -> doubled to 4 rows!
auto alloc4 = atlasManager.requestAlphaSpace(SparseStripConfig::kAtlasWidthBytes * 2);
REPORTER_ASSERT(reporter, alloc4.has_value());
REPORTER_ASSERT(reporter, alloc4->fTexPage == 0);
REPORTER_ASSERT(reporter, atlasManager.activeSlot() == 0);
REPORTER_ASSERT(reporter, atlasManager.numPages() == 2);
REPORTER_ASSERT(reporter, atlasManager.getPageProxy(0)->dimensions().height() == 4);
// Second flush: retires older slot 1 (0 ^ 1 = 1). Slot 0 remains active!
atlasManager.recordUploads(nullptr);
REPORTER_ASSERT(reporter, atlasManager.numPages() == 1);
REPORTER_ASSERT(reporter, atlasManager.getPageProxy(1) == nullptr);
REPORTER_ASSERT(reporter, atlasManager.getPageProxy(0)->dimensions().height() == 4);
REPORTER_ASSERT(reporter, atlasManager.activeSlot() == 0);
}
} // namespace skgpu::graphite