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, 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. | |
| 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, supported borrowed imported mesh buffers, lit imported meshes, imported rect clips, and the bounded generated 2D rect/line/circle slice. 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.
| … | 4 unchanged lines hidden | ||
| 5 | 5 | import ir "../render_ir" | |
| 6 | 6 | import "core:testing" | |
| 7 | 7 | ||
| 8 | - | mock_shader_desc: bk.Shader_Module_Desc | |
| 9 | - | mock_planned_begin_pass_count: int | |
| 10 | - | mock_planned_end_pass_count: int | |
| 11 | - | mock_planned_draw_count: int | |
| 12 | - | mock_planned_vertex_count: u32 | |
| 13 | - | mock_planned_instance_count: u32 | |
| 14 | - | mock_planned_bound_vertex_buffer: bk.Buffer_Handle | |
| 15 | - | mock_planned_bound_index_buffer: bk.Buffer_Handle | |
| 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 | |
| 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 | - | ||
| 30 | - | mock_create_shader_module :: proc(desc: bk.Shader_Module_Desc) -> (bk.Shader_Handle, bool) { | |
| 31 | - | mock_shader_desc = desc | |
| 32 | - | return bk.Shader_Handle(77), true | |
| 33 | - | } | |
| 34 | - | ||
| 35 | - | mock_destroy_shader :: proc(handle: bk.Shader_Handle) {} | |
| 36 | - | ||
| 37 | - | mock_planned_begin_render_pass :: proc(ctx: bk.Frame_Context, desc: bk.Render_Pass_Begin_Desc) { | |
| 38 | - | mock_planned_begin_pass_count += 1 | |
| 39 | - | mock_planned_trace_add("begin_render_pass") | |
| 40 | - | } | |
| 41 | - | ||
| 42 | - | mock_planned_begin_default_pass :: proc(ctx: bk.Frame_Context, clear_color: [4]f32) { | |
| 43 | - | mock_planned_begin_pass_count += 1 | |
| 44 | - | mock_planned_trace_add("begin_default_pass") | |
| 45 | - | } | |
| 46 | - | ||
| 47 | - | mock_planned_end_render_pass :: proc(ctx: bk.Frame_Context) { | |
| 48 | - | mock_planned_end_pass_count += 1 | |
| 49 | - | mock_planned_trace_add("end_render_pass") | |
| 50 | - | } | |
| 51 | - | ||
| 52 | - | mock_planned_set_viewport :: proc(ctx: bk.Frame_Context, x, y, w, h: f32) { | |
| 53 | - | mock_planned_trace_add("set_viewport") | |
| 54 | - | } | |
| 55 | - | ||
| 56 | - | mock_planned_set_scissor :: proc(ctx: bk.Frame_Context, x, y: i32, w, h: u32) { | |
| 57 | - | mock_planned_trace_add("set_scissor") | |
| 58 | - | } | |
| 59 | - | ||
| 60 | - | mock_planned_get_extent :: proc() -> bk.Extent { | |
| 61 | - | return {width = 640, height = 480} | |
| 62 | - | } | |
| 63 | - | ||
| 64 | - | mock_planned_create_graphics_pipeline :: proc( | |
| 65 | - | desc: bk.Pipeline_Desc, | |
| 66 | - | ) -> ( | |
| 67 | - | bk.Pipeline_Handle, | |
| 68 | - | bool, | |
| 69 | - | ) { | |
| 70 | - | return bk.Pipeline_Handle(31), true | |
| 71 | - | } | |
| 72 | - | ||
| 73 | - | mock_planned_destroy_graphics_pipeline :: proc(handle: bk.Pipeline_Handle) {} | |
| 74 | - | ||
| 75 | - | mock_planned_bind_graphics_pipeline :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) { | |
| 76 | - | mock_planned_bound_pipeline = handle | |
| 77 | - | mock_planned_trace_add("bind_pipeline") | |
| 78 | - | } | |
| 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 | - | ||
| 101 | - | mock_planned_bind_vertex_buffer :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) { | |
| 102 | - | mock_planned_bound_vertex_buffer = handle | |
| 103 | - | mock_planned_trace_add("bind_vertex_buffer") | |
| 104 | - | } | |
| 105 | - | ||
| 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 | - | } | |
| 110 | - | ||
| 111 | - | mock_planned_draw :: proc( | |
| 112 | - | ctx: bk.Frame_Context, | |
| 113 | - | vertex_count, instance_count: u32, | |
| 114 | - | first_vertex: u32, | |
| 115 | - | first_instance: u32, | |
| 116 | - | ) { | |
| 117 | - | mock_planned_draw_count += 1 | |
| 118 | - | mock_planned_vertex_count = vertex_count | |
| 119 | - | mock_planned_instance_count = instance_count | |
| 120 | - | mock_planned_trace_add("draw") | |
| 121 | - | } | |
| 122 | - | ||
| 123 | - | mock_planned_draw_indexed :: proc( | |
| 124 | - | ctx: bk.Frame_Context, | |
| 125 | - | index_count, instance_count: u32, | |
| 126 | - | first_index: u32, | |
| 127 | - | vertex_offset: i32, | |
| 128 | - | first_instance: u32, | |
| 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 | - | } | |
| 135 | - | ||
| 136 | - | mock_planned_destroy_buffer :: proc(handle: bk.Buffer_Handle) {} | |
| 137 | - | ||
| 138 | - | mock_planned_destroy_render_pass :: proc(handle: bk.Render_Pass_Handle) {} | |
| 139 | - | ||
| 140 | - | mock_planned_destroy_framebuffer :: proc(handle: bk.Framebuffer_Handle) {} | |
| 141 | - | ||
| 142 | - | mock_planned_backend :: proc() -> bk.Backend { | |
| 143 | - | return { | |
| 144 | - | begin_render_pass = mock_planned_begin_render_pass, | |
| 145 | - | begin_default_pass = mock_planned_begin_default_pass, | |
| 146 | - | end_render_pass = mock_planned_end_render_pass, | |
| 147 | - | set_viewport = mock_planned_set_viewport, | |
| 148 | - | set_scissor = mock_planned_set_scissor, | |
| 149 | - | get_extent = mock_planned_get_extent, | |
| 150 | - | create_graphics_pipeline = mock_planned_create_graphics_pipeline, | |
| 151 | - | destroy_graphics_pipeline = mock_planned_destroy_graphics_pipeline, | |
| 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, | |
| 155 | - | bind_vertex_buffer = mock_planned_bind_vertex_buffer, | |
| 156 | - | bind_index_buffer = mock_planned_bind_index_buffer, | |
| 157 | - | draw = mock_planned_draw, | |
| 158 | - | draw_indexed = mock_planned_draw_indexed, | |
| 159 | - | destroy_buffer = mock_planned_destroy_buffer, | |
| 160 | - | destroy_render_pass = mock_planned_destroy_render_pass, | |
| 161 | - | destroy_framebuffer = mock_planned_destroy_framebuffer, | |
| 162 | - | destroy_shader = mock_destroy_shader, | |
| 163 | - | } | |
| 164 | - | } | |
| 165 | - | ||
| 166 | - | reset_planned_mock :: proc() { | |
| 167 | - | mock_planned_begin_pass_count = 0 | |
| 168 | - | mock_planned_end_pass_count = 0 | |
| 169 | - | mock_planned_draw_count = 0 | |
| 170 | - | mock_planned_vertex_count = 0 | |
| 171 | - | mock_planned_instance_count = 0 | |
| 172 | - | mock_planned_bound_vertex_buffer = bk.NULL_BUFFER | |
| 173 | - | mock_planned_bound_index_buffer = bk.NULL_BUFFER | |
| 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 | |
| 179 | - | } | |
| 180 | - | ||
| 181 | 8 | mock_imported_draw_resolver :: proc( | |
| 182 | 9 | user_data: rawptr, | |
| 183 | 10 | frame: ^ir.Frame_IR, | |
| … | 286 unchanged lines hidden | ||
| 470 | 297 | `, | |
| 471 | 298 | ) | |
| 472 | 299 | ||
| 473 | - | backend := bk.Backend { | |
| 474 | - | create_shader_module = mock_create_shader_module, | |
| 475 | - | destroy_shader = mock_destroy_shader, | |
| 476 | - | } | |
| 300 | + | rec := recording_backend_init() | |
| 301 | + | defer recording_backend_destroy(&rec) | |
| 302 | + | backend := recording_backend_use(&rec) | |
| 477 | 303 | state := compiler.init_execution_state(&backend) | |
| 478 | 304 | defer compiler.destroy_execution_state(&state) | |
| 479 | 305 | ||
| … | 1 unchanged lines hidden | ||
| 481 | 307 | module, module_ok := compiler.prepared_shader(&state, shader_handle) | |
| 482 | 308 | testing.expect(t, module_ok) | |
| 483 | 309 | testing.expect_value(t, module, bk.Shader_Handle(77)) | |
| 484 | - | testing.expect_value(t, mock_shader_desc.stage, bk.Shader_Stage.Compute) | |
| 485 | - | testing.expect_value(t, mock_shader_desc.format, bk.REQUIRED_SHADER_FORMAT) | |
| 486 | - | testing.expect_value(t, mock_shader_desc.name, "compute.comp") | |
| 487 | - | testing.expect(t, len(mock_shader_desc.data) > 0) | |
| 310 | + | testing.expect_value(t, rec.shader_desc.stage, bk.Shader_Stage.Compute) | |
| 311 | + | testing.expect_value(t, rec.shader_desc.format, bk.REQUIRED_SHADER_FORMAT) | |
| 312 | + | testing.expect_value(t, rec.shader_desc.name, "compute.comp") | |
| 313 | + | testing.expect(t, len(rec.shader_desc.data) > 0) | |
| 488 | 314 | } | |
| 489 | 315 | ||
| 490 | 316 | @(test) | |
| 491 | 317 | test_compiler_executes_planned_vertex_stream_draw :: proc(t: ^testing.T) { | |
| 492 | - | reset_planned_mock() | |
| 318 | + | rec := recording_backend_init() | |
| 319 | + | defer recording_backend_destroy(&rec) | |
| 493 | 320 | frame := ir.init_frame_ir() | |
| 494 | 321 | defer ir.destroy_frame_ir(&frame) | |
| 495 | 322 | ||
| … | 12 unchanged lines hidden | ||
| 508 | 335 | } | |
| 509 | 336 | _ = ir.add_draw_packet(&frame, pass, pipeline, .Vertex_Stream, packet, instance_count = 2) | |
| 510 | 337 | ||
| 511 | - | backend := mock_planned_backend() | |
| 338 | + | backend := recording_backend_use(&rec) | |
| 512 | 339 | state := compiler.init_execution_state(&backend) | |
| 513 | 340 | defer compiler.destroy_execution_state_views(&state) | |
| 514 | 341 | append( | |
| … | 34 unchanged lines hidden | ||
| 549 | 376 | ) | |
| 550 | 377 | ||
| 551 | 378 | testing.expect(t, compiler.execute_prepared_planned_commands(&state, &frame, {}, {0, 0, 0, 1})) | |
| 552 | - | testing.expect_value(t, mock_planned_begin_pass_count, 1) | |
| 553 | - | testing.expect_value(t, mock_planned_end_pass_count, 1) | |
| 554 | - | testing.expect_value(t, mock_planned_draw_count, 1) | |
| 555 | - | testing.expect_value(t, mock_planned_vertex_count, u32(6)) | |
| 556 | - | testing.expect_value(t, mock_planned_instance_count, u32(2)) | |
| 557 | - | testing.expect_value(t, mock_planned_bound_vertex_buffer, bk.Buffer_Handle(12)) | |
| 558 | - | testing.expect_value(t, mock_planned_bound_pipeline, bk.Pipeline_Handle(31)) | |
| 379 | + | testing.expect_value(t, rec.begin_pass_count, 1) | |
| 380 | + | testing.expect_value(t, rec.end_pass_count, 1) | |
| 381 | + | testing.expect_value(t, rec.draw_count, 1) | |
| 382 | + | testing.expect_value(t, rec.vertex_count, u32(6)) | |
| 383 | + | testing.expect_value(t, rec.instance_count, u32(2)) | |
| 384 | + | testing.expect_value(t, rec.bound_vertex_buffer, bk.Buffer_Handle(12)) | |
| 385 | + | testing.expect_value(t, rec.bound_pipeline, bk.Pipeline_Handle(31)) | |
| 559 | 386 | ||
| 560 | - | reset_planned_mock() | |
| 387 | + | recording_backend_reset(&rec) | |
| 561 | 388 | imported_frame := ir.init_frame_ir() | |
| 562 | 389 | defer ir.destroy_frame_ir(&imported_frame) | |
| 563 | 390 | ||
| … | 30 unchanged lines hidden | ||
| 594 | 421 | {0, 0, 0, 1}, | |
| 595 | 422 | ), | |
| 596 | 423 | ) | |
| 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)) | |
| 424 | + | testing.expect_value(t, rec.draw_count, 1) | |
| 425 | + | testing.expect_value(t, rec.indexed_count, u32(36)) | |
| 426 | + | testing.expect_value(t, rec.bound_pipeline, bk.Pipeline_Handle(91)) | |
| 427 | + | testing.expect_value(t, rec.bound_descriptor, bk.Descriptor_Handle(92)) | |
| 428 | + | testing.expect_value(t, rec.bound_vertex_buffer, bk.Buffer_Handle(93)) | |
| 429 | + | testing.expect_value(t, rec.bound_index_buffer, bk.Buffer_Handle(94)) | |
| 430 | + | testing.expect_value(t, rec.push_constant_size, u32(16)) | |
| 604 | 431 | ||
| 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) | |
| 432 | + | expected := [?]Recording_Call_Kind { | |
| 433 | + | .Begin_Default_Pass, | |
| 434 | + | .Set_Viewport, | |
| 435 | + | .Set_Scissor, | |
| 436 | + | .Set_Scissor, | |
| 437 | + | .Bind_Pipeline, | |
| 438 | + | .Bind_Descriptor, | |
| 439 | + | .Push_Constants, | |
| 440 | + | .Bind_Vertex_Buffer, | |
| 441 | + | .Bind_Index_Buffer, | |
| 442 | + | .Draw_Indexed, | |
| 443 | + | .End_Render_Pass, | |
| 620 | 444 | } | |
| 445 | + | recording_expect_kinds(t, &rec, expected[:]) | |
| 621 | 446 | } | |
Diff hidden because this file has more than 800 lines.
| 1 | + | package gpu_tests | |
| 2 | + | ||
| 3 | + | import bk "../backend" | |
| 4 | + | import compiler "../compiler" | |
| 5 | + | import "core:mem" | |
| 6 | + | import "core:testing" | |
| 7 | + | ||
| 8 | + | Recording_Call_Kind :: enum { | |
| 9 | + | Begin_Frame, | |
| 10 | + | End_Frame, | |
| 11 | + | Begin_Render_Pass, | |
| 12 | + | Begin_Default_Pass, | |
| 13 | + | End_Render_Pass, | |
| 14 | + | Set_Viewport, | |
| 15 | + | Set_Scissor, | |
| 16 | + | Create_Buffer, | |
| 17 | + | Destroy_Buffer, | |
| 18 | + | Create_Graphics_Pipeline, | |
| 19 | + | Destroy_Graphics_Pipeline, | |
| 20 | + | Bind_Pipeline, | |
| 21 | + | Bind_Descriptor, | |
| 22 | + | Push_Constants, | |
| 23 | + | Bind_Vertex_Buffer, | |
| 24 | + | Bind_Index_Buffer, | |
| 25 | + | Draw, | |
| 26 | + | Draw_Indexed, | |
| 27 | + | Create_Shader, | |
| 28 | + | Destroy_Shader, | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | Recording_Call :: struct { | |
| 32 | + | kind: Recording_Call_Kind, | |
| 33 | + | ||
| 34 | + | frame_index: u32, | |
| 35 | + | ||
| 36 | + | x_i: i32, | |
| 37 | + | y_i: i32, | |
| 38 | + | width_u: u32, | |
| 39 | + | height_u: u32, | |
| 40 | + | x_f: f32, | |
| 41 | + | y_f: f32, | |
| 42 | + | width_f: f32, | |
| 43 | + | height_f: f32, | |
| 44 | + | ||
| 45 | + | pipeline: bk.Pipeline_Handle, | |
| 46 | + | descriptor: bk.Descriptor_Handle, | |
| 47 | + | descriptor_index: u32, | |
| 48 | + | buffer: bk.Buffer_Handle, | |
| 49 | + | ||
| 50 | + | push_constant_size: u32, | |
| 51 | + | push_constant_bytes: [compiler.IMPORTED_PUSH_CONSTANT_CAP]u8, | |
| 52 | + | ||
| 53 | + | vertex_count: u32, | |
| 54 | + | index_count: u32, | |
| 55 | + | instance_count: u32, | |
| 56 | + | first_vertex: u32, | |
| 57 | + | first_index: u32, | |
| 58 | + | vertex_offset: i32, | |
| 59 | + | first_instance: u32, | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | Recording_Buffer :: struct { | |
| 63 | + | handle: bk.Buffer_Handle, | |
| 64 | + | data: []u8, | |
| 65 | + | mapped: bool, | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | Recording_Backend_State :: struct { | |
| 69 | + | calls: [dynamic]Recording_Call, | |
| 70 | + | buffers: [dynamic]Recording_Buffer, | |
| 71 | + | ||
| 72 | + | shader_desc: bk.Shader_Module_Desc, | |
| 73 | + | extent: bk.Extent, | |
| 74 | + | ||
| 75 | + | next_buffer: u64, | |
| 76 | + | next_pipeline: u64, | |
| 77 | + | next_shader: u64, | |
| 78 | + | current_frame_index: u32, | |
| 79 | + | next_frame_index: u32, | |
| 80 | + | ||
| 81 | + | begin_pass_count: int, | |
| 82 | + | end_pass_count: int, | |
| 83 | + | draw_count: int, | |
| 84 | + | vertex_count: u32, | |
| 85 | + | indexed_count: u32, | |
| 86 | + | instance_count: u32, | |
| 87 | + | bound_vertex_buffer: bk.Buffer_Handle, | |
| 88 | + | bound_index_buffer: bk.Buffer_Handle, | |
| 89 | + | bound_pipeline: bk.Pipeline_Handle, | |
| 90 | + | bound_descriptor: bk.Descriptor_Handle, | |
| 91 | + | push_constant_size: u32, | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | @(thread_local) recording_active: ^Recording_Backend_State | |
| 95 | + | ||
| 96 | + | recording_backend_init :: proc() -> Recording_Backend_State { | |
| 97 | + | return { | |
| 98 | + | calls = make([dynamic]Recording_Call, 0, 128), | |
| 99 | + | buffers = make([dynamic]Recording_Buffer, 0, 16), | |
| 100 | + | extent = {width = 640, height = 480}, | |
| 101 | + | next_buffer = 12, | |
| 102 | + | next_pipeline = 31, | |
| 103 | + | next_shader = 77, | |
| 104 | + | } | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | recording_backend_destroy :: proc(rec: ^Recording_Backend_State) { | |
| 108 | + | if rec == nil { | |
| 109 | + | return | |
| 110 | + | } | |
| 111 | + | for &buffer in rec.buffers { | |
| 112 | + | if buffer.data != nil { | |
| 113 | + | delete(buffer.data) | |
| 114 | + | } | |
| 115 | + | } | |
| 116 | + | delete(rec.calls) | |
| 117 | + | delete(rec.buffers) | |
| 118 | + | rec^ = {} | |
| 119 | + | if recording_active == rec { | |
| 120 | + | recording_active = nil | |
| 121 | + | } | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | recording_backend_reset :: proc(rec: ^Recording_Backend_State) { | |
| 125 | + | if rec == nil { | |
| 126 | + | return | |
| 127 | + | } | |
| 128 | + | clear(&rec.calls) | |
| 129 | + | rec.begin_pass_count = 0 | |
| 130 | + | rec.end_pass_count = 0 | |
| 131 | + | rec.draw_count = 0 | |
| 132 | + | rec.vertex_count = 0 | |
| 133 | + | rec.indexed_count = 0 | |
| 134 | + | rec.instance_count = 0 | |
| 135 | + | rec.bound_vertex_buffer = bk.NULL_BUFFER | |
| 136 | + | rec.bound_index_buffer = bk.NULL_BUFFER | |
| 137 | + | rec.bound_pipeline = bk.NULL_PIPELINE | |
| 138 | + | rec.bound_descriptor = bk.NULL_DESCRIPTOR | |
| 139 | + | rec.push_constant_size = 0 | |
| 140 | + | rec.shader_desc = {} | |
| 141 | + | } | |
| 142 | + | ||
| 143 | + | recording_backend_use :: proc(rec: ^Recording_Backend_State) -> bk.Backend { | |
| 144 | + | recording_active = rec | |
| 145 | + | return { | |
| 146 | + | begin_frame = recording_begin_frame, | |
| 147 | + | end_frame = recording_end_frame, | |
| 148 | + | get_extent = recording_get_extent, | |
| 149 | + | current_frame_index = recording_current_frame_index, | |
| 150 | + | ||
| 151 | + | begin_render_pass = recording_begin_render_pass, | |
| 152 | + | begin_default_pass = recording_begin_default_pass, | |
| 153 | + | end_render_pass = recording_end_render_pass, | |
| 154 | + | set_viewport = recording_set_viewport, | |
| 155 | + | set_scissor = recording_set_scissor, | |
| 156 | + | ||
| 157 | + | create_buffer = recording_create_buffer, | |
| 158 | + | create_buffer_staged = recording_create_buffer_staged, | |
| 159 | + | destroy_buffer = recording_destroy_buffer, | |
| 160 | + | map_buffer = recording_map_buffer, | |
| 161 | + | unmap_buffer = recording_unmap_buffer, | |
| 162 | + | get_buffer_mapped = recording_get_buffer_mapped, | |
| 163 | + | ||
| 164 | + | create_graphics_pipeline = recording_create_graphics_pipeline, | |
| 165 | + | destroy_graphics_pipeline = recording_destroy_graphics_pipeline, | |
| 166 | + | bind_graphics_pipeline = recording_bind_graphics_pipeline, | |
| 167 | + | push_constants = recording_push_constants, | |
| 168 | + | bind_descriptor_set = recording_bind_descriptor_set, | |
| 169 | + | bind_vertex_buffer = recording_bind_vertex_buffer, | |
| 170 | + | bind_index_buffer = recording_bind_index_buffer, | |
| 171 | + | draw = recording_draw, | |
| 172 | + | draw_indexed = recording_draw_indexed, | |
| 173 | + | ||
| 174 | + | create_shader_module = recording_create_shader_module, | |
| 175 | + | destroy_shader = recording_destroy_shader, | |
| 176 | + | ||
| 177 | + | destroy_render_pass = recording_destroy_render_pass, | |
| 178 | + | destroy_framebuffer = recording_destroy_framebuffer, | |
| 179 | + | } | |
| 180 | + | } | |
| 181 | + | ||
| 182 | + | recording_expect_kinds :: proc( | |
| 183 | + | t: ^testing.T, | |
| 184 | + | rec: ^Recording_Backend_State, | |
| 185 | + | expected: []Recording_Call_Kind, | |
| 186 | + | ) { | |
| 187 | + | testing.expect_value(t, len(rec.calls), len(expected)) | |
| 188 | + | if len(rec.calls) != len(expected) { | |
| 189 | + | return | |
| 190 | + | } | |
| 191 | + | for kind, i in expected { | |
| 192 | + | testing.expect_value(t, rec.calls[i].kind, kind) | |
| 193 | + | } | |
| 194 | + | } | |
| 195 | + | ||
| 196 | + | recording_record :: proc(call: Recording_Call) { | |
| 197 | + | if recording_active == nil { | |
| 198 | + | return | |
| 199 | + | } | |
| 200 | + | append(&recording_active.calls, call) | |
| 201 | + | } | |
| 202 | + | ||
| 203 | + | recording_begin_frame :: proc(clear_color: [4]f32) -> (bk.Frame_Context, bool) { | |
| 204 | + | _ = clear_color | |
| 205 | + | rec := recording_active | |
| 206 | + | if rec == nil { | |
| 207 | + | return {}, false | |
| 208 | + | } | |
| 209 | + | rec.current_frame_index = rec.next_frame_index | |
| 210 | + | ctx := bk.Frame_Context {frame_index = rec.current_frame_index} | |
| 211 | + | recording_record({kind = .Begin_Frame, frame_index = ctx.frame_index}) | |
| 212 | + | return ctx, true | |
| 213 | + | } | |
| 214 | + | ||
| 215 | + | recording_end_frame :: proc(ctx: bk.Frame_Context) -> bool { | |
| 216 | + | rec := recording_active | |
| 217 | + | if rec != nil { | |
| 218 | + | rec.next_frame_index = (ctx.frame_index + 1) % bk.MAX_FRAMES_IN_FLIGHT | |
| 219 | + | } | |
| 220 | + | recording_record({kind = .End_Frame, frame_index = ctx.frame_index}) | |
| 221 | + | return false | |
| 222 | + | } | |
| 223 | + | ||
| 224 | + | recording_current_frame_index :: proc() -> u32 { | |
| 225 | + | if recording_active == nil { | |
| 226 | + | return 0 | |
| 227 | + | } | |
| 228 | + | return recording_active.current_frame_index | |
| 229 | + | } | |
| 230 | + | ||
| 231 | + | recording_begin_render_pass :: proc(ctx: bk.Frame_Context, desc: bk.Render_Pass_Begin_Desc) { | |
| 232 | + | _ = desc | |
| 233 | + | if recording_active != nil { | |
| 234 | + | recording_active.begin_pass_count += 1 | |
| 235 | + | } | |
| 236 | + | recording_record({kind = .Begin_Render_Pass, frame_index = ctx.frame_index}) | |
| 237 | + | } | |
| 238 | + | ||
| 239 | + | recording_begin_default_pass :: proc(ctx: bk.Frame_Context, clear_color: [4]f32) { | |
| 240 | + | _ = clear_color | |
| 241 | + | if recording_active != nil { | |
| 242 | + | recording_active.begin_pass_count += 1 | |
| 243 | + | } | |
| 244 | + | recording_record({kind = .Begin_Default_Pass, frame_index = ctx.frame_index}) | |
| 245 | + | } | |
| 246 | + | ||
| 247 | + | recording_end_render_pass :: proc(ctx: bk.Frame_Context) { | |
| 248 | + | if recording_active != nil { | |
| 249 | + | recording_active.end_pass_count += 1 | |
| 250 | + | } | |
| 251 | + | recording_record({kind = .End_Render_Pass, frame_index = ctx.frame_index}) | |
| 252 | + | } | |
| 253 | + | ||
| 254 | + | recording_set_viewport :: proc(ctx: bk.Frame_Context, x, y, w, h: f32) { | |
| 255 | + | recording_record({kind = .Set_Viewport, frame_index = ctx.frame_index, x_f = x, y_f = y, width_f = w, height_f = h}) | |
| 256 | + | } | |
| 257 | + | ||
| 258 | + | recording_set_scissor :: proc(ctx: bk.Frame_Context, x, y: i32, w, h: u32) { | |
| 259 | + | recording_record({kind = .Set_Scissor, frame_index = ctx.frame_index, x_i = x, y_i = y, width_u = w, height_u = h}) | |
| 260 | + | } | |
| 261 | + | ||
| 262 | + | recording_get_extent :: proc() -> bk.Extent { | |
| 263 | + | if recording_active == nil { | |
| 264 | + | return {width = 640, height = 480} | |
| 265 | + | } | |
| 266 | + | return recording_active.extent | |
| 267 | + | } | |
| 268 | + | ||
| 269 | + | recording_find_buffer :: proc( | |
| 270 | + | rec: ^Recording_Backend_State, | |
| 271 | + | handle: bk.Buffer_Handle, | |
| 272 | + | ) -> (^Recording_Buffer, bool) { | |
| 273 | + | if rec == nil { | |
| 274 | + | return nil, false | |
| 275 | + | } | |
| 276 | + | for &buffer in rec.buffers { | |
| 277 | + | if buffer.handle == handle { | |
| 278 | + | return &buffer, true | |
| 279 | + | } | |
| 280 | + | } | |
| 281 | + | return nil, false | |
| 282 | + | } | |
| 283 | + | ||
| 284 | + | recording_create_buffer :: proc(desc: bk.Buffer_Desc) -> (bk.Buffer_Handle, bool) { | |
| 285 | + | rec := recording_active | |
| 286 | + | if rec == nil { | |
| 287 | + | return bk.NULL_BUFFER, false | |
| 288 | + | } | |
| 289 | + | size := int(desc.size) | |
| 290 | + | if size <= 0 { | |
| 291 | + | size = 1 | |
| 292 | + | } | |
| 293 | + | handle := bk.Buffer_Handle(rec.next_buffer) | |
| 294 | + | rec.next_buffer += 1 | |
| 295 | + | append(&rec.buffers, Recording_Buffer {handle = handle, data = make([]u8, size)}) | |
| 296 | + | recording_record({kind = .Create_Buffer, buffer = handle}) | |
| 297 | + | return handle, true | |
| 298 | + | } | |
| 299 | + | ||
| 300 | + | recording_create_buffer_staged :: proc( | |
| 301 | + | data: rawptr, | |
| 302 | + | size: int, | |
| 303 | + | usage: bk.Buffer_Usage_Flags, | |
| 304 | + | ) -> ( | |
| 305 | + | bk.Buffer_Handle, | |
| 306 | + | bool, | |
| 307 | + | ) { | |
| 308 | + | _ = usage | |
| 309 | + | handle, ok := recording_create_buffer({size = u64(size)}) | |
| 310 | + | if ok && data != nil { | |
| 311 | + | if buffer, buffer_ok := recording_find_buffer(recording_active, handle); buffer_ok { | |
| 312 | + | mem.copy(raw_data(buffer.data), data, size) | |
| 313 | + | } | |
| 314 | + | } | |
| 315 | + | return handle, ok | |
| 316 | + | } | |
| 317 | + | ||
| 318 | + | recording_destroy_buffer :: proc(handle: bk.Buffer_Handle) { | |
| 319 | + | rec := recording_active | |
| 320 | + | if rec != nil { | |
| 321 | + | for &buffer, i in rec.buffers { | |
| 322 | + | if buffer.handle == handle { | |
| 323 | + | if buffer.data != nil { | |
| 324 | + | delete(buffer.data) | |
| 325 | + | } | |
| 326 | + | unordered_remove(&rec.buffers, i) | |
| 327 | + | break | |
| 328 | + | } | |
| 329 | + | } | |
| 330 | + | } | |
| 331 | + | recording_record({kind = .Destroy_Buffer, buffer = handle}) | |
| 332 | + | } | |
| 333 | + | ||
| 334 | + | recording_map_buffer :: proc(handle: bk.Buffer_Handle) -> rawptr { | |
| 335 | + | buffer, ok := recording_find_buffer(recording_active, handle) | |
| 336 | + | if !ok { | |
| 337 | + | return nil | |
| 338 | + | } | |
| 339 | + | buffer.mapped = true | |
| 340 | + | return raw_data(buffer.data) | |
| 341 | + | } | |
| 342 | + | ||
| 343 | + | recording_unmap_buffer :: proc(handle: bk.Buffer_Handle) { | |
| 344 | + | if buffer, ok := recording_find_buffer(recording_active, handle); ok { | |
| 345 | + | buffer.mapped = false | |
| 346 | + | } | |
| 347 | + | } | |
| 348 | + | ||
| 349 | + | recording_get_buffer_mapped :: proc(handle: bk.Buffer_Handle) -> rawptr { | |
| 350 | + | buffer, ok := recording_find_buffer(recording_active, handle) | |
| 351 | + | if !ok || !buffer.mapped { | |
| 352 | + | return nil | |
| 353 | + | } | |
| 354 | + | return raw_data(buffer.data) | |
| 355 | + | } | |
| 356 | + | ||
| 357 | + | recording_create_graphics_pipeline :: proc(desc: bk.Pipeline_Desc) -> (bk.Pipeline_Handle, bool) { | |
| 358 | + | _ = desc | |
| 359 | + | rec := recording_active | |
| 360 | + | if rec == nil { | |
| 361 | + | return bk.NULL_PIPELINE, false | |
| 362 | + | } | |
| 363 | + | handle := bk.Pipeline_Handle(rec.next_pipeline) | |
| 364 | + | rec.next_pipeline += 1 | |
| 365 | + | recording_record({kind = .Create_Graphics_Pipeline, pipeline = handle}) | |
| 366 | + | return handle, true | |
| 367 | + | } | |
| 368 | + | ||
| 369 | + | recording_destroy_graphics_pipeline :: proc(handle: bk.Pipeline_Handle) { | |
| 370 | + | recording_record({kind = .Destroy_Graphics_Pipeline, pipeline = handle}) | |
| 371 | + | } | |
| 372 | + | ||
| 373 | + | recording_bind_graphics_pipeline :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) { | |
| 374 | + | if recording_active != nil { | |
| 375 | + | recording_active.bound_pipeline = handle | |
| 376 | + | } | |
| 377 | + | recording_record({kind = .Bind_Pipeline, frame_index = ctx.frame_index, pipeline = handle}) | |
| 378 | + | } | |
| 379 | + | ||
| 380 | + | recording_bind_descriptor_set :: proc( | |
| 381 | + | ctx: bk.Frame_Context, | |
| 382 | + | pipeline: bk.Pipeline_Handle, | |
| 383 | + | set: bk.Descriptor_Handle, | |
| 384 | + | index: u32, | |
| 385 | + | ) { | |
| 386 | + | if recording_active != nil { | |
| 387 | + | recording_active.bound_descriptor = set | |
| 388 | + | } | |
| 389 | + | recording_record({ | |
| 390 | + | kind = .Bind_Descriptor, | |
| 391 | + | frame_index = ctx.frame_index, | |
| 392 | + | pipeline = pipeline, | |
| 393 | + | descriptor = set, | |
| 394 | + | descriptor_index = index, | |
| 395 | + | }) | |
| 396 | + | } | |
| 397 | + | ||
| 398 | + | recording_push_constants :: proc( | |
| 399 | + | ctx: bk.Frame_Context, | |
| 400 | + | pipeline: bk.Pipeline_Handle, | |
| 401 | + | stages: bk.Shader_Stage_Flags, | |
| 402 | + | offset, size: u32, | |
| 403 | + | data: rawptr, | |
| 404 | + | ) { | |
| 405 | + | _ = stages | |
| 406 | + | _ = offset | |
| 407 | + | call := Recording_Call { | |
| 408 | + | kind = .Push_Constants, | |
| 409 | + | frame_index = ctx.frame_index, | |
| 410 | + | pipeline = pipeline, | |
| 411 | + | push_constant_size = size, | |
| 412 | + | } | |
| 413 | + | if data != nil && size <= compiler.IMPORTED_PUSH_CONSTANT_CAP { | |
| 414 | + | mem.copy(&call.push_constant_bytes[0], data, int(size)) | |
| 415 | + | } | |
| 416 | + | if recording_active != nil { | |
| 417 | + | recording_active.push_constant_size = size | |
| 418 | + | } | |
| 419 | + | recording_record(call) | |
| 420 | + | } | |
| 421 | + | ||
| 422 | + | recording_bind_vertex_buffer :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) { | |
| 423 | + | if recording_active != nil { | |
| 424 | + | recording_active.bound_vertex_buffer = handle | |
| 425 | + | } | |
| 426 | + | recording_record({kind = .Bind_Vertex_Buffer, frame_index = ctx.frame_index, buffer = handle}) | |
| 427 | + | } | |
| 428 | + | ||
| 429 | + | recording_bind_index_buffer :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) { | |
| 430 | + | if recording_active != nil { | |
| 431 | + | recording_active.bound_index_buffer = handle | |
| 432 | + | } | |
| 433 | + | recording_record({kind = .Bind_Index_Buffer, frame_index = ctx.frame_index, buffer = handle}) | |
| 434 | + | } | |
| 435 | + | ||
| 436 | + | recording_draw :: proc( | |
| 437 | + | ctx: bk.Frame_Context, | |
| 438 | + | vertex_count, instance_count: u32, | |
| 439 | + | first_vertex: u32, | |
| 440 | + | first_instance: u32, | |
| 441 | + | ) { | |
| 442 | + | if recording_active != nil { | |
| 443 | + | recording_active.draw_count += 1 | |
| 444 | + | recording_active.vertex_count = vertex_count | |
| 445 | + | recording_active.instance_count = instance_count | |
| 446 | + | } | |
| 447 | + | recording_record({ | |
| 448 | + | kind = .Draw, | |
| 449 | + | frame_index = ctx.frame_index, | |
| 450 | + | vertex_count = vertex_count, | |
| 451 | + | instance_count = instance_count, | |
| 452 | + | first_vertex = first_vertex, | |
| 453 | + | first_instance = first_instance, | |
| 454 | + | }) | |
| 455 | + | } | |
| 456 | + | ||
| 457 | + | recording_draw_indexed :: proc( | |
| 458 | + | ctx: bk.Frame_Context, | |
| 459 | + | index_count, instance_count: u32, | |
| 460 | + | first_index: u32, | |
| 461 | + | vertex_offset: i32, | |
| 462 | + | first_instance: u32, | |
| 463 | + | ) { | |
| 464 | + | if recording_active != nil { | |
| 465 | + | recording_active.draw_count += 1 | |
| 466 | + | recording_active.indexed_count = index_count | |
| 467 | + | recording_active.instance_count = instance_count | |
| 468 | + | } | |
| 469 | + | recording_record({ | |
| 470 | + | kind = .Draw_Indexed, | |
| 471 | + | frame_index = ctx.frame_index, | |
| 472 | + | index_count = index_count, | |
| 473 | + | instance_count = instance_count, | |
| 474 | + | first_index = first_index, | |
| 475 | + | vertex_offset = vertex_offset, | |
| 476 | + | first_instance = first_instance, | |
| 477 | + | }) | |
| 478 | + | } | |
| 479 | + | ||
| 480 | + | recording_create_shader_module :: proc(desc: bk.Shader_Module_Desc) -> (bk.Shader_Handle, bool) { | |
| 481 | + | rec := recording_active | |
| 482 | + | if rec == nil { | |
| 483 | + | return bk.NULL_SHADER, false | |
| 484 | + | } | |
| 485 | + | rec.shader_desc = desc | |
| 486 | + | handle := bk.Shader_Handle(rec.next_shader) | |
| 487 | + | rec.next_shader += 1 | |
| 488 | + | recording_record({kind = .Create_Shader}) | |
| 489 | + | return handle, true | |
| 490 | + | } | |
| 491 | + | ||
| 492 | + | recording_destroy_shader :: proc(handle: bk.Shader_Handle) { | |
| 493 | + | _ = handle | |
| 494 | + | recording_record({kind = .Destroy_Shader}) | |
| 495 | + | } | |
| 496 | + | ||
| 497 | + | recording_destroy_render_pass :: proc(handle: bk.Render_Pass_Handle) { | |
| 498 | + | _ = handle | |
| 499 | + | } | |
| 500 | + | ||
| 501 | + | recording_destroy_framebuffer :: proc(handle: bk.Framebuffer_Handle) { | |
| 502 | + | _ = handle | |
| 503 | + | } |