EVU 75% & 100% bars pattern

This commit is contained in:
Dmitry Sergeev
2026-09-16 09:14:56 +03:00
parent 4726a23d21
commit 8727a9a357
5 changed files with 163 additions and 23 deletions
+22 -10
View File
@@ -190,17 +190,29 @@ type pattern struct {
} }
var patterns = map[string]pattern{ var patterns = map[string]pattern{
"bars": { "ebu75": {
name: "bars", name: "ebu75",
description: "75% color bars", description: "EBU 75% Color Bar Signal",
kernelPath: "kernels/static/colorBars.wgsl", kernelPath: "kernels/static/ebu75.wgsl",
motion: false, motion: false,
}, },
"bars-move": { "ebu75-move": {
name: "bars-move", name: "ebu75-move",
description: "75% color bars with moving square", description: "EBU 75% Color Bar Signal with moving square",
kernelPath: "kernels/dynamic/colorBars.wgsl", kernelPath: "kernels/dynamic/ebu75.wgsl",
motion: true, motion: false,
},
"ebu100": {
name: "ebu100",
description: "EBU 100% Color Bar Signal",
kernelPath: "kernels/static/ebu100.wgsl",
motion: false,
},
"ebu100-move": {
name: "ebu100-move",
description: "EBU 100% Color Bar Signal with moving square",
kernelPath: "kernels/dynamic/ebu100.wgsl",
motion: false,
}, },
"smpte": { "smpte": {
name: "smpte", name: "smpte",
@@ -210,7 +222,7 @@ var patterns = map[string]pattern{
}, },
"smpte-move": { "smpte-move": {
name: "smpte-move", name: "smpte-move",
description: "SMPTE RP-219 Color Bar Signal", description: "SMPTE RP-219 Color Bar Signal with moving square",
kernelPath: "kernels/dynamic/smpteBars.wgsl", kernelPath: "kernels/dynamic/smpteBars.wgsl",
motion: false, motion: false,
}, },
+77
View File
@@ -0,0 +1,77 @@
// EBU 100% bars, Rec.709, 10-bit Y'CbCr, packed as v210 (4:2:2).
// One work-item per 16-byte block = 6 pixels (6 Y + 3 Cb + 3 Cr).
struct Params {
width: u32,
height: u32,
frame: u32, // animation tick (0,1,2,... per generated frame), NOT the raw grain index
_pad0: u32,
};
@group(0) @binding(0) var<storage, read_write> out: array<u32>;
@group(0) @binding(1) var<uniform> params: Params;
fn bar_index(px: u32) -> u32 {
return min((px * 8u) / params.width, 7u);
}
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
let block = gid.x;
let total = (params.width * params.height) / 6u;
if (block >= total) {
return;
}
let x = (block * 6u) % params.width;
let py = (block * 6u) / params.width;
// Bar order: 100% white, yellow, cyan, green, magenta, red, blue, black
let y_tab = array<u32, 8>(940u, 877u, 754u, 691u, 313u, 250u, 127u, 64u);
let cb_tab = array<u32, 8>(512u, 64u, 615u, 167u, 857u, 409u, 960u, 512u);
let cr_tab = array<u32, 8>(512u, 553u, 64u, 105u, 919u, 960u, 471u, 512u);
// Moving square: horizontal oscillation around screen center.
// frame is a small tick; converting the huge raw grain index here
// would destroy f32 precision and freeze the motion.
let centerX = f32(params.width) / 2.0;
let centerY = f32(params.height) / 2.0;
let fps = 25.0;
let t = f32(params.frame) / fps;
const squareSize = 150u;
let half = f32(squareSize) / 2.0;
let offsetPixels = sin(t * 0.5) * (centerX - half);
let sq_x_min = centerX - half + offsetPixels;
let sq_x_max = centerX + half + offsetPixels;
let sq_y_min = centerY - half;
let sq_y_max = centerY + half;
let py_f = f32(py);
var y: array<u32, 6>;
var cb: array<u32, 6>;
var cr: array<u32, 6>;
for (var i = 0u; i < 6u; i++) {
let px = f32(x + i);
let b = bar_index(x + i);
if (px >= sq_x_min && px < sq_x_max && py_f >= sq_y_min && py_f < sq_y_max) {
// inverted bar: luma mirrored across studio range (64..940),
// chroma mirrored across neutral 512
y[i] = 1004u - y_tab[b];
cb[i] = 1024u - cb_tab[b];
cr[i] = 1024u - cr_tab[b];
} else {
y[i] = y_tab[b];
cb[i] = cb_tab[b];
cr[i] = cr_tab[b];
}
}
// v210 word layout, chroma co-sited with luma samples 0/2/4:
// w0 = Cb0|Y0<<10|Cr0<<20; w1 = Y1|Cb2<<10|Y2<<20;
// w2 = Cr2|Y3<<10|Cb4<<20; w3 = Y4|Cr4<<10|Y5<<20
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);
}
@@ -1,4 +1,4 @@
// 75% SMPTE color bars + moving square, Rec.709, 10-bit Y'CbCr, packed as v210 (4:2:2). // EBU 75% bars, Rec.709, 10-bit Y'CbCr, packed as v210 (4:2:2).
// One work-item per 16-byte block = 6 pixels (6 Y + 3 Cb + 3 Cr). // One work-item per 16-byte block = 6 pixels (6 Y + 3 Cb + 3 Cr).
struct Params { struct Params {
@@ -12,7 +12,7 @@ struct Params {
@group(0) @binding(1) var<uniform> params: Params; @group(0) @binding(1) var<uniform> params: Params;
fn bar_index(px: u32) -> u32 { fn bar_index(px: u32) -> u32 {
return min((px * 7u) / params.width, 6u); return min((px * 8u) / params.width, 7u);
} }
@compute @workgroup_size(64) @compute @workgroup_size(64)
@@ -26,10 +26,10 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
let x = (block * 6u) % params.width; let x = (block * 6u) % params.width;
let py = (block * 6u) / params.width; let py = (block * 6u) / params.width;
// Bar order: 75% white, yellow, cyan, green, magenta, red, blue // Bar order: 75% white, yellow, cyan, green, magenta, red, blue, black
let y_tab = array<u32, 7>(721u, 674u, 581u, 534u, 251u, 204u, 111u); let y_tab = array<u32, 8>(721u, 674u, 581u, 534u, 251u, 204u, 111u, 64u);
let cb_tab = array<u32, 7>(512u, 176u, 589u, 253u, 771u, 435u, 848u); let cb_tab = array<u32, 8>(512u, 176u, 589u, 253u, 771u, 435u, 848u, 512u);
let cr_tab = array<u32, 7>(512u, 543u, 176u, 207u, 817u, 848u, 481u); let cr_tab = array<u32, 8>(512u, 543u, 176u, 207u, 817u, 848u, 481u, 512u);
// Moving square: horizontal oscillation around screen center. // Moving square: horizontal oscillation around screen center.
// frame is a small tick; converting the huge raw grain index here // frame is a small tick; converting the huge raw grain index here
+50
View File
@@ -0,0 +1,50 @@
// EBU 100% bars, Rec.709, 10-bit Y'CbCr, packed as v210 (4:2:2).
// One work-item per 16-byte block = 6 pixels (6 Y + 3 Cb + 3 Cr).
struct Params {
width: u32,
height: u32,
frame: u32, // animation tick (0,1,2,... per generated frame), NOT the raw grain index
_pad0: u32,
};
@group(0) @binding(0) var<storage, read_write> out: array<u32>;
@group(0) @binding(1) var<uniform> params: Params;
fn bar_index(px: u32) -> u32 {
return min((px * 8u) / params.width, 7u);
}
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
let block = gid.x;
let total = (params.width * params.height) / 6u;
if (block >= total) {
return;
}
let x = (block * 6u) % params.width;
// Bar order: 100% white, yellow, cyan, green, magenta, red, blue, black
let y_tab = array<u32, 8>(940u, 877u, 754u, 691u, 313u, 250u, 127u, 64u);
let cb_tab = array<u32, 8>(512u, 64u, 615u, 167u, 857u, 409u, 960u, 512u);
let cr_tab = array<u32, 8>(512u, 553u, 64u, 105u, 919u, 960u, 471u, 512u);
var y: array<u32, 6>;
var cb: array<u32, 6>;
var cr: array<u32, 6>;
for (var i = 0u; i < 6u; i++) {
let b = bar_index(x + i);
y[i] = y_tab[b];
cb[i] = cb_tab[b];
cr[i] = cr_tab[b];
}
// v210 word layout, chroma co-sited with luma samples 0/2/4:
// w0 = Cb0|Y0<<10|Cr0<<20; w1 = Y1|Cb2<<10|Y2<<20;
// w2 = Cr2|Y3<<10|Cb4<<20; w3 = Y4|Cr4<<10|Y5<<20
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);
}
@@ -1,10 +1,10 @@
// 75% SMPTE color bars, Rec.709, 10-bit Y'CbCr, packed as v210 (4:2:2). // EBU 75% bars, Rec.709, 10-bit Y'CbCr, packed as v210 (4:2:2).
// One work-item per 16-byte block = 6 pixels (6 Y + 3 Cb + 3 Cr). // One work-item per 16-byte block = 6 pixels (6 Y + 3 Cb + 3 Cr).
struct Params { struct Params {
width: u32, width: u32,
height: u32, height: u32,
frame: u32, frame: u32, // animation tick (0,1,2,... per generated frame), NOT the raw grain index
_pad0: u32, _pad0: u32,
}; };
@@ -12,7 +12,7 @@ struct Params {
@group(0) @binding(1) var<uniform> params: Params; @group(0) @binding(1) var<uniform> params: Params;
fn bar_index(px: u32) -> u32 { fn bar_index(px: u32) -> u32 {
return min((px * 7u) / params.width, 6u); return min((px * 8u) / params.width, 7u);
} }
@compute @workgroup_size(64) @compute @workgroup_size(64)
@@ -25,10 +25,11 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
let x = (block * 6u) % params.width; let x = (block * 6u) % params.width;
// Bar order: 75% white, yellow, cyan, green, magenta, red, blue // Bar order: 75% white, yellow, cyan, green, magenta, red, blue, black
let y_tab = array<u32, 7>(721u, 674u, 581u, 534u, 251u, 204u, 111u); let y_tab = array<u32, 8>(721u, 674u, 581u, 534u, 251u, 204u, 111u, 64u);
let cb_tab = array<u32, 7>(512u, 176u, 589u, 253u, 771u, 435u, 848u); let cb_tab = array<u32, 8>(512u, 176u, 589u, 253u, 771u, 435u, 848u, 512u);
let cr_tab = array<u32, 7>(512u, 543u, 176u, 207u, 817u, 848u, 481u); let cr_tab = array<u32, 8>(512u, 543u, 176u, 207u, 817u, 848u, 481u, 512u);
var y: array<u32, 6>; var y: array<u32, 6>;
var cb: array<u32, 6>; var cb: array<u32, 6>;