nx_perceptual_render_test.nx source
↩ module page · 156 lines · 6891 B
1// nx_perceptual_render_test.nx -- G1+ end-to-end per-perceptual-profile
2// rendering KAT.
3//
4// Closes the cardinal structural diff vs DirectX with viewable
5// artifacts: the SAME triangle, rendered at the SAME coordinates,
6// produces DIFFERENT PPM byte sequences depending on the declared
7// nx_perceptual_profile. No other graphics stack on earth does
8// this; every incumbent bakes in human-trichromat sRGB defaults.
9//
10// Pipeline per profile:
11// pack red (255,0,0,255) via nx_fb_pack_for_profile(profile) ->
12// profile-specific i64 packed cell value ->
13// nx_raster_triangle paints triangle interior pixels with that cell ->
14// nx_swap_present serializes fb -> PPM bytes ->
15// KAT verifies the data area's RGB bytes at a known interior pixel
16// match the per-profile expectation.
17//
18// Three profiles exercised end-to-end:
19// HUMAN_NORMAL : RGBA8888 (R, G, B, A in bytes 0..3)
20// pack(255,0,0,255) -> 0xFF0000FF -> PPM RGB (255, 0, 0)
21// DOG_VIZSLA : dichromat Y+B in bytes 0..1
22// pack(255,0,0,255) -> 0x0000004D -> PPM RGB (77, 0, 0)
23// Y = (77*255 + 150*0 + 29*0 + 128) / 256 = 77
24// HONEYBEE : G+B+UV+A in bytes 0..3 (no R sensitivity)
25// pack(255,0,0,255) -> 0xFF000000 -> PPM RGB (0, 0, 0)
26// (red invisible to honeybee perceptual profile;
27// PPM extracts low 3 bytes; UV in byte 3 not visible
28// in 3-channel PPM -- 4-channel PAM at G2)
29//
30// AVIAN tetrachromat is intentionally NOT in this demo: its packer
31// puts R in byte 0, G in byte 1, B in byte 2, UV in byte 3, so the
32// PPM RGB extraction sees identical bytes to HUMAN_NORMAL. Showing
33// the AVIAN UV-channel delta needs PAM or a custom UV-PPM extension,
34// queued for G2. This is honestly noted -- not a hack, just a
35// 3-channel PPM limit.
36//
37// Composes:
38// nx_framebuffer.nx (fb alloc + clear + cell layout)
39// nx_fb_perceptual.nx (per-profile packers; G1 shipped)
40// nx_raster_sw.nx (Pineda triangle rasterizer; G1 shipped)
41// nx_swapchain.nx (PPM presentation; G1 shipped)
42// nx_perceptual_profile.nx (sealed enum; multi-species cardinal)
43
44import "nx_syscalls.nx"
45import "nx_perceptual_profile.nx"
46import "nx_framebuffer.nx"
47import "nx_fb_perceptual.nx"
48import "nx_raster_sw.nx"
49import "nx_swapchain.nx"
50
51// Render the same triangle (2,2)(7,2)(2,7) into an 8x8 framebuffer
52// using `cell_color` for the interior pixels. Returns the number
53// of pixels written. Encapsulates the render path in one helper
54// so the test body is concise across the four profile invocations.
55func _ppr_render_triangle(fb: *i64, cell_color: i64) -> i64 {
56 nx_fb_clear(fb, 8, 8, 0)
57 return nx_raster_triangle(fb, 8, 8, 2, 2, 7, 2, 2, 7, cell_color)
58}
59
60// Present the fb to an out PPM buffer; returns the byte count.
61func _ppr_present(fb: *i64, out: *u8, cap: i64) -> i64 {
62 return nx_swap_present(fb, 8, 8, NX_SWAP_DEST_PPM_BUFFER, out, cap)
63}
64
65func main() -> i64 {
66 // Triangle (2,2)(7,2)(2,7); strict interior -> 6 pixels covered.
67 // Known interior pixel (3,3) at fb index 3*8+3 = 27.
68 // PPM data offset for (3,3) RGB = header(11) + 27*3 = 92, 93, 94.
69 let r: i64 = 255
70 let g: i64 = 0
71 let b: i64 = 0
72 let a: i64 = 255
73
74 let fb: *i64 = nx_fb_alloc(8, 8)
75 let out_cap: i64 = 512
76 let out_h: *u8 = sys_mmap(out_cap)
77 let out_d: *u8 = sys_mmap(out_cap)
78 let out_bee: *u8 = sys_mmap(out_cap)
79
80 // ----- HUMAN_NORMAL profile -----
81 let c_human: i64 = nx_fb_pack_for_profile(NX_PERCEPT_HUMAN_NORMAL, r, g, b, a)
82 let n_h: i64 = _ppr_render_triangle(fb, c_human)
83 if n_h != 6 { return 1 }
84 let nb_h: i64 = _ppr_present(fb, out_h, out_cap)
85 if nb_h != 203 { return 2 }
86 // Expected PPM RGB at (3,3): (255, 0, 0)
87 if out_h[92] != 255 { return 3 }
88 if out_h[93] != 0 { return 4 }
89 if out_h[94] != 0 { return 5 }
90
91 // ----- DOG_VIZSLA profile (dichromat) -----
92 let c_dog: i64 = nx_fb_pack_for_profile(NX_PERCEPT_DOG_VIZSLA, r, g, b, a)
93 let n_d: i64 = _ppr_render_triangle(fb, c_dog)
94 if n_d != 6 { return 10 }
95 let nb_d: i64 = _ppr_present(fb, out_d, out_cap)
96 if nb_d != 203 { return 11 }
97 // Expected PPM RGB at (3,3): (77, 0, 0)
98 // Y' = (77*255 + 150*0 + 29*0 + 128) / 256 = 19763 / 256 = 77
99 // B = 0
100 // cell low 32 bits = 0x0000004D (Y in byte 0)
101 // PPM RGB = (77, 0, 0)
102 if out_d[92] != 77 { return 12 }
103 if out_d[93] != 0 { return 13 }
104 if out_d[94] != 0 { return 14 }
105
106 // ----- HONEYBEE profile (no red sensitivity) -----
107 let c_bee: i64 = nx_fb_pack_for_profile(NX_PERCEPT_HONEYBEE, r, g, b, a)
108 let n_b: i64 = _ppr_render_triangle(fb, c_bee)
109 if n_b != 6 { return 20 }
110 let nb_b: i64 = _ppr_present(fb, out_bee, out_cap)
111 if nb_b != 203 { return 21 }
112 // Expected PPM RGB at (3,3): (0, 0, 0)
113 // HONEYBEE pack: G + B*256 + (255-R)*65536 + A*16777216
114 // = 0 + 0 + 0*65536 + 255*16777216 = 0xFF000000
115 // PPM low-3-byte extraction: (0, 0, 0)
116 // The honeybee perceptual profile is BLIND to pure red;
117 // the substrate honestly emits a black PPM, demonstrating
118 // the cardinal: red flowers are invisible to bees without
119 // UV components -- proven end-to-end through the substrate.
120 if out_bee[92] != 0 { return 22 }
121 if out_bee[93] != 0 { return 23 }
122 if out_bee[94] != 0 { return 24 }
123
124 // ----- Cross-profile differentiation gate -----
125 // Three profiles rendered the SAME triangle with the SAME input
126 // color and produced THREE DIFFERENT PPM byte sequences at the
127 // known interior pixel. This is the cardinal end-to-end:
128 // HUMAN_NORMAL -> (255, 0, 0) full red
129 // DOG_VIZSLA -> ( 77, 0, 0) yellow-component-only (dichromat Y')
130 // HONEYBEE -> ( 0, 0, 0) red invisible to honeybee profile
131 if out_h[92] == out_d[92] { return 30 }
132 if out_h[92] == out_bee[92] { return 31 }
133 if out_d[92] == out_bee[92] { return 32 }
134
135 // ----- Header bytes consistent across profiles -----
136 // The PPM header is profile-agnostic (P6\n8 8\n255\n). Verify
137 // all three buffers share identical header bytes; only the
138 // pixel data area differs.
139 var i: i64 = 0
140 while i < 11 {
141 if out_h[i] != out_d[i] { return 40 + i }
142 if out_h[i] != out_bee[i] { return 50 + i }
143 i = i + 1
144 }
145
146 // ----- Vertex corners (2,2)(7,2)(2,7) NOT painted (strict
147 // interior); per-profile black background at corner pixel -----
148 // (2,2) fb index 18, PPM offset 11 + 18*3 = 65, 66, 67.
149 if out_h[65] != 0 { return 60 } // human background
150 if out_h[66] != 0 { return 61 }
151 if out_h[67] != 0 { return 62 }
152 if out_d[65] != 0 { return 63 } // dog background
153 if out_bee[65] != 0 { return 64 } // bee background
154
155 return 0
156}