Diff hidden because this file has more than 800 lines.
| … | 18 unchanged lines hidden | ||
| 19 | 19 | ||
| 20 | 20 | ## Execution model | |
| 21 | 21 | ||
| 22 | - | The authoritative runtime direction is a planned-IR executor: validation produces diagnostics, planning orders commands, and backend lowering walks the planned IR to issue backend operations. A gated backend-native planned draw path now handles materialized IR buffers, render passes, shaders, graphics pipelines, descriptor sets, direct indexed draws, and direct non-indexed draws. The current runtime translator that maps generated/imported producer forms into legacy 2D/3D renderer calls is migration-only. New backend parity work must not add feature-specific translator branches unless the branch is a temporary, tracked bridge with a removal path. | |
| 22 | + | The authoritative runtime direction is a planned-IR executor: validation produces diagnostics, planning orders commands, and backend lowering walks the planned IR to issue backend operations. A gated backend-native planned draw path now handles materialized IR buffers, render passes, shaders, graphics pipelines, descriptor sets, direct indexed draws, direct non-indexed draws, and supported borrowed imported mesh buffers. The current runtime translator that maps remaining generated/imported producer forms into legacy 2D/3D renderer calls is migration-only. New backend parity work must not add feature-specific translator branches unless the branch is a temporary, tracked bridge with a removal path. | |
| 23 | 23 | ||
| 24 | 24 | Runtime lowering must not fail silently. If a command cannot be lowered, `gpu` records a structured diagnostic with the command handle and reason, then fails submission or mode classification explicitly. | |
| 25 | 25 | ||
| … | 227 unchanged lines hidden | ||
Diff hidden because this file has more than 800 lines.
| … | 11 unchanged lines hidden | ||
| 12 | 12 | mock_planned_vertex_count: u32 | |
| 13 | 13 | mock_planned_instance_count: u32 | |
| 14 | 14 | mock_planned_bound_vertex_buffer: bk.Buffer_Handle | |
| 15 | + | mock_planned_bound_index_buffer: bk.Buffer_Handle | |
| 15 | 16 | mock_planned_bound_pipeline: bk.Pipeline_Handle | |
| 17 | + | mock_planned_bound_descriptor: bk.Descriptor_Handle | |
| 18 | + | mock_planned_push_constant_size: u32 | |
| 19 | + | mock_planned_indexed_count: u32 | |
| 20 | + | mock_planned_trace: [32]string | |
| 21 | + | mock_planned_trace_count: int | |
| 16 | 22 | ||
| 23 | + | mock_planned_trace_add :: proc(name: string) { | |
| 24 | + | if mock_planned_trace_count < len(mock_planned_trace) { | |
| 25 | + | mock_planned_trace[mock_planned_trace_count] = name | |
| 26 | + | mock_planned_trace_count += 1 | |
| 27 | + | } | |
| 28 | + | } | |
| 29 | + | ||
| 17 | 30 | mock_create_shader_module :: proc(desc: bk.Shader_Module_Desc) -> (bk.Shader_Handle, bool) { | |
| 18 | 31 | mock_shader_desc = desc | |
| 19 | 32 | return bk.Shader_Handle(77), true | |
| … | 3 unchanged lines hidden | ||
| 23 | 36 | ||
| 24 | 37 | mock_planned_begin_render_pass :: proc(ctx: bk.Frame_Context, desc: bk.Render_Pass_Begin_Desc) { | |
| 25 | 38 | mock_planned_begin_pass_count += 1 | |
| 39 | + | mock_planned_trace_add("begin_render_pass") | |
| 26 | 40 | } | |
| 27 | 41 | ||
| 28 | 42 | mock_planned_begin_default_pass :: proc(ctx: bk.Frame_Context, clear_color: [4]f32) { | |
| 29 | 43 | mock_planned_begin_pass_count += 1 | |
| 44 | + | mock_planned_trace_add("begin_default_pass") | |
| 30 | 45 | } | |
| 31 | 46 | ||
| 32 | 47 | mock_planned_end_render_pass :: proc(ctx: bk.Frame_Context) { | |
| 33 | 48 | mock_planned_end_pass_count += 1 | |
| 49 | + | mock_planned_trace_add("end_render_pass") | |
| 34 | 50 | } | |
| 35 | 51 | ||
| 36 | - | mock_planned_set_viewport :: proc(ctx: bk.Frame_Context, x, y, w, h: f32) {} | |
| 52 | + | mock_planned_set_viewport :: proc(ctx: bk.Frame_Context, x, y, w, h: f32) { | |
| 53 | + | mock_planned_trace_add("set_viewport") | |
| 54 | + | } | |
| 37 | 55 | ||
| 38 | - | mock_planned_set_scissor :: proc(ctx: bk.Frame_Context, x, y: i32, w, h: u32) {} | |
| 56 | + | mock_planned_set_scissor :: proc(ctx: bk.Frame_Context, x, y: i32, w, h: u32) { | |
| 57 | + | mock_planned_trace_add("set_scissor") | |
| 58 | + | } | |
| 39 | 59 | ||
| 40 | 60 | mock_planned_get_extent :: proc() -> bk.Extent { | |
| 41 | 61 | return {width = 640, height = 480} | |
| … | 12 unchanged lines hidden | ||
| 54 | 74 | ||
| 55 | 75 | mock_planned_bind_graphics_pipeline :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) { | |
| 56 | 76 | mock_planned_bound_pipeline = handle | |
| 77 | + | mock_planned_trace_add("bind_pipeline") | |
| 57 | 78 | } | |
| 58 | 79 | ||
| 80 | + | mock_planned_bind_descriptor_set :: proc( | |
| 81 | + | ctx: bk.Frame_Context, | |
| 82 | + | pipeline: bk.Pipeline_Handle, | |
| 83 | + | set: bk.Descriptor_Handle, | |
| 84 | + | index: u32, | |
| 85 | + | ) { | |
| 86 | + | mock_planned_bound_descriptor = set | |
| 87 | + | mock_planned_trace_add("bind_descriptor") | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | mock_planned_push_constants :: proc( | |
| 91 | + | ctx: bk.Frame_Context, | |
| 92 | + | pipeline: bk.Pipeline_Handle, | |
| 93 | + | stages: bk.Shader_Stage_Flags, | |
| 94 | + | offset, size: u32, | |
| 95 | + | data: rawptr, | |
| 96 | + | ) { | |
| 97 | + | mock_planned_push_constant_size = size | |
| 98 | + | mock_planned_trace_add("push_constants") | |
| 99 | + | } | |
| 100 | + | ||
| 59 | 101 | mock_planned_bind_vertex_buffer :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) { | |
| 60 | 102 | mock_planned_bound_vertex_buffer = handle | |
| 103 | + | mock_planned_trace_add("bind_vertex_buffer") | |
| 61 | 104 | } | |
| 62 | 105 | ||
| 63 | - | mock_planned_bind_index_buffer :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) {} | |
| 106 | + | mock_planned_bind_index_buffer :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) { | |
| 107 | + | mock_planned_bound_index_buffer = handle | |
| 108 | + | mock_planned_trace_add("bind_index_buffer") | |
| 109 | + | } | |
| 64 | 110 | ||
| 65 | 111 | mock_planned_draw :: proc( | |
| 66 | 112 | ctx: bk.Frame_Context, | |
| … | 4 unchanged lines hidden | ||
| 71 | 117 | mock_planned_draw_count += 1 | |
| 72 | 118 | mock_planned_vertex_count = vertex_count | |
| 73 | 119 | mock_planned_instance_count = instance_count | |
| 120 | + | mock_planned_trace_add("draw") | |
| 74 | 121 | } | |
| 75 | 122 | ||
| 76 | 123 | mock_planned_draw_indexed :: proc( | |
| … | 2 unchanged lines hidden | ||
| 79 | 126 | first_index: u32, | |
| 80 | 127 | vertex_offset: i32, | |
| 81 | 128 | first_instance: u32, | |
| 82 | - | ) {} | |
| 129 | + | ) { | |
| 130 | + | mock_planned_draw_count += 1 | |
| 131 | + | mock_planned_indexed_count = index_count | |
| 132 | + | mock_planned_instance_count = instance_count | |
| 133 | + | mock_planned_trace_add("draw_indexed") | |
| 134 | + | } | |
| 83 | 135 | ||
| 84 | 136 | mock_planned_destroy_buffer :: proc(handle: bk.Buffer_Handle) {} | |
| 85 | 137 | ||
| … | 12 unchanged lines hidden | ||
| 98 | 150 | create_graphics_pipeline = mock_planned_create_graphics_pipeline, | |
| 99 | 151 | destroy_graphics_pipeline = mock_planned_destroy_graphics_pipeline, | |
| 100 | 152 | bind_graphics_pipeline = mock_planned_bind_graphics_pipeline, | |
| 153 | + | push_constants = mock_planned_push_constants, | |
| 154 | + | bind_descriptor_set = mock_planned_bind_descriptor_set, | |
| 101 | 155 | bind_vertex_buffer = mock_planned_bind_vertex_buffer, | |
| 102 | 156 | bind_index_buffer = mock_planned_bind_index_buffer, | |
| 103 | 157 | draw = mock_planned_draw, | |
| … | 12 unchanged lines hidden | ||
| 116 | 170 | mock_planned_vertex_count = 0 | |
| 117 | 171 | mock_planned_instance_count = 0 | |
| 118 | 172 | mock_planned_bound_vertex_buffer = bk.NULL_BUFFER | |
| 173 | + | mock_planned_bound_index_buffer = bk.NULL_BUFFER | |
| 119 | 174 | mock_planned_bound_pipeline = bk.NULL_PIPELINE | |
| 175 | + | mock_planned_bound_descriptor = bk.NULL_DESCRIPTOR | |
| 176 | + | mock_planned_push_constant_size = 0 | |
| 177 | + | mock_planned_indexed_count = 0 | |
| 178 | + | mock_planned_trace_count = 0 | |
| 120 | 179 | } | |
| 121 | 180 | ||
| 181 | + | mock_imported_draw_resolver :: proc( | |
| 182 | + | user_data: rawptr, | |
| 183 | + | frame: ^ir.Frame_IR, | |
| 184 | + | command: ir.Command_Handle, | |
| 185 | + | draw: ^ir.Draw_Command, | |
| 186 | + | out: ^compiler.Imported_Draw_Binding, | |
| 187 | + | ) -> bool { | |
| 188 | + | _ = user_data | |
| 189 | + | _ = frame | |
| 190 | + | _ = command | |
| 191 | + | _ = draw | |
| 192 | + | out.pipeline = bk.Pipeline_Handle(91) | |
| 193 | + | out.descriptor_sets[0] = bk.Descriptor_Handle(92) | |
| 194 | + | out.descriptor_set_indices[0] = 0 | |
| 195 | + | out.descriptor_set_count = 1 | |
| 196 | + | out.push_constant_stages = {.Vertex} | |
| 197 | + | out.push_constant_size = 16 | |
| 198 | + | out.vertex_buffer = bk.Buffer_Handle(93) | |
| 199 | + | out.index_buffer = bk.Buffer_Handle(94) | |
| 200 | + | out.index_count = 36 | |
| 201 | + | out.instance_count = 1 | |
| 202 | + | out.indexed = true | |
| 203 | + | return true | |
| 204 | + | } | |
| 205 | + | ||
| 122 | 206 | @(test) | |
| 123 | 207 | test_compiler_materializes_offscreen_frame_target :: proc(t: ^testing.T) { | |
| 124 | 208 | frame := ir.init_frame_ir() | |
| … | 347 unchanged lines hidden | ||
| 472 | 556 | testing.expect_value(t, mock_planned_instance_count, u32(2)) | |
| 473 | 557 | testing.expect_value(t, mock_planned_bound_vertex_buffer, bk.Buffer_Handle(12)) | |
| 474 | 558 | testing.expect_value(t, mock_planned_bound_pipeline, bk.Pipeline_Handle(31)) | |
| 559 | + | ||
| 560 | + | reset_planned_mock() | |
| 561 | + | imported_frame := ir.init_frame_ir() | |
| 562 | + | defer ir.destroy_frame_ir(&imported_frame) | |
| 563 | + | ||
| 564 | + | imported_buffer := ir.add_buffer( | |
| 565 | + | &imported_frame, | |
| 566 | + | "imported-mesh", | |
| 567 | + | 0, | |
| 568 | + | {.Vertex, .Index}, | |
| 569 | + | {}, | |
| 570 | + | .Imported, | |
| 571 | + | ) | |
| 572 | + | _ = ir.add_draw_packet( | |
| 573 | + | &imported_frame, | |
| 574 | + | ir.INVALID_PASS, | |
| 575 | + | ir.INVALID_PIPELINE, | |
| 576 | + | .Indexed_Mesh, | |
| 577 | + | { | |
| 578 | + | geometry = {kind = .Indexed_Mesh, resource = imported_buffer, index_count = 36}, | |
| 579 | + | order = .Strict, | |
| 580 | + | }, | |
| 581 | + | ) | |
| 582 | + | ||
| 583 | + | imported_state := compiler.init_execution_state(&backend) | |
| 584 | + | defer compiler.destroy_execution_state(&imported_state) | |
| 585 | + | compiler.set_imported_draw_resolver(&imported_state, mock_imported_draw_resolver, nil) | |
| 586 | + | ||
| 587 | + | testing.expect(t, compiler.prepare_planned_frame(&imported_state, &imported_frame)) | |
| 588 | + | testing.expect( | |
| 589 | + | t, | |
| 590 | + | compiler.execute_prepared_planned_commands( | |
| 591 | + | &imported_state, | |
| 592 | + | &imported_frame, | |
| 593 | + | {}, | |
| 594 | + | {0, 0, 0, 1}, | |
| 595 | + | ), | |
| 596 | + | ) | |
| 597 | + | testing.expect_value(t, mock_planned_draw_count, 1) | |
| 598 | + | testing.expect_value(t, mock_planned_indexed_count, u32(36)) | |
| 599 | + | testing.expect_value(t, mock_planned_bound_pipeline, bk.Pipeline_Handle(91)) | |
| 600 | + | testing.expect_value(t, mock_planned_bound_descriptor, bk.Descriptor_Handle(92)) | |
| 601 | + | testing.expect_value(t, mock_planned_bound_vertex_buffer, bk.Buffer_Handle(93)) | |
| 602 | + | testing.expect_value(t, mock_planned_bound_index_buffer, bk.Buffer_Handle(94)) | |
| 603 | + | testing.expect_value(t, mock_planned_push_constant_size, u32(16)) | |
| 604 | + | ||
| 605 | + | expected := [?]string { | |
| 606 | + | "begin_default_pass", | |
| 607 | + | "set_viewport", | |
| 608 | + | "set_scissor", | |
| 609 | + | "bind_pipeline", | |
| 610 | + | "bind_descriptor", | |
| 611 | + | "push_constants", | |
| 612 | + | "bind_vertex_buffer", | |
| 613 | + | "bind_index_buffer", | |
| 614 | + | "draw_indexed", | |
| 615 | + | "end_render_pass", | |
| 616 | + | } | |
| 617 | + | testing.expect_value(t, mock_planned_trace_count, len(expected)) | |
| 618 | + | for event, i in expected { | |
| 619 | + | testing.expect_value(t, mock_planned_trace[i], event) | |
| 620 | + | } | |
| 475 | 621 | } | |