Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ LIB_C := h3.c h3_host.c h3_safetensors.c h3_weights.c h3_text_encoder.c \
LIB_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
LIB_M := h3_metal.m h3_gpu.m h3_tokenizer.m
LIB_OBJ := $(LIB_C:.c=.o) $(LIB_M:.m=.o)
LIB_S := h3_shaders_embedded.S
LIB_OBJ := $(LIB_C:.c=.o) $(LIB_M:.m=.o) $(LIB_S:.S=.o)
CLI_OBJ := main.o h3_cli.o linenoise.o

.PHONY: all test parity real-parity clean
Expand Down Expand Up @@ -193,6 +194,11 @@ real-parity: h3_real_prompt_test h3_real_dit_block_test
%.o: %.m
$(CC) $(OBJCFLAGS) -I. -c $< -o $@

%.o: %.S
$(CC) -c $< -o $@

h3_shaders_embedded.o: h3_shaders.metal

tests/%.o: tests/%.c
$(CC) $(CFLAGS) -I. -c $< -o $@

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ mkdir -p outputs
mapping all weights or generating media. Run `./h3 --help` for the complete CLI
reference.

The Metal shader source is embedded in the executable and compiled at runtime,
so `h3` can be installed as a standalone binary:

```sh
mkdir -p "$HOME/bin"
install -m 755 h3 "$HOME/bin/h3"
```

Without `-p`, the same binary starts an Iris-style interactive session:

```sh
Expand Down
2 changes: 2 additions & 0 deletions h3_gpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ typedef struct {
double gpu_seconds;
} h3_gpu_stats;

/* Pass NULL to compile the shader source embedded in the library. The default
* source filename also falls back to the embedded copy when it is absent. */
h3_gpu *h3_gpu_create(const char *shader_source_path,
char *error, size_t error_size);
void h3_gpu_free(h3_gpu *gpu);
Expand Down
34 changes: 27 additions & 7 deletions h3_gpu.m
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,17 @@ static int h3_gpu_dispatch_rows(H3GPU *gpu, NSString *name, uint32_t rows,
return 1;
}

extern const unsigned char h3_shaders_embedded_start[];
extern const unsigned char h3_shaders_embedded_end[];

static NSString *h3_gpu_embedded_shader_source(void) {
size_t length = (size_t)(h3_shaders_embedded_end -
h3_shaders_embedded_start);
return [[NSString alloc] initWithBytes:h3_shaders_embedded_start
length:length
encoding:NSUTF8StringEncoding];
}

h3_gpu *h3_gpu_create(const char *shader_source_path,
char *error, size_t error_size) {
@autoreleasepool {
Expand All @@ -350,13 +361,21 @@ static int h3_gpu_dispatch_rows(H3GPU *gpu, NSString *name, uint32_t rows,
"%.3f GiB\n", (double)gpu.device.currentAllocatedSize /
(1024.0 * 1024.0 * 1024.0));
}
const char *source_path = shader_source_path ? shader_source_path :
"h3_shaders.metal";
NSString *path = [NSString stringWithUTF8String:source_path];
NSError *libraryError = nil;
NSString *source = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:&libraryError];
NSString *source = nil;
NSString *sourceName = @"embedded h3_shaders.metal";
if (shader_source_path) {
sourceName = [NSString stringWithUTF8String:shader_source_path];
source = [NSString stringWithContentsOfFile:sourceName
encoding:NSUTF8StringEncoding
error:&libraryError];
}
if (!source && (!shader_source_path ||
!strcmp(shader_source_path, "h3_shaders.metal"))) {
source = h3_gpu_embedded_shader_source();
sourceName = @"embedded h3_shaders.metal";
libraryError = nil;
}
if (source) {
MTLCompileOptions *options = [[MTLCompileOptions alloc] init];
options.mathMode = MTLMathModeSafe;
Expand Down Expand Up @@ -397,7 +416,8 @@ static int h3_gpu_dispatch_rows(H3GPU *gpu, NSString *name, uint32_t rows,
if (error && error_size) {
const char *description = libraryError.localizedDescription.UTF8String;
snprintf(error, error_size, "cannot compile %s: %s",
source_path, description ? description : "unknown error");
sourceName.UTF8String,
description ? description : "unknown error");
}
return NULL;
}
Expand Down
9 changes: 9 additions & 0 deletions h3_shaders_embedded.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.section __TEXT,__const
.p2align 4

.globl _h3_shaders_embedded_start
_h3_shaders_embedded_start:
.incbin "h3_shaders.metal"

.globl _h3_shaders_embedded_end
_h3_shaders_embedded_end:
2 changes: 1 addition & 1 deletion tests/test_audio_gpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ static void activation_reference(float *output, const float *input,
int main(void) {
test_context test = {0};
char error[512];
test.gpu = h3_gpu_create("h3_shaders.metal", error, sizeof(error));
test.gpu = h3_gpu_create(NULL, error, sizeof(error));
if (!test.gpu) die(error);

const float input_values[] = {0.2f, -0.3f, 0.5f, 0.1f,
Expand Down