Harbor

Changelog 138e04c61af8

pin gpu instance input parity

@sky · 1 month ago · parent 850ceea8c535
0 added 21 modified 0 deleted
gpu/backend/backend.odin +17 -2 modified
280 unchanged lines hidden
281 281
282 282 Vertex_Attribute :: struct {
283 283 location: u32,
284 + binding: u32,
284 285 offset: u32,
285 286 format: Vertex_Format,
286 287 }
5 unchanged lines hidden
292 293 }
293 294
294 295 Vertex_Binding :: struct {
295 - binding: u32,
296 - stride: u32,
296 + binding: u32,
297 + stride: u32,
298 + input_rate: Vertex_Input_Rate,
297 299 }
298 300
301 + Vertex_Input_Rate :: enum {
302 + Vertex,
303 + Instance,
304 + }
305 +
299 306 // --- Vertex layout descriptor ---
300 307
301 308 Vertex_Attrib :: enum u8 {
214 unchanged lines hidden
516 523 unmap_buffer: proc(handle: Buffer_Handle),
517 524 get_buffer_mapped: proc(handle: Buffer_Handle) -> rawptr,
518 525 bind_vertex_buffer: proc(ctx: Frame_Context, handle: Buffer_Handle),
526 + bind_vertex_buffer_slot: proc(
527 + ctx: Frame_Context,
528 + slot: u32,
529 + handle: Buffer_Handle,
530 + offset: u64,
531 + stride: u32,
532 + ),
519 533 bind_index_buffer: proc(ctx: Frame_Context, handle: Buffer_Handle),
520 534
521 535 // --- Textures ---
148 unchanged lines hidden
670 684 b.unmap_buffer != nil &&
671 685 b.get_buffer_mapped != nil &&
672 686 b.bind_vertex_buffer != nil &&
687 + b.bind_vertex_buffer_slot != nil &&
673 688 b.bind_index_buffer != nil &&
674 689 b.create_texture != nil &&
675 690 b.destroy_texture != nil &&
35 unchanged lines hidden
gpu/backend/d3d11/d3d11_backend.odin +1 -0 modified
378 unchanged lines hidden
379 379 unmap_buffer = unmap_buffer_d3d11,
380 380 get_buffer_mapped = get_buffer_mapped_d3d11,
381 381 bind_vertex_buffer = bind_vertex_buffer_d3d11,
382 + bind_vertex_buffer_slot = bind_vertex_buffer_slot_d3d11,
382 383 bind_index_buffer = bind_index_buffer_d3d11,
383 384
384 385 // Textures
212 unchanged lines hidden
gpu/backend/d3d11/d3d11_ops.odin modified

Diff hidden because this file has more than 800 lines.

gpu/backend/d3d12/d3d12_backend.odin modified

Diff hidden because this file has more than 800 lines.

gpu/backend/d3d12/d3d12_ops.odin modified

Diff hidden because this file has more than 800 lines.

gpu/backend/opengl/gl_backend.odin +2 -0 modified
74 unchanged lines hidden
75 75 cull_mode: bk.Cull_Mode,
76 76 front_face: bk.Front_Face,
77 77 vertex_stride: u32,
78 + vertex_strides: [16]u32,
78 79 push_constant_size: u32,
79 80 push_constant_stages: bk.Shader_Stage_Flags,
80 81 is_compute: bool,
116 unchanged lines hidden
197 198 unmap_buffer = unmap_buffer_opengl,
198 199 get_buffer_mapped = get_buffer_mapped_opengl,
199 200 bind_vertex_buffer = bind_vertex_buffer_opengl,
201 + bind_vertex_buffer_slot = bind_vertex_buffer_slot_opengl,
200 202 bind_index_buffer = bind_index_buffer_opengl,
201 203 create_texture = create_texture_opengl,
202 204 destroy_texture = destroy_texture_opengl,
107 unchanged lines hidden
gpu/backend/opengl/gl_ops.odin modified

Diff hidden because this file has more than 800 lines.

gpu/backend/vertex_layout.odin +17 -2 modified
75 unchanged lines hidden
76 76 attributes: [MAX_VERTEX_ATTRIBS]Vertex_Attribute,
77 77 attr_count: int,
78 78 ) {
79 + return vertex_layout_to_pipeline_attrs_for_binding(layout, 0, .Vertex, 0)
80 + }
81 +
82 + vertex_layout_to_pipeline_attrs_for_binding :: proc(
83 + layout: ^Vertex_Layout,
84 + binding_index: u32,
85 + input_rate: Vertex_Input_Rate,
86 + first_location: u32,
87 + ) -> (
88 + binding: Vertex_Binding,
89 + attributes: [MAX_VERTEX_ATTRIBS]Vertex_Attribute,
90 + attr_count: int,
91 + ) {
79 92 binding = Vertex_Binding {
80 - binding = 0,
93 + binding = binding_index,
81 94 stride = layout.stride,
95 + input_rate = input_rate,
82 96 }
83 97 for i in 0 ..< int(layout.count) {
84 98 e := &layout.entries[i]
85 99 attributes[i] = Vertex_Attribute {
86 - location = u32(i),
100 + location = first_location + u32(i),
101 + binding = binding_index,
87 102 offset = e.offset,
88 103 format = e.format,
89 104 }
109 unchanged lines hidden
gpu/backend/vulkan/vk_backend.odin +1 -0 modified
326 unchanged lines hidden
327 327 unmap_buffer = unmap_buffer_vk,
328 328 get_buffer_mapped = get_buffer_mapped_vk,
329 329 bind_vertex_buffer = bind_vertex_buffer_vk,
330 + bind_vertex_buffer_slot = bind_vertex_buffer_slot_vk,
330 331 bind_index_buffer = bind_index_buffer_vk,
331 332
332 333 // Textures
211 unchanged lines hidden
gpu/backend/vulkan/vk_ops.odin modified

Diff hidden because this file has more than 800 lines.

gpu/compiler/executor.odin modified

Diff hidden because this file has more than 800 lines.

gpu/docs/gpu_intent_api.md +4 -2 modified
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, single-command indirect indexed/non-indexed draws, supported borrowed imported mesh buffers, lit imported meshes, imported rect clips, the generated 2D rect/line/circle slice, generated textured quads with concrete texture IDs, and generated glyph quads with texture-backed materials. 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, true per-instance vertex buffers for pipelines that declare an instance layout, single-command indirect indexed/non-indexed draws, supported borrowed imported mesh buffers, lit imported meshes, imported rect clips, the generated 2D rect/line/circle slice, generated textured quads with concrete texture IDs, and generated glyph quads with texture-backed materials. 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
26 unchanged lines hidden
52 52
53 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 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 - Indirect draw argument buffers use the backend-neutral `Indirect_Draw_Args` and `Indirect_Draw_Indexed_Args` ABI. The layouts are intentionally byte-identical to Vulkan, D3D11, D3D12, and OpenGL single-command indirect arguments: 16 bytes for non-indexed draws and 20 bytes for indexed draws. In IR, `packet.instances.indirect` is the argument-buffer resource, `packet.instances.offset` is the byte offset, `packet.instances.stride` is zero-or-ABI-size for Phase 7a, and `packet.instances.count` is limited to one command. True per-instance vertex streams remain separate unsupported vertex-input work.
55 + Indirect draw argument buffers use the backend-neutral `Indirect_Draw_Args` and `Indirect_Draw_Indexed_Args` ABI. The layouts are intentionally byte-identical to Vulkan, D3D11, D3D12, and OpenGL single-command indirect arguments: 16 bytes for non-indexed draws and 20 bytes for indexed draws. In IR, `packet.instances.indirect` is the argument-buffer resource, `packet.instances.offset` is the byte offset, `packet.instances.stride` is zero-or-ABI-size for indirect arguments, and `packet.instances.count` is limited to one command in the current executor.
56 56
57 + Per-instance vertex streams use the normal `packet.instances.resource` field, not the indirect argument field. A planned instance-buffer draw requires a graphics pipeline with `has_instance_layout = true`, a nonzero `packet.instances.stride`, and a prepared instance buffer. Slot 0 is the per-vertex stream; slot 1 is the per-instance stream bound with `packet.instances.offset` and `packet.instances.stride`. Vulkan, D3D11, D3D12, and OpenGL all lower the same backend-neutral `Vertex_Binding.input_rate` and `Vertex_Attribute.binding` metadata; OpenGL uses the DSA vertex-attrib-binding/divisor path internally. Indirect draws may also bind an instance stream: the indirect buffer controls counts/firsts, while the instance buffer controls per-instance attributes.
58 +
57 59 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.
58 60
59 61 Current backend sync matrix:
195 unchanged lines hidden
gpu/frame_api.odin modified

Diff hidden because this file has more than 800 lines.

gpu/render_ir/dump.odin modified

Diff hidden because this file has more than 800 lines.

gpu/render_ir/render_ir.odin modified

Diff hidden because this file has more than 800 lines.

gpu/tests/capabilities_test.odin +21 -0 modified
52 unchanged lines hidden
53 53 }
54 54
55 55 @(test)
56 + test_vertex_layout_can_emit_instance_binding_metadata :: proc(t: ^testing.T) {
57 + layout := bk.vertex_layout_build(.Position, .Color)
58 + binding, attrs, attr_count := bk.vertex_layout_to_pipeline_attrs_for_binding(
59 + &layout,
60 + 1,
61 + .Instance,
62 + 3,
63 + )
64 + testing.expect_value(t, binding.binding, u32(1))
65 + testing.expect_value(t, binding.input_rate, bk.Vertex_Input_Rate.Instance)
66 + testing.expect_value(t, binding.stride, u32(28))
67 + testing.expect_value(t, attr_count, 2)
68 + testing.expect_value(t, attrs[0].location, u32(3))
69 + testing.expect_value(t, attrs[0].binding, u32(1))
70 + testing.expect_value(t, attrs[0].offset, u32(0))
71 + testing.expect_value(t, attrs[1].location, u32(4))
72 + testing.expect_value(t, attrs[1].binding, u32(1))
73 + testing.expect_value(t, attrs[1].offset, u32(12))
74 + }
75 +
76 + @(test)
56 77 test_backend_handle_index_rejects_null_and_out_of_range :: proc(t: ^testing.T) {
57 78 _, null_ok := bk.handle_index(bk.NULL_BUFFER, 8)
58 79 testing.expect(t, !null_ok)
39 unchanged lines hidden
gpu/tests/compiler_executor_test.odin modified

Diff hidden because this file has more than 800 lines.

gpu/tests/recording_backend.odin +40 -2 modified
52 unchanged lines hidden
53 53 descriptor: bk.Descriptor_Handle,
54 54 descriptor_index: u32,
55 55 buffer: bk.Buffer_Handle,
56 + buffer_slot: u32,
57 + buffer_offset: u64,
58 + buffer_stride: u32,
56 59 argument_buffer: bk.Buffer_Handle,
57 60 argument_offset: u64,
58 61 draw_count: u32,
2 unchanged lines hidden
61 64 color_formats: [bk.MAX_COLOR_TARGETS]bk.Format,
62 65 color_views: [bk.MAX_COLOR_TARGETS]bk.Texture_Handle,
63 66 clear_colors: [bk.MAX_COLOR_TARGETS][4]f32,
67 + vertex_binding_count: u32,
68 + vertex_bindings: [16]bk.Vertex_Binding,
69 + vertex_attribute_count: u32,
70 + vertex_attributes: [16]bk.Vertex_Attribute,
64 71
65 72 push_constant_size: u32,
66 73 push_constant_bytes: [compiler.IMPORTED_PUSH_CONSTANT_CAP]u8,
115 unchanged lines hidden
182 189 push_constants = recording_push_constants,
183 190 bind_descriptor_set = recording_bind_descriptor_set,
184 191 bind_vertex_buffer = recording_bind_vertex_buffer,
192 + bind_vertex_buffer_slot = recording_bind_vertex_buffer_slot,
185 193 bind_index_buffer = recording_bind_index_buffer,
186 194 draw = recording_draw,
187 195 draw_indexed = recording_draw_indexed,
204 unchanged lines hidden
392 400 pipeline = handle,
393 401 color_count = desc.color_attachment_count,
394 402 color_formats = desc.color_formats,
403 + vertex_binding_count = u32(len(desc.vertex_bindings)),
404 + vertex_attribute_count = u32(len(desc.vertex_attributes)),
395 405 })
406 + call := &rec.calls[len(rec.calls) - 1]
407 + for binding, i in desc.vertex_bindings {
408 + if i >= len(call.vertex_bindings) {break}
409 + call.vertex_bindings[i] = binding
410 + }
411 + for attr, i in desc.vertex_attributes {
412 + if i >= len(call.vertex_attributes) {break}
413 + call.vertex_attributes[i] = attr
414 + }
396 415 return handle, true
397 416 }
398 417
51 unchanged lines hidden
450 469 }
451 470
452 471 recording_bind_vertex_buffer :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) {
472 + recording_bind_vertex_buffer_slot(ctx, 0, handle, 0, 0)
473 + }
474 +
475 + recording_bind_vertex_buffer_slot :: proc(
476 + ctx: bk.Frame_Context,
477 + slot: u32,
478 + handle: bk.Buffer_Handle,
479 + offset: u64,
480 + stride: u32,
481 + ) {
453 482 if recording_active != nil {
454 - recording_active.bound_vertex_buffer = handle
483 + if slot == 0 {
484 + recording_active.bound_vertex_buffer = handle
485 + }
455 486 }
456 - recording_record({kind = .Bind_Vertex_Buffer, frame_index = ctx.frame_index, buffer = handle})
487 + recording_record({
488 + kind = .Bind_Vertex_Buffer,
489 + frame_index = ctx.frame_index,
490 + buffer = handle,
491 + buffer_slot = slot,
492 + buffer_offset = offset,
493 + buffer_stride = stride,
494 + })
457 495 }
458 496
459 497 recording_bind_index_buffer :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) {
160 unchanged lines hidden
gpu/tests/render_ir/dump_test.odin +1 -1 modified
95 unchanged lines hidden
96 96 " #1 name=\"material\" layout=1 bindings=1\n" +
97 97 " binding=0 type=Combined_Image_Sampler resource=2 sampler=3 size=0\n" +
98 98 "pipelines 2\n" +
99 - " #1 Graphics name=\"rect\" key=\"gfx:vs=1;fs=2;layouts=1;layout=PNU;topology=Triangle_List;cull=Back;front=Counter_Clockwise;blend=true/Alpha;depth=false/false;color=B8G8R8A8_SRGB;depth_format=Undefined;pc=0;pc_stages=None\"\n" +
99 + " #1 Graphics name=\"rect\" key=\"gfx:vs=1;fs=2;layouts=1;layout=PNU;instance_layout=false/PNU;topology=Triangle_List;cull=Back;front=Counter_Clockwise;blend=true/Alpha;depth=false/false;color=B8G8R8A8_SRGB;depth_format=Undefined;pc=0;pc_stages=None\"\n" +
100 100 " #2 Compute name=\"particles\" key=\"compute:shader=0;layouts=None;pc=0;pc_stages=None\"\n" +
101 101 "commands 2\n" +
102 102 " #1 Draw pass=1 pipeline=1 vertex_count=6 instance_count=1\n" +
3 unchanged lines hidden
gpu/tests/render_ir/pipeline_desc_test.odin +2 -2 modified
83 unchanged lines hidden
84 84 testing.expect_value(
85 85 t,
86 86 gfx_key,
87 - "gfx:vs=1;fs=2;layouts=None;layout=PNU;topology=Triangle_List;cull=Back;front=Counter_Clockwise;blend=true/Alpha;depth=true/true;color=B8G8R8A8_SRGB;depth_format=D32_SFLOAT;pc=64;pc_stages=Vertex|Fragment",
87 + "gfx:vs=1;fs=2;layouts=None;layout=PNU;instance_layout=false/PNU;topology=Triangle_List;cull=Back;front=Counter_Clockwise;blend=true/Alpha;depth=true/true;color=B8G8R8A8_SRGB;depth_format=D32_SFLOAT;pc=64;pc_stages=Vertex|Fragment",
88 88 )
89 89
90 90 compute_key := ir.format_pipeline_cache_key(
23 unchanged lines hidden
114 114 testing.expect_value(
115 115 t,
116 116 key,
117 - "gfx:vs=0;fs=0;layouts=1|2;layout=PNU;topology=Triangle_List;cull=Back;front=Counter_Clockwise;blend=true/Alpha;depth=false/false;color=B8G8R8A8_SRGB;depth_format=Undefined;pc=0;pc_stages=None",
117 + "gfx:vs=0;fs=0;layouts=1|2;layout=PNU;instance_layout=false/PNU;topology=Triangle_List;cull=Back;front=Counter_Clockwise;blend=true/Alpha;depth=false/false;color=B8G8R8A8_SRGB;depth_format=Undefined;pc=0;pc_stages=None",
118 118 )
119 119 }
gpu/types.odin +1 -0 modified
206 unchanged lines hidden
207 207 Vertex_Binding :: bk.Vertex_Binding
208 208 Vertex_Attribute :: bk.Vertex_Attribute
209 209 Vertex_Format :: bk.Vertex_Format
210 + Vertex_Input_Rate :: bk.Vertex_Input_Rate
210 211 Vertex_Attrib :: bk.Vertex_Attrib
211 212 Vertex_Layout_Entry :: bk.Vertex_Layout_Entry
212 213 Vertex_Layout :: bk.Vertex_Layout
26 unchanged lines hidden