//SPDX-License-Identifier: LGPL-3.0-only //SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // The text kernels decode FontAtlas's SDF with FontAtlas's own encoding. // stb_truetype writes kOnEdgeValue + kPixelDistScale * distance into each // byte, and the shaders carry copies of those two numbers: UI_SDF_ON_EDGE and // UI_SDF_PIXEL_DIST_SCALE in shaders/ui-shared.glsl, which ui-text and the // fused kernel decode through, and again in the WGSL text kernel of // additional/dom-webgpu.js. The decode once multiplied by kPixelDistScale // where 255 / kPixelDistScale belongs, which left every edge pixelated, and a // copy that drifts from FontAtlas would go unseen on WebGPU, where no test // renders (wasm runs none) and a small drift passes TextAntialiasing's // tolerances anyway. So this reads the sources and compares the numbers, and // checks that both GLSL kernels decode through the shared helper rather than // arithmetic of their own. // // Pure CPU: reads files, no device. #include import Crafter.Graphics; import std; using namespace Crafter; namespace fs = std::filesystem; namespace { int failures = 0; void Check(bool ok, std::string_view what) { std::println("{} {}", ok ? "PASS" : "FAIL", what); if (!ok) ++failures; } // The directory holding shaders/ and additional/, whether the test runs from // its build output directory or the project root, as ExampleShaders finds it. fs::path LocateRoot() { for (const fs::path candidate : {".", "..", "../..", "../../.."}) { std::error_code ec; if (fs::is_regular_file(candidate / "shaders" / "ui-shared.glsl", ec) && fs::is_regular_file(candidate / "additional" / "dom-webgpu.js", ec)) { return candidate; } } return {}; } std::string ReadFile(const fs::path& path) { std::ifstream in(path, std::ios::binary); return std::string(std::istreambuf_iterator(in), std::istreambuf_iterator()); } // The numbers `pattern`'s one capture group matches in `source`. std::vector Matches(const std::string& source, const std::string& pattern) { std::vector values; const std::regex re(pattern); for (auto it = std::sregex_iterator(source.begin(), source.end(), re); it != std::sregex_iterator(); ++it) { values.push_back(std::stof((*it)[1].str())); } return values; } // `declaration` is the source up to the name, `name` the constant, followed // by "= ;" in either language. void CheckConstant(std::string_view file, const std::string& source, const std::string& declaration, std::string_view name, float expected) { const std::vector values = Matches(source, declaration + R"(\s*=\s*([0-9]+(?:\.[0-9]+)?)\s*;)"); std::string found = values.empty() ? "none" : ""; for (std::size_t i = 0; i < values.size(); ++i) found += std::format("{}{}", i == 0 ? "" : ", ", values[i]); Check(values.size() == 1 && values[0] == expected, std::format("{} defines {} once, as {} (found {})", file, name, expected, found)); } } // namespace int main() { const fs::path root = LocateRoot(); if (root.empty()) { std::println(std::cerr, "could not find shaders/ and additional/ from {}", fs::current_path().string()); return EXIT_FAILURE; } const std::string glsl = ReadFile(root / "shaders" / "ui-shared.glsl"); const std::string wgsl = ReadFile(root / "additional" / "dom-webgpu.js"); const float onEdge = static_cast(FontAtlas::kOnEdgeValue); const float pixelDistScale = FontAtlas::kPixelDistScale; CheckConstant("ui-shared.glsl", glsl, R"(const\s+float\s+UI_SDF_ON_EDGE)", "UI_SDF_ON_EDGE", onEdge); CheckConstant("ui-shared.glsl", glsl, R"(const\s+float\s+UI_SDF_PIXEL_DIST_SCALE)", "UI_SDF_PIXEL_DIST_SCALE", pixelDistScale); CheckConstant("dom-webgpu.js", wgsl, R"(const\s+UI_SDF_ON_EDGE\s*:\s*f32)", "UI_SDF_ON_EDGE", onEdge); CheckConstant("dom-webgpu.js", wgsl, R"(const\s+UI_SDF_PIXEL_DIST_SCALE\s*:\s*f32)", "UI_SDF_PIXEL_DIST_SCALE", pixelDistScale); for (const std::string_view kernel : {"ui-text.comp.glsl", "ui-fused.comp.glsl"}) { const std::string source = ReadFile(root / "shaders" / kernel); Check(source.find("uiGlyphCoverage(") != std::string::npos, std::format("{} decodes the atlas through ui-shared.glsl's uiGlyphCoverage", kernel)); } std::println("{}", failures == 0 ? "all checks passed" : "check(s) failed"); return failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }