code wiki / (root) / nx_perceptual_render_test.nx

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}