-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgpu.psy
More file actions
580 lines (506 loc) · 16.1 KB
/
Copy pathgpu.psy
File metadata and controls
580 lines (506 loc) · 16.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
== default ==
{
add_source_directory("src/gpu");
}
gpu_present_mode ::= enum
{
.immediate := 0;
.vsync := 1;
};
gpu_wsi_type ::= enum
{
// invalid - default value. psygpu will emit an error if a pass attempts to use gpu_resource.window_resource
.invalid := 0;
// gpu_resource.window_resource refers to a genuine existing window
.window := 1;
// gpu_resource.window_resource refers to an internal, offscreen image
.headless := 2;
};
// describes the nature of gpu_resource.window_resource
gpu_wsi ::= struct
{
// type of window system integration (see gpu_wsi_type for info). if != .window, all other data members are ignored.
type : gpu_wsi_type;
// data if .type == .window
window : gpu_wsi_window;
// data if .type == .headless
headless : gpu_wsi_headless;
};
gpu_wsi_window ::= struct
{
// arbitrary handle that you associate with a specific window suitable for use as a gpu-accelerated render target
handle : u64;
// function that converts the arbitrary handle to the underlying native window handle of your operating system
get_native : func(handle : u64 -> u64);
// function that retrieves the dimensions (width and height, in pixels) of the *renderable* window region, e.g excluding title bar height
get_dimensions : func(handle : u64 -> u32[2]);
};
gpu_wsi_headless ::= struct
{
dimensions : u32[2];
};
// initialise psygpu. you must call this before doing anything else. gpu_term() will undo this.
// gpu_err.malformed => you have already called gpu_init without prior termination
// gpu_err.reqfailed => your graphics setup/drivers do not meet the requirements to be used
// gpu_err.cpuoom => not enough cpu memory
// gpu_err.gpuoom => not enough gpu memory
// gpu_err.unknown => internal error, upto you to figure out
gpu_init ::= func(info : gpu_appinfo -> gpu_err)
{
return impl_gpu_init(info);
};
// terminate psygpu. frees all cpu/gpu memory used by psygpu and invalidates all state. you will need to call gpu_init again to do anything.
// gpu_err.malformed => you have not called gpu_init or you have called gpu_term twice
gpu_term ::= func( -> gpu_err)
{
return impl_gpu_term();
};
gpu_create_graph ::= func(info : gpu_graph_info, a : arena mut? -> gpu_graph)
{
return impl_gpu_create_graph(info, a);
};
gpu_graph_add_subgraph ::= func(g : gpu_graph, subgraph : gpu_graph, dependencies : u64?, dependencies_count : u64, a : arena mut? -> u64)
{
return impl_gpu_graph_add_subgraph(g, subgraph, dependencies, dependencies_count, a);
};
gpu_graph_add_pass ::= func(g : gpu_graph, p : gpu_pass, dependencies : u64 mut?, dependencies_count : u64, a : arena mut? -> u64)
{
return impl_gpu_graph_add_pass(g, p, dependencies, dependencies_count, a);
};
gpu_graph_add_operation ::= func(g : gpu_graph, o : gpu_operation, dependencies : u64 mut?, dependencies_count : u64, a : arena mut? -> u64)
{
return impl_gpu_graph_add_operation(g, o, dependencies, dependencies_count, a);
};
// execute the gpu work (async) associated with a graph.
gpu_execute ::= func(g : gpu_graph, long : arena mut?, short : arena mut? -> gpu_err)
{
return impl_gpu_execute(g, long, short);
};
// blocks the current thread until all gpu work associated with the graph to fully complete.
gpu_wait ::= func(g : gpu_graph -> v0)
{
impl_gpu_wait(g);
};
gpu_wait_all ::= func( -> v0)
{
impl_gpu_wait_all();
};
gpu_hardware_count ::= func( -> u64)
{
return impl_gpu_hardware_count();
};
// fill an array with information about all hardware on the machine
// [min(array_size, gpu_hardware_count()) array elements will be filled, in no particular order]
// gpu_err.cpuoom => not enough cpu memory
// gpu_err.gpuoom => not enough gpu memory
// gpu_err.unknown => internal error, upto you to figure out
gpu_query_hardware ::= func(array : gpu_hardware mut?, array_size : u64, a : arena mut? -> gpu_err)
{
return impl_gpu_query_hardware(array, array_size, a);
};
// generate a score for a piece of hardware. higher score means a device generally better-suited for 3d graphics + compute
// you are encouraged to write your own, this is just for starters
gpu_default_hardware_score ::= func(hw : gpu_hardware -> s64)
{
type_tbl ::= s64[4]{100; 20; 10; 0;};
caps_tbl ::= s64[4]{10; -100; -100; 0;};
score : s64 mut := 0;
score = (score + deref(type_tbl # (hw.type@s64)));
score = (score + deref(caps_tbl # (hw.caps@s64)));
// 1 score per GiB of vram
score = (score + ((hw.vram_size_mib / 1024)@s64));
return score;
};
// given an array of enumerated hardwares on the machine, return the index of the hardware that scores highest according to the given scoring function
// - array size must be > 0
// [you likely want to write your own scoring function, but gpu_default_hardware_score for you to have a look at]
gpu_hardware_select ::= func(hardware_options : gpu_hardware?, hardware_options_len : u64, score_fn : func(hw : gpu_hardware -> s64) -> u64)
{
i : u64 mut;
select_idx : u64 mut := 0;
max_score : s64 mut := 0;
for(i = 0, i < hardware_options_len, i = i + 1)
{
score ::= score_fn(deref(hardware_options # i));
if(score > max_score)
{
select_idx = i;
max_score = score;
}
}
return select_idx;
};
// use one of the hardwares you retrieved via gpu_iterate_hardware
// gpu_err.fatal => you used this hardware earlier and did something so evil that the hardware itself is invalidated
// gpu_err.reqfailed => the hardware does not meet the requirements to be used
// gpu_err.cpuoom => not enough cpu memory
// gpu_err.gpuoom => not enough gpu memory
// gpu_err.unknown => internal error, upto you to figure out
gpu_use_hardware ::= func(hw : gpu_hardware, wsi : gpu_wsi -> gpu_err)
{
return impl_gpu_use_hardware(hw, wsi);
};
gpu_set_present_mode ::= func(present_mode : gpu_present_mode -> v0)
{
impl_gpu_set_present_mode(present_mode);
};
// create either a graphics or compute pass
// long <= arena whose lifetime should go until gpu_term
// short <= arena whose lifetime can end immediately
// gpu_err.cpuoom => not enough gpu memory
// gpu_err.gpuoom => not enough gpu memory
// gpu_err.malformed => {
// the pass uses gpu_resource.window_resource but you never provided a valid gpu_wsi in gpu_use_hardware
// }
gpu_create_pass ::= func(info : gpu_pass_info, long : arena mut?, short : arena mut? -> gpu_pass_result)
{
return impl_gpu_create_pass(info, long, short);
};
// create a buffer resource
gpu_create_buffer ::= func(info : gpu_resource_info, a : arena mut? -> gpu_resource_result)
{
return impl_gpu_create_buffer(info, a);
};
// create an image resource
gpu_create_image ::= func(info : gpu_resource_info, a : arena mut? -> gpu_resource_result)
{
return impl_gpu_create_image(info, a);
};
// transfer new data to an existing resource.
// - data_size must be <= the size of the resource data
// [if its a buffer resource with flag .dynamic, this will be really fast, otherwise you should assume it will gpu_wait_all]
gpu_resource_write ::= func(res : gpu_resource, data : v0? weak, data_size : u64, offset : u64 -> v0)
{
impl_gpu_resource_write(res, data, data_size, offset);
};
// read the data from an existing resource into the buffer.
// [if the resource is a dynamic buffer, this is guaranteed to be "relatively recent"]
// [if the resource is written to by the gpu, the data *may* be out of date and there's currently no way to deal with that]
// [you are expected to use gpu_resource_size(...) and your offset to figure out how many bytes read - note this will read less than your array size if it would otherwise go out of bounds]
// gpu_err.malformed => {
// the offset provided was greater than the length of the resource itself i.e the read would go out of bounds
// }
gpu_resource_read ::= func(res : gpu_resource, buf : v0? weak, len : u64, offset : u64 -> gpu_err)
{
return impl_gpu_resource_read(res, buf, len, offset);
};
// get the data size of a resource, in bytes
gpu_resource_size ::= func(res : gpu_resource -> u64)
{
return impl_gpu_resource_size(res);
};
// get a cpu-read/write pointer to a mapping of a dynamic buffer resource
// - res must be a buffer with the .dynamic flag
gpu_resource_pointer ::= func(res : gpu_resource -> gpu_resource_pointer_result)
{
return impl_gpu_resource_pointer(res);
};
// create an arena wrapped around the mapping of a dynamic buffer resource
// - res must be a buffer with the .dynamic flag
gpu_map_resource ::= func(res : gpu_resource -> gpu_map_resource_result)
{
return impl_gpu_map_resource(res);
};
// resizes a buffer to make it smaller or larger. attempts to preserve old data
// res => resource to resize. must be a buffer
// new_size => new size of the resource, in bytes
// long => arena which must outlive the buffer's lifetime
// short => scratch arena which can be cleared at any time
// [invalidates all previous mappings and mapped arenas]
gpu_resize_buffer ::= func(res : gpu_resource, new_size : u64, long : arena mut?, short : arena mut? -> v0)
{
impl_gpu_resize_buffer(res, new_size, long, short);
};
// identical to gpu_resize_buffer, but will early-out if new_size <= gpu_resource_size(res)
gpu_resize_buffer_at_least ::= func(res : gpu_resource, new_size : u64, long : arena mut?, short : arena mut? -> v0)
{
if(gpu_resource_size(res) < new_size)
{
gpu_resize_buffer(res, new_size, long, short);
}
};
// resizes an image. does not preserve old data - the new image memory is entirely zero'd
// res => resource to resize. must be an image
// new_dimensions => new dimensions of the resource, in [width, height] pixels
// long => arena which must outlive the buffer's lifetime
// short => scratch arena which can be cleared at any time
// [guaranteed to early-out if new_dimensions are the same as the old dimensions]
gpu_resize_image ::= func(res : gpu_resource, new_dimensions : u32[2], long : arena mut?, short : arena mut? -> v0)
{
impl_gpu_resize_image(res, new_dimensions, long, short);
};
gpu_pass_set_clear_colour ::= func(p : gpu_pass, new_clear_colour : f32[4] -> v0)
{
impl_gpu_pass_set_clear_colour(p, new_clear_colour);
};
// helper function that loads some .spv files from the file system
// vertex_spv_path => relative path to a .spv for the vertex shader. must exist.
// fragment_spv_path => relative path to a .spv for the fragment shader. must exist.
// a => arena whose lifetime constraints the result
gpu_load_shader_files ::= func(vertex_spv_path : u8?, fragment_spv_path : u8?, a : arena mut? -> gpu_shader_sources)
{
return impl_gpu_load_shader_files(vertex_spv_path, fragment_spv_path, a);
};
// create a shader program from a vertex and fragment spv data
gpu_create_graphics_shader ::= func(vertex_source : u8?, vertex_source_len : u64, fragment_source : u8?, fragment_source_len : u64, a : arena mut? -> gpu_shader)
{
return impl_gpu_create_graphics_shader(vertex_source, vertex_source_len, fragment_source, fragment_source_len, a);
};
// create a shader program from a compute spv data
gpu_create_compute_shader ::= func(compute_source : u8?, compute_source_len : u64, a : arena mut? -> gpu_shader)
{
return impl_gpu_create_compute_shader(compute_source, compute_source_len, a);
};
gpu_create_operation ::= func(info : gpu_operation_info, a : arena mut? -> gpu_operation_result)
{
return impl_gpu_create_operation(info, a);
};
// basic information about your application. it is perfectly valid to pass zero if you cba
gpu_appinfo ::= struct
{
name : u8?;
ver_maj : u32;
ver_min : u32;
};
// represents an error code and a message describing the error. if .code == zero then there is no error
gpu_err ::= struct
{
code : gpu_err_code;
msg : u8?;
};
gpu_err_code ::= enum
{
// something happened that i cant explain
.unknown := 1;
// one of the arguments or state was malformed in some way
.malformed := 2;
// forgot to initialise
.notinit := 3;
// out of cpu memory
.cpuoom := 4;
// out of gpu memory
.gpuoom := 5;
// fatal error has occurred, gpu state is now corrupt
.fatal := 6;
// a driver/hardware feature was required but not available. this is a sign the host machine does not support psygpu
.reqfailed := 7;
};
gpu_cull ::= enum
{
.none := 0;
.back := 2;
.front := 1;
.both := 3;
};
gpu_draw_command ::= struct
{
vertex_count : u32;
instance_count : u32;
first_vertex : u32;
first_instance : u32;
};
gpu_draw_indexed_command ::= struct
{
index_count : u32;
instance_count : u32;
first_index : u32;
vertex_offset : s32;
first_instance : u32;
};
gpu_dispatch_command ::= struct
{
kernel : u32[3];
};
gpu_primitive ::= enum
{
.triangle := 0;
.point := 1;
};
gpu_graphics_state ::= struct
{
primitive : gpu_primitive;
clear_colour : f32[4];
scissor : u32[4];
colour_targets_data : gpu_resource?;
colour_targets_count : u64;
depth_target : gpu_resource;
index_buffer : gpu_resource;
draw_buffer : gpu_resource;
culling : gpu_cull;
static_prim_count : u64;
};
gpu_compute_state ::= struct
{
static_kernel : u32[3];
kernel_buffer : gpu_resource;
};
gpu_hardware_type ::= enum
{
.gpu := 0;
.igpu := 1;
.cpu := 2;
.unknown := 3;
};
gpu_hardware_caps ::= enum
{
.graphics_compute := 0;
.graphics_only := 1;
.compute_only := 2;
.none := 3;
};
gpu_hardware ::= struct
{
name : u8 mut?;
target_heap_gpu : u64;
target_heap_cpu : u64;
vram_size_mib : u64;
type : gpu_hardware_type;
caps : gpu_hardware_caps;
id : u32;
native_handle : u64;
};
gpu_graph_flag ::= enum
{
// at the end of the graphs execution, present the current swapchain image.
.present := 1;
};
gpu_graph_info ::= struct
{
flags : gpu_graph_flag;
name : u8?;
};
gpu_graph ::= enum
{
.invalid := -1;
};
gpu_pass_info ::= struct
{
graphics : gpu_graphics_state;
compute : gpu_compute_state;
shader : gpu_shader;
resources_data : gpu_resource?;
resources_count : u64;
name : u8?;
};
gpu_pass ::= enum
{
.invalid := -1;
};
gpu_pass_result ::= struct
{
res : gpu_pass;
err : gpu_err;
};
gpu_operation_code ::= enum
{
.fill_buffer := 0;
.fill_image := 1;
};
gpu_operation_fill_buffer ::= struct
{
dst : gpu_resource;
val : u8;
};
gpu_operation_fill_image ::= struct
{
dst : gpu_resource;
colour : f32[3];
depth : f32;
};
gpu_operation_info ::= struct
{
code : gpu_operation_code;
fill_buffer : gpu_operation_fill_buffer;
fill_image : gpu_operation_fill_image;
name : u8?;
};
gpu_operation ::= enum
{
.invalid := -1;
};
gpu_operation_result ::= struct
{
res : gpu_operation;
err : gpu_err;
};
gpu_buffer_flag ::= enum
{
.none := 0b00000000;
// buffer memory will be directly accessible from the cpu (i.e BAR) - making writes extremely fast. subject to hardware requirements.
.dynamic := 0b00000001;
// buffer can be used as an index buffer.
.index_buffer := 0b00000010;
// buffer can be used as an indirect draw/compute-kernel buffer.
.indirect_buffer := 0b00000100;
};
gpu_image_flag ::= enum
{
.none := 0b00000000;
// image can be used as a colour target.
.colour_attachment := 0b00000001;
// image can be used as a depth target.
.depth_attachment := 0b00000010;
};
gpu_resource_flag ::= enum
{
// set the memory contents of the resource to zero upon creation. this overwrites any data initially provided.
.zero_memory := 0b00000001;
};
gpu_resource_pointer_result ::= struct
{
ptr : u8 mut? weak;
err : gpu_err;
};
gpu_map_resource_result ::= struct
{
mapping : arena;
err : gpu_err;
};
gpu_image_type ::= enum
{
.invalid := -1;
.rgba32 := 0;
.rgba64 := 1;
.rgba_float := 2;
.depth := 3;
};
gpu_resource_info ::= struct
{
// pointer to any data you want to initially write into the resource. if not, leave this as zero.
data : v0? weak;
// how many bytes do you want to copy from 'data'? if you're creating a buffer, this is also the size of the buffer.
data_size : u64;
// width and height of the image.
image_dimensions : u32[2];
// format of the image.
image_type : gpu_image_type;
// see gpu_resource_flag for details.
flags : gpu_resource_flag;
// see gpu_buffer_flag for details.
buffer_flags : gpu_buffer_flag;
// see gpu_image_flag for details.
image_flags : gpu_image_flag;
// name of the resource. this will be provided to graphics debuggers, and ignored otherwise.
name : u8?;
};
gpu_resource ::= enum
{
.invalid := -1;
.window_resource := -2;
};
gpu_resource_result ::= struct
{
res : gpu_resource;
err : gpu_err;
};
gpu_shader_sources ::= struct
{
vertex_spv_data : u8 mut?;
vertex_spv_count : u64;
fragment_spv_data : u8 mut?;
fragment_spv_count : u64;
};
gpu_shader ::= enum
{
.invalid := -1;
};