diff --git a/Makefile b/Makefile index bb202379..49560b74 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ LIB_M := h3_metal.m h3_gpu.m h3_tokenizer.m LIB_OBJ := $(LIB_C:.c=.o) $(LIB_M:.m=.o) CLI_OBJ := main.o h3_cli.o linenoise.o -.PHONY: all test parity real-parity clean +.PHONY: all test parity real-parity clean h3-cuda cuda-spark cuda-generic cuda all: h3 libh3.a @@ -196,6 +196,54 @@ real-parity: h3_real_prompt_test h3_real_dit_block_test tests/%.o: tests/%.c $(CC) $(CFLAGS) -I. -c $< -o $@ +# --------------------------------------------------------------------------- +# CUDA backend (feat/cuda). The Metal implementation (h3_gpu.m, h3_shaders.metal, +# h3_metal.m, h3_tokenizer.m) is preserved untouched. This builds the CUDA path +# on Linux, replacing the Metal GPU layer with h3_cuda.cu and the Foundation +# tokenizer with h3_tokenizer.c. Usage: +# make cuda-spark DGX Spark / GB10 (omits explicit -arch: fastest on GB10) +# make cuda-generic any local CUDA GPU (nvcc -arch=native) +# make cuda CUDA_ARCH=sm_N explicit arch +CUDA_HOME ?= /usr/local/cuda-13.0 +NVCC ?= $(CUDA_HOME)/bin/nvcc +CUDA_ARCH ?= sm_121 +CUDA_CFLAGS := -std=c11 -O3 -MMD -MP -Wall -Wextra -Wpedantic -Wshadow \ + -Wno-sign-conversion -D_GNU_SOURCE -DH3_CUDA +CUDA_LDLIBS := -L$(CUDA_HOME)/lib64 -lcudart -lcublas -lm + +CUDA_C_SRC := h3.c h3_host.c h3_safetensors.c h3_weights.c h3_text_encoder.c \ + h3_dit_schedule.c h3_dit.c h3_video_vae.c h3_video_encoder.c h3_audio_vae.c \ + h3_ffmpeg.c h3_terminal.c h3_vision_encoder.c h3_multimodal.c h3_tokenizer.c +CUDA_OBJ := $(CUDA_C_SRC:.c=.cuda.o) h3_cuda.cuda.o + +CLI_CUDA_OBJ := main.cuda.o h3_cli.cuda.o linenoise.cuda.o + +%.cuda.o: %.c + $(CC) $(CUDA_CFLAGS) -I. -c $< -o $@ + +h3_cuda.cuda.o: h3_cuda.cu h3_gpu.h h3_cuda.h + $(NVCC) -std=c++17 -arch=$(CUDA_ARCH) -I. -DH3_CUDA -c $< -o $@ + +linenoise.cuda.o: linenoise.c + $(CC) $(CUDA_CFLAGS) -Wno-conversion -Wno-variadic-macro-arguments-omitted -I. -c $< -o $@ + +h3-cuda: $(CLI_CUDA_OBJ) $(CUDA_OBJ) + $(NVCC) -o h3 $^ $(CUDA_LDLIBS) + +cuda-spark: + $(MAKE) -B h3-cuda CUDA_ARCH=sm_121 CC=gcc + +cuda-generic: + $(MAKE) -B h3-cuda CUDA_ARCH=native CC=gcc + +cuda: + @if [ -z "$(strip $(CUDA_ARCH))" ]; then \ + echo "error: specify CUDA_ARCH, e.g. make cuda CUDA_ARCH=sm_120"; \ + exit 2; \ + fi + $(MAKE) -B h3-cuda CUDA_ARCH="$(CUDA_ARCH)" CC=gcc + +# --------------------------------------------------------------------------- # Vendored from Iris. Keep the main project strict without rewriting this small # terminal editor for conversion diagnostics unrelated to H3. linenoise.o: CFLAGS += -Wno-conversion -Wno-variadic-macro-arguments-omitted diff --git a/h3.c b/h3.c index d5dca259..ab4cf7ed 100644 --- a/h3.c +++ b/h3.c @@ -4,6 +4,9 @@ #include "h3_dit.h" #include "h3_ffmpeg.h" #include "h3_metal.h" +#ifdef H3_CUDA +#include "h3_cuda.h" +#endif #include "h3_multimodal.h" #include "h3_safetensors.h" #include "h3_text_encoder.h" @@ -132,8 +135,13 @@ static int h3_key_file(h3_key *key, const char *role, const char *path) { strlen(path), path); return h3_key_append(key, "|%s=%zu:%s:%lld:%lld:%ld", role, strlen(path), path, (long long)status.st_size, +#ifdef __APPLE__ (long long)status.st_mtimespec.tv_sec, status.st_mtimespec.tv_nsec); +#else + (long long)status.st_mtim.tv_sec, + status.st_mtim.tv_nsec); +#endif } static char *h3_conditioning_key(const char *prompt, const h3_params *params, @@ -449,6 +457,15 @@ h3_ctx *h3_load_dir(const char *model_dir) { h3_free(ctx); return NULL; } +#ifdef H3_CUDA + char cuda_error[256]; + if (!h3_cuda_probe(&ctx->device, cuda_error, sizeof(cuda_error))) { + h3_set_error(ctx, "%s", cuda_error); + snprintf(h3_global_error, sizeof(h3_global_error), "%s", ctx->error); + h3_free(ctx); + return NULL; + } +#else char metal_error[256]; if (!h3_metal_probe(&ctx->device, metal_error, sizeof(metal_error))) { h3_set_error(ctx, "%s", metal_error); @@ -456,6 +473,7 @@ h3_ctx *h3_load_dir(const char *model_dir) { h3_free(ctx); return NULL; } +#endif return ctx; } diff --git a/h3_cli.c b/h3_cli.c index 79339c82..6ec06745 100644 --- a/h3_cli.c +++ b/h3_cli.c @@ -15,6 +15,9 @@ #include #include #include +#ifndef __APPLE__ +#include +#endif #include #include @@ -103,7 +106,12 @@ static int set_directory(char destination[H3_CLI_PATH], const char *path) { static uint64_t random_seed(void) { uint64_t value; +#ifdef __APPLE__ arc4random_buf(&value, sizeof(value)); +#else + if (getrandom(&value, sizeof(value), 0) != (ssize_t)sizeof(value)) + return (uint64_t)time(NULL); +#endif return value; } diff --git a/h3_cuda.cu b/h3_cuda.cu new file mode 100644 index 00000000..b2637377 --- /dev/null +++ b/h3_cuda.cu @@ -0,0 +1,589 @@ +/* h3_cuda.cu - CUDA backend skeleton for h3.c (feat/cuda). + * Metal backend (h3_gpu.m/h3_shaders.metal) is preserved untouched; + * this file implements the same h3_gpu.h C API against CUDA/cuBLAS. + * I1 scaffold: probe/create/free real, compute ops are stubs. + */ +#include +#include +#include +#include +#include +#include "h3_gpu.h" +#include "h3.h" +#include "h3_cuda.h" + +#define H3_CUDA_ERR "CUDA backend: op not yet implemented (feat/cuda)" + +struct h3_gpu { void *dev_ctx; char error[512]; }; +struct h3_gpu_tensor { void *device_ptr; h3_gpu_dtype dtype; size_t elements; }; + +int h3_cuda_probe(h3_device_info *info, char *error, size_t error_size) { + int count = 0; + cudaError_t ce = cudaGetDeviceCount(&count); + if (ce != cudaSuccess || count < 1) { + if (error && error_size) + snprintf(error, error_size, "no CUDA device available: %s", cudaGetErrorString(ce)); + return 0; + } + if (info) { + memset(info, 0, sizeof(*info)); + cudaDeviceProp prop; + cudaGetDeviceProperties(&prop, 0); + snprintf(info->name, sizeof(info->name), "%s", prop.name); + snprintf(info->architecture, sizeof(info->architecture), "sm_%d", prop.major * 100 + prop.minor * 10); + info->physical_memory = (uint64_t)prop.totalGlobalMem; + /* GB10/DGX Spark is a unified-memory architecture (CUDA 13 removed + * both prop.unifiedMemory and cudaDevAttrUnifiedMemory). */ + info->unified_memory = 1; + } + return 1; +} +h3_gpu *h3_gpu_create(const char *shader_source_path, char *error, size_t error_size) { + (void)shader_source_path; + h3_gpu *g = (h3_gpu *)calloc(1, sizeof(*g)); + if (!g) { if (error && error_size) snprintf(error, error_size, "oom"); return NULL; } + return g; +} +void h3_gpu_free(h3_gpu *gpu) { if (gpu) free(gpu); } +const char *h3_gpu_error(const h3_gpu *gpu) { + return gpu && gpu->error[0] ? gpu->error : "no error"; +} +static void h3_cuda_seterr(const h3_gpu *gpu) { + if (gpu) snprintf(((h3_gpu *)gpu)->error, sizeof(((h3_gpu *)gpu)->error), "%s", H3_CUDA_ERR); +} + +int h3_gpu_is_m5(const h3_gpu *gpu) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_has_nax_mlp(const h3_gpu *gpu) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_has_int8_mlp(const h3_gpu *gpu) { h3_cuda_seterr(gpu); return (int)0; } +h3_gpu_tensor * h3_gpu_tensor_new_f32(h3_gpu *gpu, size_t elements) { h3_cuda_seterr(gpu); return (h3_gpu_tensor *)NULL; } +h3_gpu_tensor * h3_gpu_tensor_new_bf16(h3_gpu *gpu, size_t elements) { h3_cuda_seterr(gpu); return (h3_gpu_tensor *)NULL; } +h3_gpu_tensor * h3_gpu_tensor_new_i8(h3_gpu *gpu, size_t elements) { h3_cuda_seterr(gpu); return (h3_gpu_tensor *)NULL; } +h3_gpu_tensor * h3_gpu_tensor_from_f32(h3_gpu *gpu, const float *values, + size_t elements) { h3_cuda_seterr(gpu); return (h3_gpu_tensor *)NULL; } +h3_gpu_tensor * h3_gpu_tensor_from_bf16(h3_gpu *gpu, const uint16_t *values, + size_t elements) { h3_cuda_seterr(gpu); return (h3_gpu_tensor *)NULL; } +h3_gpu_tensor * h3_gpu_tensor_from_u32(h3_gpu *gpu, const uint32_t *values, + size_t elements) { h3_cuda_seterr(gpu); return (h3_gpu_tensor *)NULL; } +h3_gpu_tensor * h3_gpu_tensor_load_bf16(h3_gpu *gpu, const char *path, + uint64_t file_offset, size_t elements) { h3_cuda_seterr(gpu); return (h3_gpu_tensor *)NULL; } +h3_gpu_tensor * h3_gpu_tensor_load_f32(h3_gpu *gpu, const char *path, + uint64_t file_offset, size_t elements) { h3_cuda_seterr(gpu); return (h3_gpu_tensor *)NULL; } +int h3_gpu_tensor_read_file_bf16(h3_gpu_tensor *tensor, const char *path, + uint64_t file_offset, size_t elements, + char *error, size_t error_size) { return (int)0; } +int h3_gpu_tensor_stream_file_bf16(h3_gpu_tensor *tensor, const char *path, + uint64_t file_offset, size_t elements, + char *error, size_t error_size) { return (int)0; } +void h3_gpu_tensor_free(h3_gpu_tensor *tensor) { } +size_t h3_gpu_tensor_elements(const h3_gpu_tensor *tensor) { return (size_t)0; } +h3_gpu_dtype h3_gpu_tensor_dtype(const h3_gpu_tensor *tensor) { return (h3_gpu_dtype)0; } +int h3_gpu_tensor_read_f32(const h3_gpu_tensor *tensor, float *values, + size_t elements) { return (int)0; } +int h3_gpu_tensor_read_f32_range(const h3_gpu_tensor *tensor, + size_t source_offset, float *values, + size_t elements) { return (int)0; } +int h3_gpu_tensor_read_bf16(const h3_gpu_tensor *tensor, uint16_t *values, + size_t elements) { return (int)0; } +int h3_gpu_tensor_write_f32(h3_gpu_tensor *tensor, const float *values, + size_t elements) { return (int)0; } +int h3_gpu_tensor_write_f32_range(h3_gpu_tensor *tensor, + size_t destination_offset, + const float *values, size_t elements) { return (int)0; } +int h3_gpu_tensor_write_bf16(h3_gpu_tensor *tensor, const uint16_t *values, + size_t elements) { return (int)0; } +int h3_gpu_tensor_write_bf16_range(h3_gpu_tensor *tensor, + size_t destination_offset, + const uint16_t *values, size_t elements) { return (int)0; } +int h3_gpu_begin(h3_gpu *gpu) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_continue(h3_gpu *gpu) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_submit(h3_gpu *gpu) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_get_stats(const h3_gpu *gpu, h3_gpu_stats *stats) { h3_cuda_seterr(gpu); return (int)0; } +void h3_gpu_profile_set_label(h3_gpu *gpu, const char *label) { h3_cuda_seterr(gpu); } +void h3_gpu_profile_mark(h3_gpu *gpu, const char *phase) { h3_cuda_seterr(gpu); } +int h3_gpu_linear_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t rows, + uint32_t input_dim, uint32_t output_dim) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_patch_linear_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t rows, + uint32_t input_dim, uint32_t output_dim) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_patch_linear_bf16_offset( + h3_gpu *gpu, h3_gpu_tensor *output, + size_t output_offset, + const h3_gpu_tensor *input, size_t input_offset, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t rows, + uint32_t input_dim, uint32_t output_dim) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_patch_linear_bf16_map( + h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, + const h3_gpu_tensor *row_map, + uint32_t output_rows, uint32_t rows, + uint32_t input_dim, uint32_t output_dim) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_silu_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, uint32_t elements) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_cast_f32_to_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, uint32_t elements) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_cast_bf16_to_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, uint32_t elements) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_copy_bf16(h3_gpu *gpu, h3_gpu_tensor *destination, + size_t destination_offset, + const h3_gpu_tensor *source, size_t source_offset, + size_t elements) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_copy_f32(h3_gpu *gpu, h3_gpu_tensor *destination, + size_t destination_offset, + const h3_gpu_tensor *source, size_t source_offset, + size_t elements) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_rms_norm_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, uint32_t rows, + uint32_t width, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_adaln_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *norm_weight, + const h3_gpu_tensor *modulation, + const h3_gpu_tensor *row_map, uint32_t rows, + uint32_t width, uint32_t slots, uint32_t shift_slot, + uint32_t scale_slot, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_gate_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *residual, + const h3_gpu_tensor *branch, + const h3_gpu_tensor *modulation, + const h3_gpu_tensor *row_map, uint32_t rows, + uint32_t width, uint32_t slots, uint32_t gate_slot) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_qkv_rope_f32(h3_gpu *gpu, h3_gpu_tensor *query, + h3_gpu_tensor *key, h3_gpu_tensor *value, + const h3_gpu_tensor *qkv, + const h3_gpu_tensor *q_norm, + const h3_gpu_tensor *k_norm, + const h3_gpu_tensor *rope_cos, + const h3_gpu_tensor *rope_sin, uint32_t sequence, + uint32_t heads, uint32_t head_dim, + uint32_t rope_half, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_sdpa_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *query, const h3_gpu_tensor *key, + const h3_gpu_tensor *value, uint32_t sequence, + uint32_t heads, uint32_t head_dim, float scale) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_swiglu_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *fused, uint32_t rows, + uint32_t width) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_scale_add_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *residual, + const h3_gpu_tensor *branch, + const h3_gpu_tensor *scale, uint32_t rows, + uint32_t width) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_layer_norm_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t rows, + uint32_t width, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_video_qkv_rope_f32(h3_gpu *gpu, h3_gpu_tensor *query, + h3_gpu_tensor *key, h3_gpu_tensor *value, + const h3_gpu_tensor *qkv, + const h3_gpu_tensor *rope_cos, + const h3_gpu_tensor *rope_sin, + uint32_t sequence, uint32_t heads, + uint32_t head_dim, uint32_t rope_half, + float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_conv1d_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t batch, + uint32_t length, uint32_t input_channels, + uint32_t output_channels, uint32_t kernel, + uint32_t padding, uint32_t dilation) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_conv1d_stride_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t batch, + uint32_t length, uint32_t input_channels, + uint32_t output_channels, uint32_t kernel, + uint32_t stride, uint32_t padding, + uint32_t dilation) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_conv_transpose1d_f32( + h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t batch, + uint32_t length, uint32_t input_channels, + uint32_t output_channels, uint32_t kernel, + uint32_t stride, uint32_t padding) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_weight_norm_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *vector, + const h3_gpu_tensor *magnitude, + uint32_t outer, uint32_t inner) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_add_scaled_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *left, + const h3_gpu_tensor *right, float left_scale, + float right_scale, uint32_t elements) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_alias_free_snake_f32( + h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *alpha_log, + const h3_gpu_tensor *beta_log, + const h3_gpu_tensor *upsample_filter, + const h3_gpu_tensor *downsample_filter, + uint32_t batch, uint32_t length, + uint32_t channels) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_snake1d_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *alpha, uint32_t batch, + uint32_t length, uint32_t channels) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_audio_qkv_split_f32(h3_gpu *gpu, + h3_gpu_tensor *query, h3_gpu_tensor *key, + h3_gpu_tensor *value, const h3_gpu_tensor *qkv, + const h3_gpu_tensor *q_bias, + const h3_gpu_tensor *k_bias, + const h3_gpu_tensor *v_bias, uint32_t batch, + uint32_t length, uint32_t heads, + uint32_t head_dim) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_sdpa_causal_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *query, + const h3_gpu_tensor *key, + const h3_gpu_tensor *value, uint32_t batch, + uint32_t sequence, uint32_t heads, + uint32_t head_dim, float scale) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_audio_attention_pool_f32(h3_gpu *gpu, + h3_gpu_tensor *output, + const h3_gpu_tensor *attended, uint32_t batch, + uint32_t length, uint32_t heads, + uint32_t head_dim, uint32_t output_dim) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_geglu_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *gate, + const h3_gpu_tensor *linear, uint32_t elements) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_clip_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, uint32_t elements, + float minimum, float maximum) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_vae_encoder_pad_f32( + h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, uint32_t batch, + uint32_t depth, uint32_t height, uint32_t width, + uint32_t channels, uint32_t depth_front, + uint32_t height_before, uint32_t height_after, + uint32_t width_before, uint32_t width_after) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_conv3d_f32(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t batch, + uint32_t depth, uint32_t height, uint32_t width, + uint32_t input_channels, uint32_t output_channels, + uint32_t kernel_depth, uint32_t kernel_height, + uint32_t kernel_width, uint32_t stride_depth, + uint32_t stride_height, uint32_t stride_width) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_vae_encoder_group_norm_silu_f32( + h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t batch, + uint32_t depth, uint32_t height, uint32_t width, + uint32_t channels, uint32_t groups, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_linear_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t rows, + uint32_t input_dim, uint32_t output_dim) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_mlp_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *fc1_weight, + const h3_gpu_tensor *fc2_weight, uint32_t rows, + uint32_t input_dim, uint32_t hidden_dim, + uint32_t output_dim) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_mlp_nax_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + h3_gpu_tensor *activated, + const h3_gpu_tensor *input, + const h3_gpu_tensor *fc1_weight, + const h3_gpu_tensor *fc2_weight, uint32_t rows, + uint32_t input_dim, uint32_t hidden_dim, + uint32_t output_dim) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_quantize_weight_int8(h3_gpu *gpu, h3_gpu_tensor *output, + h3_gpu_tensor *scales, + const h3_gpu_tensor *input, uint32_t rows, + uint32_t columns) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_linear_int8_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + h3_gpu_tensor *quantized_input, + h3_gpu_tensor *input_scales, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *weight_scales, + uint32_t rows, uint32_t input_dim, + uint32_t output_dim, + int use_slower_uncached_int8_scales) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_linear_int8_head_major_bf16( + h3_gpu *gpu, h3_gpu_tensor *output, + h3_gpu_tensor *quantized_input, + h3_gpu_tensor *input_scales, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *weight_scales, + uint32_t rows, uint32_t heads, + uint32_t head_dim, uint32_t output_dim) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_mlp_int8_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + h3_gpu_tensor *activated, + h3_gpu_tensor *quantized_activation, + h3_gpu_tensor *activation_scales, + const h3_gpu_tensor *input, + const h3_gpu_tensor *fc1_weight, + const h3_gpu_tensor *fc1_scales, + const h3_gpu_tensor *fc2_weight, + const h3_gpu_tensor *fc2_scales, + const h3_gpu_tensor *fc1_bf16, + const h3_gpu_tensor *fc2_bf16, uint32_t rows, + uint32_t input_dim, uint32_t hidden_dim, + uint32_t output_dim, + int use_slower_grouped_quantizer, + int use_slower_dynamic_fc1_k, + int use_int8_row_fc2, + int input_is_quantized) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_silu_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, uint32_t elements) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_rms_norm_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, uint32_t rows, + uint32_t width, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_layer_norm_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t rows, + uint32_t width, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_gelu_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, uint32_t elements, + int approximate) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_vision_qkv_rope_bf16( + h3_gpu *gpu, h3_gpu_tensor *query, + h3_gpu_tensor *key, h3_gpu_tensor *value, + const h3_gpu_tensor *qkv, + const h3_gpu_tensor *rope_cos, + const h3_gpu_tensor *rope_sin, uint32_t sequence, + uint32_t heads, uint32_t head_dim, + uint32_t rope_half) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_adaln_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + const h3_gpu_tensor *norm_weight, + const h3_gpu_tensor *modulation, + const h3_gpu_tensor *row_map, uint32_t rows, + uint32_t width, uint32_t slots, uint32_t shift_slot, + uint32_t scale_slot, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_adaln_bf16_offset(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, size_t input_offset, + const h3_gpu_tensor *norm_weight, + const h3_gpu_tensor *modulation, + const h3_gpu_tensor *row_map, uint32_t rows, + uint32_t width, uint32_t slots, uint32_t shift_slot, + uint32_t scale_slot, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_adaln_linear_bf16( + h3_gpu *gpu, h3_gpu_tensor *output, + h3_gpu_tensor *inverse, + const h3_gpu_tensor *input, size_t input_offset, + const h3_gpu_tensor *norm_weight, + const h3_gpu_tensor *modulation, + const h3_gpu_tensor *row_map, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *bias, uint32_t rows, + uint32_t width, uint32_t output_dim, uint32_t slots, + uint32_t shift_slot, uint32_t scale_slot, + float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_gate_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *residual, + const h3_gpu_tensor *branch, + const h3_gpu_tensor *modulation, + const h3_gpu_tensor *row_map, uint32_t rows, + uint32_t width, uint32_t slots, uint32_t gate_slot) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_gate_adaln_bf16( + h3_gpu *gpu, h3_gpu_tensor *gated_residual, + h3_gpu_tensor *output, + const h3_gpu_tensor *residual, + const h3_gpu_tensor *branch, + const h3_gpu_tensor *norm_weight, + const h3_gpu_tensor *gate_modulation, + const h3_gpu_tensor *norm_modulation, + const h3_gpu_tensor *row_map, uint32_t rows, + uint32_t width, uint32_t slots, uint32_t gate_slot, + uint32_t shift_slot, uint32_t scale_slot, + float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_gate_adaln_quantize_int8( + h3_gpu *gpu, h3_gpu_tensor *gated_residual, + h3_gpu_tensor *quantized_output, + h3_gpu_tensor *quantized_scales, + const h3_gpu_tensor *residual, + const h3_gpu_tensor *branch, + const h3_gpu_tensor *norm_weight, + const h3_gpu_tensor *gate_modulation, + const h3_gpu_tensor *norm_modulation, + const h3_gpu_tensor *row_map, uint32_t rows, + uint32_t padded_rows, uint32_t width, uint32_t slots, + uint32_t gate_slot, uint32_t shift_slot, + uint32_t scale_slot, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_qkv_rope_bf16(h3_gpu *gpu, h3_gpu_tensor *query, + h3_gpu_tensor *key, h3_gpu_tensor *value, + const h3_gpu_tensor *qkv, + const h3_gpu_tensor *q_norm, + const h3_gpu_tensor *k_norm, + const h3_gpu_tensor *rope_cos, + const h3_gpu_tensor *rope_sin, uint32_t sequence, + uint32_t heads, uint32_t head_dim, + uint32_t rope_half, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_grouped_qkv_rope_bf16(h3_gpu *gpu, h3_gpu_tensor *query, + h3_gpu_tensor *key, h3_gpu_tensor *value, + const h3_gpu_tensor *qkv, + const h3_gpu_tensor *q_norm, + const h3_gpu_tensor *k_norm, + const h3_gpu_tensor *rope_cos, + const h3_gpu_tensor *rope_sin, + uint32_t sequence, uint32_t heads, + uint32_t head_dim, uint32_t rope_half, + float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_grouped_qkv_linear_rope_bf16( + h3_gpu *gpu, + h3_gpu_tensor *query, + h3_gpu_tensor *key, + h3_gpu_tensor *value, + h3_gpu_tensor *qkv, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *q_norm, + const h3_gpu_tensor *k_norm, + const h3_gpu_tensor *rope_cos, + const h3_gpu_tensor *rope_sin, + uint32_t rows, uint32_t input_dim, + uint32_t heads, uint32_t head_dim, + uint32_t rope_half, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_grouped_qkv_linear_rope_int8( + h3_gpu *gpu, + h3_gpu_tensor *query, + h3_gpu_tensor *key, + h3_gpu_tensor *value, + h3_gpu_tensor *quantized_input, + h3_gpu_tensor *input_scales, + const h3_gpu_tensor *input, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *weight_scales, + const h3_gpu_tensor *q_norm, + const h3_gpu_tensor *k_norm, + const h3_gpu_tensor *rope_cos, + const h3_gpu_tensor *rope_sin, + uint32_t rows, uint32_t input_dim, + uint32_t heads, uint32_t head_dim, + uint32_t rope_half, float epsilon, + int input_is_quantized, + int use_slower_unfused_qkv_rope, + int use_slower_scalar_qkv_rms, + int use_slower_uncached_int8_scales) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_sdpa_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *query, const h3_gpu_tensor *key, + const h3_gpu_tensor *value, uint32_t sequence, + uint32_t heads, uint32_t head_dim, float scale) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_sdpa_bf16_head_major_output( + h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *query, const h3_gpu_tensor *key, + const h3_gpu_tensor *value, uint32_t sequence, + uint32_t heads, uint32_t head_dim, float scale) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_swiglu_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *fused, uint32_t rows, + uint32_t width) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_embedding_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *weight, + const h3_gpu_tensor *token_ids, uint32_t tokens, + uint32_t vocab_size, uint32_t width) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_text_qk_rope_bf16(h3_gpu *gpu, + h3_gpu_tensor *query_output, + h3_gpu_tensor *key_output, + const h3_gpu_tensor *query_input, + const h3_gpu_tensor *key_input, + const h3_gpu_tensor *q_norm, + const h3_gpu_tensor *k_norm, + const h3_gpu_tensor *rope_cos, + const h3_gpu_tensor *rope_sin, + uint32_t sequence, uint32_t query_heads, + uint32_t kv_heads, uint32_t head_dim, + float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_head_rms_norm_bf16(h3_gpu *gpu, h3_gpu_tensor *tensor, + const h3_gpu_tensor *weight, + uint32_t sequence, uint32_t heads, + uint32_t head_dim, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_rope_text_bf16(h3_gpu *gpu, h3_gpu_tensor *query, + h3_gpu_tensor *key, + const h3_gpu_tensor *rope_cos_f32, + const h3_gpu_tensor *rope_sin_f32, + uint32_t sequence, uint32_t query_heads, + uint32_t kv_heads, uint32_t head_dim) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_gqa_causal_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *query, + const h3_gpu_tensor *key, + const h3_gpu_tensor *value, + uint32_t sequence, uint32_t query_heads, + uint32_t kv_heads, uint32_t head_dim, + float scale) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_add_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *left, const h3_gpu_tensor *right, + uint32_t elements) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_sub_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *left, const h3_gpu_tensor *right, + uint32_t elements) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_token_pool_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *input, + size_t input_offset, + h3_gpu_tensor *original, + size_t original_offset, + h3_gpu_tensor *baseline, + size_t baseline_offset, + const h3_gpu_tensor *baseline_indices, + const h3_gpu_tensor *pairs, uint32_t input_rows, + uint32_t rows, uint32_t baseline_rows, + uint32_t width) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_token_pool_adaln_bf16( + h3_gpu *gpu, h3_gpu_tensor *residual, + h3_gpu_tensor *output, + const h3_gpu_tensor *input, size_t input_offset, + h3_gpu_tensor *original, size_t original_offset, + h3_gpu_tensor *baseline, size_t baseline_offset, + const h3_gpu_tensor *baseline_indices, + const h3_gpu_tensor *pairs, + const h3_gpu_tensor *norm_weight, + const h3_gpu_tensor *modulation, + const h3_gpu_tensor *row_map, + uint32_t input_rows, uint32_t rows, + uint32_t baseline_rows, uint32_t width, + uint32_t slots, uint32_t shift_slot, + uint32_t scale_slot, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_token_expand_delta_bf16( + h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *original, + size_t original_offset, + const h3_gpu_tensor *reduced, + const h3_gpu_tensor *baseline, + size_t baseline_offset, + const h3_gpu_tensor *baseline_indices, + const h3_gpu_tensor *parents, uint32_t rows, + uint32_t reduced_rows, uint32_t baseline_rows, + uint32_t width, + uint32_t exact_prefix_rows, + float update_scale) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_token_expand_adaln_bf16( + h3_gpu *gpu, h3_gpu_tensor *residual, + h3_gpu_tensor *output, + const h3_gpu_tensor *original, + size_t original_offset, + const h3_gpu_tensor *reduced, + const h3_gpu_tensor *baseline, + size_t baseline_offset, + const h3_gpu_tensor *baseline_indices, + const h3_gpu_tensor *parents, + const h3_gpu_tensor *norm_weight, + const h3_gpu_tensor *modulation, + const h3_gpu_tensor *row_map, + uint32_t rows, uint32_t reduced_rows, + uint32_t baseline_rows, uint32_t width, + uint32_t exact_prefix_rows, float update_scale, + uint32_t slots, uint32_t shift_slot, + uint32_t scale_slot, float epsilon) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_euler_bf16(h3_gpu *gpu, h3_gpu_tensor *sample, + size_t sample_offset, const h3_gpu_tensor *last, + const h3_gpu_tensor *previous, uint32_t elements, + float delta, float ratio) { h3_cuda_seterr(gpu); return (int)0; } +int h3_gpu_silu_mul_bf16(h3_gpu *gpu, h3_gpu_tensor *output, + const h3_gpu_tensor *gate, + const h3_gpu_tensor *up, uint32_t elements) { h3_cuda_seterr(gpu); return (int)0; } diff --git a/h3_cuda.h b/h3_cuda.h new file mode 100644 index 00000000..955171cf --- /dev/null +++ b/h3_cuda.h @@ -0,0 +1,16 @@ +#ifndef H3_CUDA_H +#define H3_CUDA_H + +#include "h3.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int h3_cuda_probe(h3_device_info *info, char *error, size_t error_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/h3_ffmpeg.c b/h3_ffmpeg.c index 66762425..cb730f3e 100644 --- a/h3_ffmpeg.c +++ b/h3_ffmpeg.c @@ -12,6 +12,10 @@ #include #include +#ifndef SSIZE_MAX +#define SSIZE_MAX ((ssize_t)(~((size_t)0) >> 1)) +#endif + extern char **environ; static const char *ffmpeg_program(void) { diff --git a/h3_gpu.h b/h3_gpu.h index 3a47cc35..2f1db0f0 100644 --- a/h3_gpu.h +++ b/h3_gpu.h @@ -1,6 +1,10 @@ #ifndef H3_GPU_H #define H3_GPU_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include @@ -610,4 +614,8 @@ int h3_gpu_silu_mul_bf16(h3_gpu *gpu, h3_gpu_tensor *output, const h3_gpu_tensor *gate, const h3_gpu_tensor *up, uint32_t elements); +#ifdef __cplusplus +} +#endif + #endif diff --git a/h3_host.c b/h3_host.c index a04a2a0a..d637d92a 100644 --- a/h3_host.c +++ b/h3_host.c @@ -1,6 +1,8 @@ #include "h3_host.h" +#ifndef H3_CUDA #include +#endif #include #include @@ -555,14 +557,15 @@ int h3_resize_rgb24_high_quality(const uint8_t *input, int frames, free(pixels); return 0; } + size_t input_frame_bytes = input_area * 3; + size_t output_frame_bytes = output_area * 3; +#ifndef H3_CUDA uint8_t *source_argb = malloc(input_area * 4); uint8_t *output_argb = malloc(output_area * 4); if (!source_argb || !output_argb) { free(source_argb); free(output_argb); free(pixels); return 0; } - size_t input_frame_bytes = input_area * 3; - size_t output_frame_bytes = output_area * 3; vImage_Buffer source_buffer = { source_argb, (vImagePixelCount)input_height, (vImagePixelCount)input_width, (size_t)input_width * 4 @@ -594,6 +597,39 @@ int h3_resize_rgb24_high_quality(const uint8_t *input, int frames, } } free(source_argb); free(output_argb); +#else + /* Portable bilinear RGB24 resize (CUDA build; Metal keeps vImage). */ + for (int frame = 0; frame < frames; frame++) { + const uint8_t *src = input + (size_t)frame * input_frame_bytes; + uint8_t *dst = pixels + (size_t)frame * output_frame_bytes; + for (size_t y = 0; y < output_height; y++) { + float gy = (output_height == 1) ? 0.0f : + (float)y * (input_height - 1) / (output_height - 1); + int y0 = (int)gy; + if (y0 > input_height - 2) y0 = input_height - 2; + float fy = gy - (float)y0; + for (size_t x = 0; x < output_width; x++) { + float gx = (output_width == 1) ? 0.0f : + (float)x * (input_width - 1) / (output_width - 1); + int x0 = (int)gx; + if (x0 > input_width - 2) x0 = input_width - 2; + float fx = gx - (float)x0; + const uint8_t *p00 = src + ((size_t)y0 * input_width + x0) * 3; + const uint8_t *p10 = p00 + 3; + const uint8_t *p01 = src + ((size_t)(y0 + 1) * input_width + x0) * 3; + const uint8_t *p11 = p01 + 3; + uint8_t *o = dst + (y * output_width + x) * 3; + for (int c = 0; c < 3; c++) { + float v = (1.0f - fy) * (1.0f - fx) * p00[c] + + (1.0f - fy) * fx * p10[c] + + fy * (1.0f - fx) * p01[c] + + fy * fx * p11[c]; + o[c] = (uint8_t)(v + 0.5f); + } + } + } + } +#endif *output = pixels; return 1; } diff --git a/h3_tokenizer.c b/h3_tokenizer.c new file mode 100644 index 00000000..3993390e --- /dev/null +++ b/h3_tokenizer.c @@ -0,0 +1,59 @@ +/* h3_tokenizer.c - C stub for the CUDA/Linux build (feat/cuda). + * + * The Metal build uses h3_tokenizer.m (Objective-C/Foundation). On Linux there + * is no Foundation, so the CUDA build compiles this C file against the same + * h3_tokenizer.h API. It is a scaffold stub for I1: it satisfies the link so + * the host binary builds and `--info` runs. Real tokenizer port is a later + * iteration (I20). Generation paths that need tokenization return an error. + */ +#include "h3_tokenizer.h" + +#include +#include +#include + +struct h3_tokenizer { + int unused; +}; + +h3_tokenizer *h3_tokenizer_load(const char *tokenizer_json, + char *error, size_t error_size) { + (void)tokenizer_json; + if (error && error_size) { + snprintf(error, error_size, + "CUDA build: tokenizer not yet ported (feat/cuda)"); + } + return NULL; +} + +void h3_tokenizer_free(h3_tokenizer *tokenizer) { + (void)tokenizer; +} + +int h3_tokenizer_encode(const h3_tokenizer *tokenizer, const char *utf8, + int pad_empty, uint32_t **ids, size_t *count, + char *error, size_t error_size) { + (void)tokenizer; (void)utf8; (void)pad_empty; + if (ids) *ids = NULL; + if (count) *count = 0; + if (error && error_size) { + snprintf(error, error_size, + "CUDA build: tokenizer not yet ported (feat/cuda)"); + } + return 0; +} + +void h3_tokenizer_ids_free(uint32_t *ids) { + free(ids); +} + +char *h3_tokenizer_decode(const h3_tokenizer *tokenizer, + const uint32_t *ids, size_t count, + char *error, size_t error_size) { + (void)tokenizer; (void)ids; (void)count; + if (error && error_size) { + snprintf(error, error_size, + "CUDA build: tokenizer not yet ported (feat/cuda)"); + } + return NULL; +}