| … | 394 unchanged lines hidden | ||
| 395 | 395 | usage: Image_Usage_Flags, | |
| 396 | 396 | } | |
| 397 | 397 | ||
| 398 | + | Readback_Texture_Desc :: struct { | |
| 399 | + | texture: Texture_Handle, | |
| 400 | + | width, height: u32, | |
| 401 | + | current_layout: Image_Layout, | |
| 402 | + | } | |
| 403 | + | ||
| 398 | 404 | Sampler_Desc :: struct { | |
| 399 | 405 | mag_filter: Filter, | |
| 400 | 406 | min_filter: Filter, | |
| … | 140 unchanged lines hidden | ||
| 541 | 547 | bool, | |
| 542 | 548 | ), | |
| 543 | 549 | destroy_texture: proc(handle: Texture_Handle), | |
| 550 | + | read_texture_rgba8: proc(desc: Readback_Texture_Desc, out: []u8) -> bool, | |
| 544 | 551 | ||
| 545 | 552 | // --- Samplers --- | |
| 546 | 553 | create_sampler: proc(desc: Sampler_Desc) -> (Sampler_Handle, bool), | |
| … | 179 unchanged lines hidden | ||
| … | 333 unchanged lines hidden | ||
| 334 | 334 | // Textures | |
| 335 | 335 | create_texture = create_texture_vk, | |
| 336 | 336 | destroy_texture = destroy_texture_vk, | |
| 337 | + | read_texture_rgba8 = read_texture_rgba8_vk, | |
| 337 | 338 | ||
| 338 | 339 | // Samplers | |
| 339 | 340 | create_sampler = create_sampler_vk, | |
| … | 206 unchanged lines hidden | ||
Diff hidden because this file has more than 800 lines.
| … | 255 unchanged lines hidden | ||
| 256 | 256 | - invalid clips/targets do not silently cull | |
| 257 | 257 | - particle-specific semantic draw kinds are absent from final core IR | |
| 258 | 258 | - Vulkan/D3D11/D3D12/OpenGL checks cover backend-neutral lowering | |
| 259 | + | ||
| 260 | + | Visual/runtime proof: | |
| 261 | + | ||
| 262 | + | - Final proof artifacts must come from deterministic readback images, not window screenshots. | |
| 263 | + | - The proof image contract is RGBA8, fixed dimensions, top-left assertion coordinates, straight alpha, and exact byte colors unless a test explicitly declares tolerance. | |
| 264 | + | - Window screenshots are smoke artifacts only and must not close backend-parity ledger rows. | |