Harbor

Changelog f1f7987caba8

pin gpu sync barrier work

@sky · 1 month ago · parent 8dd7394c8d9c
0 added 5 modified 0 deleted
gpu/backend/opengl/gl_ops.odin modified

Diff hidden because this file has more than 800 lines.

gpu/backend/vulkan/vk_ops.odin modified

Diff hidden because this file has more than 800 lines.

gpu/docs/gpu_intent_api.md +4 -4 modified
49 unchanged lines hidden
50 50
51 51 CPU mapping is only valid for host-visible buffers. GPU-local/default buffers are not mappable through `map_buffer` or `get_buffer_mapped`; backends must report that explicitly and return `nil`. Host-visible upload buffers may stay persistently mapped when the backend memory model allows it.
52 52
53 - `compute_barrier` means storage/UAV visibility between backend dispatch/draw work in one frame. It is not a render-target transition substitute. Backends that lack a complete storage descriptor path must report that separately through capabilities or diagnostics instead of pretending the barrier enables unsupported binding.
53 + `compute_barrier(ctx)` is a conservative/global **in-frame** barrier for compute-written storage/UAV data consumed by later backend work in the same frame. Its consumer scope is later compute shader reads/writes, vertex/fragment shader reads, vertex/index fetch, and future indirect command reads. It is intentionally over-synchronized for correctness; it is not a render-target transition substitute, CPU readback fence, cross-frame lifetime rule, or feedback-loop validator. Backends that lack a complete storage descriptor path must report that separately through capabilities or diagnostics instead of pretending the barrier enables unsupported binding.
54 54
55 55 D3D12 runtime fence behavior cannot be executed by Linux CI. D3D12 lifetime and transition work is accepted by static backend checks, pure fence-order tests where available, and explicit Windows runtime verification debt.
56 56
1 unchanged lines hidden
58 58
59 59 | Backend | Compute barrier scope | Sampled target feedback | Runtime proof |
60 60 | --- | --- | --- | --- |
61 - | Vulkan | Conservative compute-write barrier for later shader reads/writes plus vertex/index fetch. Indirect command reads stay Phase 7. | Render pass final layouts come from target usage; descriptor layout is caller-declared. | Static checks now; device smoke gate still needed. |
61 + | Vulkan | Conservative compute-write barrier for later shader reads/writes, vertex/index fetch, and future indirect command reads. Indirect draw execution stays Phase 7. | Render pass final layouts come from target usage; descriptor layout is caller-declared. | Static checks now; device smoke gate still needed. |
62 62 | D3D11 | Immediate context resolves many SRV/RTV/UAV conflicts by auto-unbinding and debug warnings. Manual hazard cleanup needs slot tracking before it is safe. | No explicit image states; render-target/SRV overlap needs future warning-proof validation, not broad unbinds. | Windows runtime deferred. |
63 - | D3D12 | Global UAV barrier only. Storage descriptor binding is still a separate unsupported root-signature gap. | Render/depth targets transition to write at pass begin and to shader-resource lazily when bound as sampled descriptors. | Windows runtime deferred. |
64 - | OpenGL | Storage, texture-fetch, and framebuffer barriers are emitted after compute when requested. Indirect command barriers stay Phase 7. | Feedback-loop validation is a future diagnostics gap; current backend does not track attached texture handles at descriptor bind. | Linux device smoke gate still needed. |
63 + | D3D12 | Global UAV barrier for compute storage visibility. Future indirect execution still needs argument-buffer state handling. | Render/depth targets transition to write at pass begin and to shader-resource lazily when bound as sampled descriptors. | Windows runtime deferred. |
64 + | OpenGL | Storage, texture-fetch, framebuffer, and future command/indirect barriers are emitted after compute when requested. Indirect draw execution stays Phase 7. | Feedback-loop validation is a future diagnostics gap; current backend does not track attached texture handles at descriptor bind. | Linux device smoke gate still needed. |
65 65
66 66 ## Public frame shape
67 67
185 unchanged lines hidden
gpu/tests/compiler_executor_test.odin +35 -0 modified
663 unchanged lines hidden
664 664 }
665 665 recording_expect_kinds(t, &rec, expected[:])
666 666 }
667 +
668 + @(test)
669 + test_recording_backend_preserves_compute_barrier_draw_order :: proc(t: ^testing.T) {
670 + rec := recording_backend_init()
671 + defer recording_backend_destroy(&rec)
672 + backend := recording_backend_use(&rec)
673 +
674 + ctx, ok := backend.begin_frame({0, 0, 0, 1})
675 + testing.expect(t, ok)
676 +
677 + pipeline, pipeline_ok := backend.create_compute_pipeline(bk.Shader_Handle(77), 1, 0)
678 + testing.expect(t, pipeline_ok)
679 + backend.bind_compute_pipeline(ctx, pipeline)
680 + backend.dispatch_compute(ctx, 4, 2, 1)
681 + backend.compute_barrier(ctx)
682 + backend.begin_default_pass(ctx, {0, 0, 0, 1})
683 + backend.draw(ctx, 6, 1, 0, 0)
684 + backend.end_render_pass(ctx)
685 +
686 + expected := [?]Recording_Call_Kind {
687 + .Begin_Frame,
688 + .Create_Compute_Pipeline,
689 + .Bind_Compute_Pipeline,
690 + .Dispatch_Compute,
691 + .Compute_Barrier,
692 + .Begin_Default_Pass,
693 + .Draw,
694 + .End_Render_Pass,
695 + }
696 + recording_expect_kinds(t, &rec, expected[:])
697 + testing.expect_value(t, rec.calls[3].width_u, u32(4))
698 + testing.expect_value(t, rec.calls[3].height_u, u32(2))
699 + testing.expect_value(t, rec.calls[3].vertex_count, u32(1))
700 + testing.expect_value(t, rec.calls[6].vertex_count, u32(6))
701 + }
gpu/tests/recording_backend.odin +56 -0 modified
23 unchanged lines hidden
24 24 Bind_Index_Buffer,
25 25 Draw,
26 26 Draw_Indexed,
27 + Create_Compute_Pipeline,
28 + Destroy_Compute_Pipeline,
29 + Bind_Compute_Pipeline,
30 + Dispatch_Compute,
31 + Compute_Barrier,
27 32 Create_Shader,
28 33 Destroy_Shader,
29 34 }
144 unchanged lines hidden
174 179 bind_index_buffer = recording_bind_index_buffer,
175 180 draw = recording_draw,
176 181 draw_indexed = recording_draw_indexed,
182 + create_compute_pipeline = recording_create_compute_pipeline,
183 + destroy_compute_pipeline = recording_destroy_compute_pipeline,
184 + bind_compute_pipeline = recording_bind_compute_pipeline,
185 + dispatch_compute = recording_dispatch_compute,
186 + compute_barrier = recording_compute_barrier,
177 187
178 188 create_shader_module = recording_create_shader_module,
179 189 destroy_shader = recording_destroy_shader,
309 unchanged lines hidden
489 499 })
490 500 }
491 501
502 + recording_create_compute_pipeline :: proc(
503 + shader: bk.Shader_Handle,
504 + num_buffers: u32,
505 + push_constant_size: u32,
506 + ) -> (
507 + bk.Pipeline_Handle,
508 + bool,
509 + ) {
510 + _ = shader
511 + _ = num_buffers
512 + _ = push_constant_size
513 + rec := recording_active
514 + if rec == nil {
515 + return bk.NULL_PIPELINE, false
516 + }
517 + handle := bk.Pipeline_Handle(rec.next_pipeline)
518 + rec.next_pipeline += 1
519 + recording_record({kind = .Create_Compute_Pipeline, pipeline = handle})
520 + return handle, true
521 + }
522 +
523 + recording_destroy_compute_pipeline :: proc(handle: bk.Pipeline_Handle) {
524 + recording_record({kind = .Destroy_Compute_Pipeline, pipeline = handle})
525 + }
526 +
527 + recording_bind_compute_pipeline :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) {
528 + if recording_active != nil {
529 + recording_active.bound_pipeline = handle
530 + }
531 + recording_record({kind = .Bind_Compute_Pipeline, frame_index = ctx.frame_index, pipeline = handle})
532 + }
533 +
534 + recording_dispatch_compute :: proc(ctx: bk.Frame_Context, groups_x, groups_y, groups_z: u32) {
535 + recording_record({
536 + kind = .Dispatch_Compute,
537 + frame_index = ctx.frame_index,
538 + width_u = groups_x,
539 + height_u = groups_y,
540 + vertex_count = groups_z,
541 + })
542 + }
543 +
544 + recording_compute_barrier :: proc(ctx: bk.Frame_Context) {
545 + recording_record({kind = .Compute_Barrier, frame_index = ctx.frame_index})
546 + }
547 +
492 548 recording_create_shader_module :: proc(desc: bk.Shader_Module_Desc) -> (bk.Shader_Handle, bool) {
493 549 rec := recording_active
494 550 if rec == nil {
21 unchanged lines hidden