uiResolveScreenPixel ran four float compares against hdr.clipRectPx for
every pixel of every UI dispatch, even though the clip rect is the
full-surface default {0,0,1e9,1e9} in the overwhelmingly common case.
Reserve the high bit of the header flags word (UI_FLAG_CLIP) and have
FillHeader set it only when the clip rect is narrower than the surface
(extracted into the unit-testable UIRenderer::ClipFlags). The shader
gates the compares on the bit; the branch is push-constant-uniform so it
never diverges. When the rect covers the surface the skipped compares
could never reject an in-surface pixel, so output is pixel-identical.
Mirrored on the WebGPU/WGSL path (dom-webgpu.js) for parity. Adds the
UIClipFlag CPU test covering the gate decision.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
105 lines
4.6 KiB
C++
105 lines
4.6 KiB
C++
/*
|
|
Crafter®.Graphics
|
|
Copyright (C) 2026 Catcrafts®
|
|
catcrafts.net
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License version 3.0 as published by the Free Software Foundation;
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
// Regression test for issue #50: uiResolveScreenPixel used to run four float
|
|
// compares against hdr.clipRectPx for every pixel of every dispatch, even
|
|
// though the clip rect is the full-surface default {0,0,1e9,1e9} in the
|
|
// overwhelmingly common case. The compares are now gated on the reserved
|
|
// kUIFlagClip bit, which UIRenderer::FillHeader sets (via ClipFlags) only when
|
|
// the clip rect is actually narrower than the surface.
|
|
//
|
|
// ClipFlags is the pure CPU decision behind that gate: given a clip rect, the
|
|
// surface size, and the caller's user flags, it returns the header flag word
|
|
// with kUIFlagClip set iff the rect does not cover the whole surface. This
|
|
// test drives it directly with synthetic dimensions — no Window or device.
|
|
//
|
|
// The contract it must hold for the shader gate to be correct:
|
|
// * full-surface (and over-covering) clips must NOT set the bit, so the
|
|
// shader skips the compares — and the skipped compares would never have
|
|
// rejected an in-surface pixel anyway, so output is pixel-identical;
|
|
// * any clip narrower than the surface on ANY edge MUST set the bit;
|
|
// * the caller's user flag bits are preserved either way; only the reserved
|
|
// high bit is touched.
|
|
|
|
#include <cstdlib>
|
|
|
|
import Crafter.Graphics;
|
|
import std;
|
|
using namespace Crafter;
|
|
|
|
namespace {
|
|
|
|
int failures = 0;
|
|
|
|
void Check(bool ok, std::string_view what) {
|
|
std::println("{} {}", ok ? "PASS" : "FAIL", what);
|
|
if (!ok) ++failures;
|
|
}
|
|
|
|
constexpr std::array<float,4> kFullDefault = {0.0f, 0.0f, 1e9f, 1e9f};
|
|
|
|
bool ClipBitSet(std::array<float,4> rect, std::uint32_t w, std::uint32_t h,
|
|
std::uint32_t userFlags = 0) {
|
|
return (UIRenderer::ClipFlags(rect, w, h, userFlags) & kUIFlagClip) != 0u;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
constexpr std::uint32_t W = 1920, H = 1080;
|
|
|
|
// --- full-surface clips leave the bit clear (the fast path) -----------
|
|
Check(!ClipBitSet(kFullDefault, W, H),
|
|
"the {0,0,1e9,1e9} default does not set the clip bit");
|
|
Check(!ClipBitSet({0.0f, 0.0f, float(W), float(H)}, W, H),
|
|
"a clip exactly matching the surface does not set the clip bit");
|
|
Check(!ClipBitSet({-5.0f, -5.0f, float(W) + 10.0f, float(H) + 10.0f}, W, H),
|
|
"a clip that over-covers the surface does not set the clip bit");
|
|
|
|
// --- any narrower edge sets the bit -----------------------------------
|
|
Check(ClipBitSet({0.0f, 0.0f, float(W) - 1.0f, float(H)}, W, H),
|
|
"a clip one pixel short on the right edge sets the clip bit");
|
|
Check(ClipBitSet({0.0f, 0.0f, float(W), float(H) - 1.0f}, W, H),
|
|
"a clip one pixel short on the bottom edge sets the clip bit");
|
|
Check(ClipBitSet({1.0f, 0.0f, float(W), float(H)}, W, H),
|
|
"a clip inset on the left edge sets the clip bit");
|
|
Check(ClipBitSet({0.0f, 1.0f, float(W), float(H)}, W, H),
|
|
"a clip inset on the top edge sets the clip bit");
|
|
Check(ClipBitSet({100.0f, 100.0f, 200.0f, 200.0f}, W, H),
|
|
"a small interior clip rect sets the clip bit");
|
|
|
|
// --- user flag bits survive the decision ------------------------------
|
|
constexpr std::uint32_t kUser = 0x0000000Fu; // four low user bits
|
|
Check(UIRenderer::ClipFlags(kFullDefault, W, H, kUser) == kUser,
|
|
"full-surface clip preserves user flags and clears the reserved bit");
|
|
Check(UIRenderer::ClipFlags({0.0f, 0.0f, 200.0f, 200.0f}, W, H, kUser)
|
|
== (kUser | kUIFlagClip),
|
|
"narrow clip preserves user flags and sets the reserved bit");
|
|
// A caller that (wrongly) pre-set the reserved bit must not flip the
|
|
// decision for a full-surface clip — ClipFlags owns that bit.
|
|
Check(UIRenderer::ClipFlags(kFullDefault, W, H, kUIFlagClip | kUser) == kUser,
|
|
"full-surface clip clears a stale reserved bit the caller passed in");
|
|
|
|
if (failures != 0) {
|
|
std::println(std::cerr, "{} case(s) failed", failures);
|
|
return 1;
|
|
}
|
|
std::println(std::cout, "all UI clip-flag cases passed");
|
|
return 0;
|
|
}
|