Harbor

Changelog 88859d5c5166

pin runtime diagnostics

@sky · 1 month ago · parent abf6af9d2abc
0 added 5 modified 0 deleted
gpu/docs/gpu_intent_api.md +6 -0 modified
16 unchanged lines hidden
17 17 -> Vulkan / D3D11 / D3D12 / OpenGL / future software
18 18 ```
19 19
20 + ## Execution model
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. The current runtime translator that maps selected IR draw kinds 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 +
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 +
20 26 ## Ownership boundary
21 27
22 28 Host owns semantic visibility and policy:
197 unchanged lines hidden
gpu/frame_api.odin modified

Diff hidden because this file has more than 800 lines.

gpu/render_ir/diagnostics.odin +18 -7 modified
34 unchanged lines hidden
35 35 severity: Diagnostic_Severity,
36 36 code: Diagnostic_Code,
37 37 message: string, // borrowed; caller owns dynamic message storage
38 + command: Command_Handle,
38 39 span: Source_Span,
39 40 }
40 41
19 unchanged lines hidden
60 61 severity: Diagnostic_Severity,
61 62 code: Diagnostic_Code,
62 63 message: string,
64 + command: Command_Handle = INVALID_COMMAND,
63 65 span: Source_Span = {},
64 66 ) {
65 67 append(&list.items, Diagnostic{
66 68 severity = severity,
67 69 code = code,
68 70 message = message,
71 + command = command,
69 72 span = span,
70 73 })
71 74 }
72 75
73 76 add_error :: proc(list: ^Diagnostic_List, code: Diagnostic_Code, message: string, span: Source_Span = {}) {
74 - add_diagnostic(list, .Error, code, message, span)
77 + add_diagnostic(list, .Error, code, message, span = span)
75 78 }
76 79
77 80 add_warning :: proc(list: ^Diagnostic_List, code: Diagnostic_Code, message: string, span: Source_Span = {}) {
78 - add_diagnostic(list, .Warning, code, message, span)
81 + add_diagnostic(list, .Warning, code, message, span = span)
79 82 }
80 83
81 84 add_note :: proc(list: ^Diagnostic_List, code: Diagnostic_Code, message: string, span: Source_Span = {}) {
82 - add_diagnostic(list, .Note, code, message, span)
85 + add_diagnostic(list, .Note, code, message, span = span)
83 86 }
84 87
88 + add_command_error :: proc(list: ^Diagnostic_List, code: Diagnostic_Code, command: Command_Handle, message: string) {
89 + add_diagnostic(list, .Error, code, message, command = command)
90 + }
91 +
85 92 format_code :: proc(code: Diagnostic_Code) -> string {
86 93 switch code {
87 94 case .None: return "RIR0000"
35 unchanged lines hidden
123 130
124 131 format_diagnostic :: proc(d: Diagnostic, allocator: mem.Allocator = context.allocator) -> string {
125 132 severity := format_severity(d.severity)
133 + message := d.message
134 + if d.command != INVALID_COMMAND {
135 + message = fmt.aprintf("command #%d: %s", d.command, d.message, allocator = context.temp_allocator)
136 + }
126 137 if d.code == .None {
127 138 if has_span(d.span) {
128 139 loc := format_span(d.span, allocator = context.temp_allocator)
129 - return fmt.aprintf("%v: %v: %v", loc, severity, d.message, allocator = allocator)
140 + return fmt.aprintf("%v: %v: %v", loc, severity, message, allocator = allocator)
130 141 }
131 - return fmt.aprintf("%v: %v", severity, d.message, allocator = allocator)
142 + return fmt.aprintf("%v: %v", severity, message, allocator = allocator)
132 143 }
133 144
134 145 code := format_code(d.code)
135 146 if has_span(d.span) {
136 147 loc := format_span(d.span, allocator = context.temp_allocator)
137 - return fmt.aprintf("%v: %v[%v]: %v", loc, severity, code, d.message, allocator = allocator)
148 + return fmt.aprintf("%v: %v[%v]: %v", loc, severity, code, message, allocator = allocator)
138 149 }
139 - return fmt.aprintf("%v[%v]: %v", severity, code, d.message, allocator = allocator)
150 + return fmt.aprintf("%v[%v]: %v", severity, code, message, allocator = allocator)
140 151 }
141 152
142 153 format_diagnostic_with_source :: proc(d: Diagnostic, source: string, allocator: mem.Allocator = context.allocator) -> string {
219 unchanged lines hidden
gpu/tests/frame_api_test.odin modified

Diff hidden because this file has more than 800 lines.

gpu/tests/render_ir/diagnostics_test.odin +13 -0 modified
43 unchanged lines hidden
44 44 }
45 45
46 46 @(test)
47 + test_format_diagnostic_with_command_handle :: proc(t: ^testing.T) {
48 + msg := ir.format_diagnostic({
49 + severity = .Error,
50 + code = .Unsupported,
51 + message = "runtime lowering rejected command",
52 + command = ir.Command_Handle(7),
53 + })
54 + defer delete(msg)
55 +
56 + testing.expect_value(t, msg, "error[RIR0007]: command #7: runtime lowering rejected command")
57 + }
58 +
59 + @(test)
47 60 test_format_diagnostic_with_file_and_code :: proc(t: ^testing.T) {
48 61 msg := ir.format_diagnostic({
49 62 severity = .Warning,
50 unchanged lines hidden