Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions ss_player/ss_internal_player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,18 @@ bool SsInternalPlayer::_build_mask_writers(const DrawFrame& f) {
// drawing its own colour over the scene.
if (pure_mask) pure_mask_flags[p_idx] = 1;

// A writer only writes where its per-frame mask is non-zero and it is not
// hidden (Rule_Mask.md §2-2). Both live in FrameData, so this is the one
// half of the classification PartData cannot answer: an authored MASK of 0
// is how a writer is switched off over a range of frames, and the cutout
// threshold cannot stand in for it — a shape mask samples no texture, so
// its coverage is its whole geometry whatever the threshold says. A part
// with no PartState this frame was not evaluated and writes nothing, which
// is what `_bake_coverage_geometry` would decide about it anyway.
// Tested before the bitmap budget so a switched-off writer costs no bit.
const auto* ps = (p_idx < (int)_parts_by_idx.size()) ? _parts_by_idx[p_idx] : nullptr;
if (!ps || ps->mask() == 0.0f || ps->hide()) continue;

// Bitmap holds 24 writers; keep scanning past that so the pure masks that
// did not fit are still flagged above.
if ((int)_mask_writers.size() >= MAX_MASK_WRITERS) continue;
Expand Down
13 changes: 8 additions & 5 deletions ss_player/ss_internal_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,8 @@ class SsInternalPlayer {

// ---- CBP masking (clever bit packing) ----------------------------------
// One entry per part that writes the mask this frame. Rebuilt by
// `_build_mask_writers` each frame from draw_order + static PartData.
// `_build_mask_writers` each frame from draw_order, static PartData, and the
// per-frame mask / hide that say whether a writer writes at all.
// `bit` is the writer's slot in the coverage bitmap (0..MAX_MASK_WRITERS-1).
// `op_invert` comes from PartData.mask_influence (false=increment / true=
// invert). `is_clipping` selects the scope direction: a pure Mask part
Expand All @@ -575,11 +576,13 @@ class SsInternalPlayer {
Vector<MaskWriter> _mask_writers;
// Per part index (parallel to `_parts_by_idx`): 1 when the part is a "pure"
// mask this frame. Rebuilt with `_mask_writers` so the emit paths can test it
// once per part without walking the writer list.
// once per part without walking the writer list. A pure mask keeps this flag
// when it writes nothing — switched off, hidden, or past the bit budget — so
// that it still draws no colour of its own.
LocalVector<uint8_t> _part_pure_mask;
// Populate `_mask_writers` from this frame's draw_order + static PartData.
// Returns true if at least one writer is present (i.e., masking is active
// this frame). Only the top-root player owns the mask state.
// Populate `_mask_writers` from this frame's draw_order, static PartData and
// per-frame mask / hide. Returns true if at least one writer is present (i.e.,
// masking is active this frame). Only the top-root player owns the mask state.
bool _build_mask_writers(const DrawFrame& f);

// ---- CBP coverage bitmap (offscreen RGBA8 = 32 mask bits) --------------
Expand Down
Loading