// SMPTE RP 219-1 Color Bar Pattern Generator // 10-bit Y'CbCr packed as v210 (4:2:2). Compliant with SMPTE RP 219-1:2014 struct Params { width: u32, height: u32, frame: u32, _pad0: u32, }; @group(0) @binding(0) var out: array; @group(0) @binding(1) var params: Params; fn bar_index(px: u32, bars_75_width: u32) -> u32 { return min((px * 7u) / bars_75_width, 6u); } @compute @workgroup_size(64) fn main(@builtin(global_invocation_id) gid: vec3) { let block = gid.x; let total = (params.width * params.height) / 6u; if (block >= total) { return; } let x = (block * 6u) % params.width; let y_px = (block * 6u) / params.width; // 75% Color Table: white, yellow, cyan, green, magenta, red, blue let y_tab = array(721u, 674u, 581u, 534u, 251u, 204u, 111u); let cb_tab = array(512u, 176u, 589u, 253u, 771u, 435u, 848u); let cr_tab = array(512u, 543u, 176u, 207u, 817u, 848u, 481u); // Layout calculations mapping the 4:3 area centered in a 16:9 canvas let bars_width: u32 = (params.height / 3u) * 4u; let bars_start: u32 = (params.width - bars_width) / 2u; let bars_end: u32 = bars_width + bars_start; let one_bar_width: u32 = bars_width / 7u; // Strict vertical boundary calculations based on 12th parts let unit_h: u32 = params.height / 12u; let bound_s1: u32 = unit_h * 7u; // Row 630 on 1080p let bound_s2: u32 = bound_s1 + unit_h; // Row 720 on 1080p let bound_s3: u32 = bound_s2 + unit_h; // Row 810 on 1080p // Section 3 Linear Y-Ramp boundaries let ramp_start = bars_start + one_bar_width; let ramp_end = bars_end; let ramp_width = ramp_end - ramp_start; var y: array; var cb: array; var cr: array; for (var i = 0u; i < 6u; i++) { let px_x = x + i; // SECTION 1 (Top 7/12): Main 75% Color Bars if (y_px < bound_s1) { if (px_x >= bars_start && px_x < bars_end) { let b = bar_index(px_x - bars_start, bars_width); y[i] = y_tab[b]; cb[i] = cb_tab[b]; cr[i] = cr_tab[b]; } else { // 40% Gray Flanks y[i] = 414u; cb[i] = 512u; cr[i] = 512u; } } // SECTION 2 (Next 1/12): 100% Cyan, -I Signal, 75% White, 100% Blue else if (y_px >= bound_s1 && y_px < bound_s2) { if (px_x < bars_start) { y[i] = 754u; cb[i] = 615u; cr[i] = 64u; // 100% Cyan } else if (px_x >= bars_start && px_x < ramp_start) { y[i] = 244u; cb[i] = 612u; cr[i] = 395u; // -I Signal } else if (px_x >= bars_end) { y[i] = 127u; cb[i] = 960u; cr[i] = 471u; // 100% Blue } else { y[i] = 721u; cb[i] = 512u; cr[i] = 512u; // 75% White } } // SECTION 3 (Next 1/12): 100% Yellow, +Q Signal, Linear Y-Ramp, 75% Red else if (y_px >= bound_s2 && y_px < bound_s3) { if (px_x < bars_start) { y[i] = 877u; cb[i] = 64u; cr[i] = 553u; // 100% Yellow } else if (px_x >= bars_start && px_x < ramp_start) { y[i] = 141u; cb[i] = 697u; cr[i] = 606u; // +Q Signal } else if (px_x >= bars_end) { y[i] = 250u; cb[i] = 409u; cr[i] = 960u; // 75% Red } else { // Perfect linear scaling from Digital Black (64u) to Digital White (940u) let clamped_x = clamp(px_x, ramp_start, ramp_end); y[i] = 64u + ((clamped_x - ramp_start) * (940u - 64u)) / ramp_width; cb[i] = 512u; cr[i] = 512u; } } // SECTION 4 (Bottom 3/12): Pluge Panel Calibration Blocks else { if (px_x >= bars_start && px_x < bars_end) { let b = (px_x - bars_start) / one_bar_width; if (b == 0u || b == 2u || b >= 4u) { y[i] = 64u; cb[i] = 512u; cr[i] = 512u; // 0% Black } else if (b == 1u) { y[i] = 940u; cb[i] = 512u; cr[i] = 512u; // 100% White Calibration } else if (b == 3u) { // PLUGE Sub-bar generation (Split into 3 absolute equal fragments) let sub_bar_offset = (px_x - bars_start) % one_bar_width; let sub_b = (sub_bar_offset * 3u) / one_bar_width; cb[i] = 512u; cr[i] = 512u; if (sub_b == 0u) { y[i] = 46u; // -2% Sub-Black } else if (sub_b == 1u) { y[i] = 64u; // 0% Black } else { y[i] = 82u; // +2% Above-Black } } } else { // 15% Gray Flanks y[i] = 195u; cb[i] = 512u; cr[i] = 512u; } } } // Packing into standard v210 10-bit macroblock layout words out[block * 4u + 0u] = (cb[0] & 0x3FFu) | ((y[0] & 0x3FFu) << 10u) | ((cr[0] & 0x3FFu) << 20u); out[block * 4u + 1u] = (y[1] & 0x3FFu) | ((cb[2] & 0x3FFu) << 10u) | ((y[2] & 0x3FFu) << 20u); out[block * 4u + 2u] = (cr[2] & 0x3FFu) | ((y[3] & 0x3FFu) << 10u) | ((cb[4] & 0x3FFu) << 20u); out[block * 4u + 3u] = (y[4] & 0x3FFu) | ((cr[4] & 0x3FFu) << 10u) | ((y[5] & 0x3FFu) << 20u); }