perf(font): upload only the dirty sub-rect of the glyph atlas (#51)
FontAtlas::Update re-uploaded the entire 1024x1024 R8 atlas (1 MiB) whenever a single glyph was rasterized, so warm-up / language-switch / size-churn cost roughly one full-atlas copy per frame that added a codepoint. Accumulate a dirty bounding box (FontAtlas::DirtyRect) in MarkDirty as glyphs are placed, and copy only that sub-rect. On Vulkan this is a new ImageVulkan::UpdateRegion: the staging buffer keeps the full atlas row stride, so bufferRowLength stays the atlas width and bufferOffset points at the rect origin while imageOffset/imageExtent carve out the rect. Layout transitions stay whole-image (cheap); only the copy extent shrinks. WebGPU passes the rect straight to wgpuWriteAtlasRegion, which already took x/y/w/h. The freshly-zeroed atlas marks its whole extent dirty in Initialize so the first Update clears the image once; thereafter every untouched texel stays the zero it was uploaded as, keeping the image byte-identical to the staging copy. Tested: new FontAtlasDirtyRect unit test covers the accumulation / clamp / lockstep-with-`dirty` math (no GPU needed); HelloUI renders text correctly on both Vulkan (NVIDIA, validation-clean) and WebGPU. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
459f10afa8
commit
97c79c23ee
5 changed files with 276 additions and 6 deletions
27
project.cpp
27
project.cpp
|
|
@ -295,6 +295,33 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
|||
blasImpls.emplace_back("tests/BLASBuildOptions/main");
|
||||
bc.GetInterfacesAndImplementations(ifaces, blasImpls);
|
||||
cfg.tests.push_back(std::move(blasTest));
|
||||
|
||||
// Issue #51: FontAtlas only re-uploads the dirty sub-rect now,
|
||||
// tracked via FontAtlas::DirtyRect. The accumulation/clamp math is
|
||||
// pure CPU, so this test drives it directly — no GPU device needed
|
||||
// at runtime (the real copy params are covered by HelloUI rendering
|
||||
// text on both backends).
|
||||
Test atlasTest;
|
||||
Configuration& ac = atlasTest.config;
|
||||
ac.path = cfg.path;
|
||||
ac.name = "FontAtlasDirtyRect";
|
||||
ac.outputName = "FontAtlasDirtyRect";
|
||||
ac.type = ConfigurationType::Executable;
|
||||
ac.target = cfg.target;
|
||||
ac.march = cfg.march;
|
||||
ac.mtune = cfg.mtune;
|
||||
ac.debug = cfg.debug;
|
||||
ac.sysroot = cfg.sysroot;
|
||||
ac.dependencies = cfg.dependencies;
|
||||
ac.externalDependencies = cfg.externalDependencies;
|
||||
ac.compileFlags = cfg.compileFlags;
|
||||
ac.linkFlags = cfg.linkFlags;
|
||||
ac.defines = cfg.defines;
|
||||
ac.cFiles = cfg.cFiles;
|
||||
std::vector<fs::path> atlasImpls(impls.begin(), impls.end());
|
||||
atlasImpls.emplace_back("tests/FontAtlasDirtyRect/main");
|
||||
ac.GetInterfacesAndImplementations(ifaces, atlasImpls);
|
||||
cfg.tests.push_back(std::move(atlasTest));
|
||||
}
|
||||
|
||||
return cfg;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue