| |
| |
| |
| |
| |
| |
| |
| #include "ds4_gpu_args.h" |
|
|
| #include <ctype.h> |
| #include <errno.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
|
|
| #define DS4_GPU_ARGS_DEFAULT_SAFETY_MARGIN ((size_t)512 * 1024 * 1024) |
|
|
| |
| static void errbuf_set(char *out, size_t outlen, const char *msg) { |
| if (!out || outlen == 0) return; |
| size_t n = strlen(msg); |
| if (n >= outlen) n = outlen - 1; |
| memcpy(out, msg, n); |
| out[n] = '\0'; |
| } |
|
|
| static void errbuf_setf(char *out, size_t outlen, const char *fmt, ...) { |
| if (!out || outlen == 0) return; |
| va_list ap; |
| va_start(ap, fmt); |
| vsnprintf(out, outlen, fmt, ap); |
| va_end(ap); |
| } |
|
|
| |
| |
| #define DS4_GPU_ARGS_MAX_VRAM_GB 16384 |
| #define DS4_GPU_ARGS_MAX_DEVICE_INDEX 1023 |
|
|
| |
| |
| |
| |
| static int parse_csv_int_list(const char *s, |
| long *out, |
| int *out_count, |
| int max_count, |
| long max_value, |
| char *errbuf, |
| size_t errbuflen, |
| const char *what) { |
| *out_count = 0; |
| if (!s || !*s) { |
| errbuf_setf(errbuf, errbuflen, "%s: empty value", what); |
| return 1; |
| } |
| const char *p = s; |
| while (*p) { |
| |
| while (*p == ' ' || *p == '\t') p++; |
| char *end = NULL; |
| errno = 0; |
| long v = strtol(p, &end, 10); |
| if (end == p) { |
| errbuf_setf(errbuf, errbuflen, "%s: not a number: '%s'", what, p); |
| return 1; |
| } |
| if (errno != 0 || v < 0 || v > max_value) { |
| errbuf_setf(errbuf, errbuflen, |
| "%s: out-of-range value: '%s' (allowed 0..%ld)", |
| what, p, max_value); |
| return 1; |
| } |
| if (*out_count >= max_count) { |
| errbuf_setf(errbuf, errbuflen, "%s: too many entries (max %d)", what, max_count); |
| return 1; |
| } |
| out[*out_count] = v; |
| (*out_count)++; |
| while (*end == ' ' || *end == '\t') end++; |
| if (*end == ',') { |
| end++; |
| if (*end == '\0' || *end == ',') { |
| errbuf_setf(errbuf, errbuflen, "%s: empty entry near '%s'", what, end); |
| return 1; |
| } |
| } else if (*end != '\0') { |
| errbuf_setf(errbuf, errbuflen, "%s: junk after value: '%s'", what, end); |
| return 1; |
| } |
| p = end; |
| } |
| if (*out_count == 0) { |
| errbuf_setf(errbuf, errbuflen, "%s: no values parsed", what); |
| return 1; |
| } |
| return 0; |
| } |
|
|
| int parse_gpu_vram_arg(const char *vram_arg, |
| const char *devices_arg, |
| ds4_gpu_config *out, |
| bool *out_skip_cuda, |
| char *errbuf, |
| size_t errbuflen) { |
| if (!out || !out_skip_cuda) { |
| errbuf_set(errbuf, errbuflen, "internal: NULL out parameter"); |
| return 1; |
| } |
| *out_skip_cuda = false; |
| |
| out->n_gpus = 0; |
| out->safety_margin_bytes = DS4_GPU_ARGS_DEFAULT_SAFETY_MARGIN; |
|
|
| |
| long dev_list[DS4_MAX_GPUS] = {0}; |
| int dev_count = 0; |
| if (devices_arg) { |
| if (parse_csv_int_list(devices_arg, dev_list, &dev_count, |
| DS4_MAX_GPUS, |
| DS4_GPU_ARGS_MAX_DEVICE_INDEX, |
| errbuf, errbuflen, |
| "--gpu-devices") != 0) { |
| return 1; |
| } |
| } |
|
|
| |
| if (vram_arg == NULL) { |
| if (devices_arg == NULL) { |
| errbuf_set(errbuf, errbuflen, "internal: parse_gpu_vram_arg called with both args NULL"); |
| return 1; |
| } |
| |
| vram_arg = "auto"; |
| } |
|
|
| |
| if (!strcmp(vram_arg, "0")) { |
| if (devices_arg) { |
| errbuf_set(errbuf, errbuflen, |
| "--gpu-vram 0 conflicts with --gpu-devices"); |
| return 1; |
| } |
| *out_skip_cuda = true; |
| return 0; |
| } |
|
|
| |
| if (!strcmp(vram_arg, "auto")) { |
| #if !defined(DS4_NO_GPU) && !defined(__APPLE__) |
| int filter_buf[DS4_MAX_GPUS]; |
| const int *filter_ptr = NULL; |
| if (dev_count > 0) { |
| for (int i = 0; i < dev_count; i++) filter_buf[i] = (int)dev_list[i]; |
| filter_ptr = filter_buf; |
| } |
| if (ds4_gpu_args_probe_auto_cuda(filter_ptr, dev_count, out, |
| out->safety_margin_bytes, |
| errbuf, errbuflen) != 0) { |
| return 1; |
| } |
| return 0; |
| #else |
| (void)dev_list; (void)dev_count; |
| errbuf_set(errbuf, errbuflen, |
| "--gpu-vram auto requires a CUDA build"); |
| return 1; |
| #endif |
| } |
|
|
| |
| long vram_list[DS4_MAX_GPUS] = {0}; |
| int vram_count = 0; |
| if (parse_csv_int_list(vram_arg, vram_list, &vram_count, |
| DS4_MAX_GPUS, |
| DS4_GPU_ARGS_MAX_VRAM_GB, |
| errbuf, errbuflen, |
| "--gpu-vram") != 0) { |
| return 1; |
| } |
| if (dev_count > 0 && dev_count != vram_count) { |
| errbuf_setf(errbuf, errbuflen, |
| "--gpu-devices count (%d) does not match --gpu-vram count (%d)", |
| dev_count, vram_count); |
| return 1; |
| } |
| out->n_gpus = vram_count; |
| for (int i = 0; i < vram_count; i++) { |
| int dev_id = (dev_count > 0) ? (int)dev_list[i] : i; |
| out->device_indices[i] = dev_id; |
| out->vram_bytes[i] = (size_t)vram_list[i] * (size_t)1024 * 1024 * 1024; |
| } |
| return 0; |
| } |
|
|
| int format_gpu_layout_line(const ds4_gpu_config *cfg, |
| bool was_auto, |
| char *out, |
| size_t outlen) { |
| if (!out || outlen == 0 || !cfg) return -1; |
| |
| int written = snprintf(out, outlen, |
| "ds4: GPU config: %d device%s [", |
| cfg->n_gpus, cfg->n_gpus == 1 ? "" : "s"); |
| if (written < 0 || (size_t)written >= outlen) return written; |
| for (int i = 0; i < cfg->n_gpus; i++) { |
| int n = snprintf(out + written, outlen - (size_t)written, |
| "%s%d", i ? "," : "", cfg->device_indices[i]); |
| if (n < 0 || (size_t)(written + n) >= outlen) return written + n; |
| written += n; |
| } |
| int n = snprintf(out + written, outlen - (size_t)written, |
| "] requested, budgets "); |
| if (n < 0 || (size_t)(written + n) >= outlen) return written + n; |
| written += n; |
| for (int i = 0; i < cfg->n_gpus; i++) { |
| double gb = (double)cfg->vram_bytes[i] / (1024.0 * 1024.0 * 1024.0); |
| int m = snprintf(out + written, outlen - (size_t)written, |
| "%s%.0f", i ? "," : "", gb); |
| if (m < 0 || (size_t)(written + m) >= outlen) return written + m; |
| written += m; |
| } |
| int t = snprintf(out + written, outlen - (size_t)written, |
| " GB; auto=%s", was_auto ? "true" : "false"); |
| if (t < 0) return t; |
| written += t; |
| return written; |
| } |
|
|