4646static struct ggml_backend_metal_device_context {
4747 id <MTLDevice > mtl_device;
4848 int mtl_device_ref_count;
49+ id <MTLLibrary > mtl_library;
4950
5051 bool has_simdgroup_reduction;
5152 bool has_simdgroup_mm;
5758} g_ggml_ctx_dev_main = {
5859 /* .mtl_device =*/ nil ,
5960 /* .mtl_device_ref_count =*/ 0 ,
61+ /* .mtl_library =*/ nil ,
6062 /* .has_simdgroup_reduction =*/ false ,
6163 /* .has_simdgroup_mm =*/ false ,
6264 /* .has_residency_sets =*/ false ,
6567 /* .name =*/ " " ,
6668};
6769
70+ // load library
71+ //
72+ // - first check if the library is embedded
73+ // - then check if the library is in the bundle
74+ // - if not found, load the source and compile it
75+ // - if that fails, return NULL
76+ static id <MTLLibrary > ggml_metal_load_library (id <MTLDevice > device, bool use_bfloat) {
77+ id <MTLLibrary > metal_library = nil ;
78+ NSError * error = nil ;
79+ NSString * src = nil ;
80+
81+ #if GGML_METAL_EMBED_LIBRARY
82+ GGML_LOG_INFO (" %s : using embedded metal library\n " , __func__);
83+
84+ extern const char ggml_metallib_start[];
85+ extern const char ggml_metallib_end[];
86+
87+ src = [[NSString alloc ] initWithBytes: ggml_metallib_start length: (ggml_metallib_end-ggml_metallib_start) encoding: NSUTF8StringEncoding];
88+
89+ #else
90+
91+ #ifdef SWIFT_PACKAGE
92+ NSBundle * bundle = SWIFTPM_MODULE_BUNDLE;
93+ #else
94+ NSBundle * bundle = [NSBundle bundleForClass: [GGMLMetalClass class ]];
95+ #endif
96+
97+ NSString * path_lib = [bundle pathForResource: @" default" ofType: @" metallib" ];
98+ if (path_lib == nil ) {
99+ // Try to find the resource in the directory where the current binary located.
100+ NSString * current_binary = [[NSProcessInfo processInfo ] arguments ][0 ];
101+ NSString * bin_dir = [current_binary stringByDeletingLastPathComponent ];
102+ NSString * default_metallib_path = [NSString pathWithComponents: @[bin_dir, @" default.metallib" ]];
103+ if ([[NSFileManager defaultManager ] isReadableFileAtPath: default_metallib_path]) {
104+ GGML_LOG_INFO (" %s : found '%s '\n " , __func__, [default_metallib_path UTF8String ]);
105+ NSDictionary * atts = [[NSFileManager defaultManager ] attributesOfItemAtPath: default_metallib_path error: &error];
106+ if (atts && atts[NSFileType ] == NSFileTypeSymbolicLink ) {
107+ // Optionally, if this is a symlink, try to resolve it.
108+ default_metallib_path = [[NSFileManager defaultManager ] destinationOfSymbolicLinkAtPath: default_metallib_path error: &error];
109+ if (default_metallib_path && [default_metallib_path length ] > 0 && ![[default_metallib_path substringToIndex: 1 ] isEqualToString: @" /" ]) {
110+ // It is a relative path, adding the binary directory as directory prefix.
111+ default_metallib_path = [NSString pathWithComponents: @[bin_dir, default_metallib_path]];
112+ }
113+ if (!default_metallib_path || ![[NSFileManager defaultManager ] isReadableFileAtPath: default_metallib_path]) {
114+ // Link to the resource could not be resolved.
115+ default_metallib_path = nil ;
116+ } else {
117+ GGML_LOG_INFO (" %s : symlink resolved '%s '\n " , __func__, [default_metallib_path UTF8String ]);
118+ }
119+ }
120+ } else {
121+ // The resource couldn't be found in the binary's directory.
122+ default_metallib_path = nil ;
123+ }
124+ path_lib = default_metallib_path;
125+ }
126+
127+ if (path_lib != nil ) {
128+ // pre-compiled library found
129+ NSURL * libURL = [NSURL fileURLWithPath: path_lib];
130+ GGML_LOG_INFO (" %s : loading '%s '\n " , __func__, [path_lib UTF8String ]);
131+
132+ metal_library = [device newLibraryWithURL: libURL error: &error];
133+ if (error) {
134+ GGML_LOG_ERROR (" %s : error: %s \n " , __func__, [[error description ] UTF8String ]);
135+ return NULL ;
136+ }
137+ } else {
138+ GGML_LOG_INFO (" %s : default.metallib not found, loading from source\n " , __func__);
139+
140+ NSString * path_source;
141+ NSString * path_resource = [[NSProcessInfo processInfo ].environment objectForKey: @" GGML_METAL_PATH_RESOURCES" ];
142+
143+ GGML_LOG_INFO (" %s : GGML_METAL_PATH_RESOURCES = %s \n " , __func__, path_resource ? [path_resource UTF8String ] : " nil" );
144+
145+ if (path_resource) {
146+ path_source = [path_resource stringByAppendingPathComponent: @" ggml-metal.metal" ];
147+ } else {
148+ path_source = [bundle pathForResource: @" ggml-metal" ofType: @" metal" ];
149+ }
150+
151+ if (path_source == nil ) {
152+ GGML_LOG_WARN (" %s : error: could not use bundle path to find ggml-metal.metal, falling back to trying cwd\n " , __func__);
153+ path_source = @" ggml-metal.metal" ;
154+ }
155+
156+ GGML_LOG_INFO (" %s : loading '%s '\n " , __func__, [path_source UTF8String ]);
157+
158+ src = [NSString stringWithContentsOfFile: path_source encoding: NSUTF8StringEncoding error: &error];
159+ if (error) {
160+ GGML_LOG_ERROR (" %s : error: %s \n " , __func__, [[error description ] UTF8String ]);
161+ return NULL ;
162+ }
163+ }
164+ #endif
165+
166+ if (!metal_library) {
167+ @autoreleasepool {
168+ // dictionary of preprocessor macros
169+ NSMutableDictionary * prep = [NSMutableDictionary dictionary ];
170+
171+ if (use_bfloat) {
172+ [prep setObject: @" 1" forKey: @" GGML_METAL_USE_BF16" ];
173+ }
174+
175+ #if GGML_METAL_EMBED_LIBRARY
176+ [prep setObject: @" 1" forKey: @" GGML_METAL_EMBED_LIBRARY" ];
177+ #endif
178+
179+ MTLCompileOptions * options = [MTLCompileOptions new ];
180+ options.preprocessorMacros = prep;
181+
182+ // [options setFastMathEnabled:false];
183+
184+ metal_library = [device newLibraryWithSource: src options: options error: &error];
185+ if (error) {
186+ GGML_LOG_ERROR (" %s : error: %s \n " , __func__, [[error description ] UTF8String ]);
187+ return NULL ;
188+ }
189+
190+ #if !__has_feature(objc_arc)
191+ [options release ];
192+ #endif
193+ }
194+ }
195+
196+ #if GGML_METAL_EMBED_LIBRARY
197+ [src release ];
198+ #endif // GGML_METAL_EMBED_LIBRARY
199+
200+ return metal_library;
201+ }
202+
68203// acquire
69204static id <MTLDevice > ggml_backend_metal_device_acq (struct ggml_backend_metal_device_context * ctx) {
70205 assert (ctx != NULL );
93228#endif
94229
95230 strncpy (ctx->name , [[ctx->mtl_device name ] UTF8String ], sizeof (ctx->name ) - 1 );
231+
232+ if (ctx->mtl_library == nil ) {
233+ ctx->mtl_library = ggml_metal_load_library (ctx->mtl_device , ctx->use_bfloat );
234+ if (ctx->mtl_library == nil ) {
235+ GGML_LOG_ERROR (" %s : error: failed to load metal library\n " , __func__);
236+ }
237+ }
96238 }
97239
98240 ctx->mtl_device_ref_count ++;
@@ -111,6 +253,11 @@ static void ggml_backend_metal_device_rel(struct ggml_backend_metal_device_conte
111253 if (ctx->mtl_device ) {
112254 [ctx->mtl_device release ];
113255 ctx->mtl_device = nil ;
256+
257+ if (ctx->mtl_library ) {
258+ [ctx->mtl_library release ];
259+ ctx->mtl_library = nil ;
260+ }
114261 }
115262 }
116263}
@@ -522,136 +669,10 @@ @implementation GGMLMetalClass
522669
523670 ctx->d_queue = dispatch_queue_create (" ggml-metal" , DISPATCH_QUEUE_CONCURRENT);
524671
525- id <MTLLibrary > metal_library = nil ;
526-
527- // load library
528- //
529- // - first check if the library is embedded
530- // - then check if the library is in the bundle
531- // - if not found, load the source and compile it
532- // - if that fails, return NULL
533- {
534- NSError * error = nil ;
535- NSString * src = nil ;
536-
537- #if GGML_METAL_EMBED_LIBRARY
538- GGML_LOG_INFO (" %s : using embedded metal library\n " , __func__);
539-
540- extern const char ggml_metallib_start[];
541- extern const char ggml_metallib_end[];
542-
543- src = [[NSString alloc ] initWithBytes: ggml_metallib_start length: (ggml_metallib_end-ggml_metallib_start) encoding: NSUTF8StringEncoding];
544-
545- #else
546-
547- #ifdef SWIFT_PACKAGE
548- NSBundle * bundle = SWIFTPM_MODULE_BUNDLE;
549- #else
550- NSBundle * bundle = [NSBundle bundleForClass: [GGMLMetalClass class ]];
551- #endif
552-
553- NSString * path_lib = [bundle pathForResource: @" default" ofType: @" metallib" ];
554- if (path_lib == nil ) {
555- // Try to find the resource in the directory where the current binary located.
556- NSString * current_binary = [[NSProcessInfo processInfo ] arguments ][0 ];
557- NSString * bin_dir = [current_binary stringByDeletingLastPathComponent ];
558- NSString * default_metallib_path = [NSString pathWithComponents: @[bin_dir, @" default.metallib" ]];
559- if ([[NSFileManager defaultManager ] isReadableFileAtPath: default_metallib_path]) {
560- GGML_LOG_INFO (" %s : found '%s '\n " , __func__, [default_metallib_path UTF8String ]);
561- NSDictionary * atts = [[NSFileManager defaultManager ] attributesOfItemAtPath: default_metallib_path error: &error];
562- if (atts && atts[NSFileType ] == NSFileTypeSymbolicLink ) {
563- // Optionally, if this is a symlink, try to resolve it.
564- default_metallib_path = [[NSFileManager defaultManager ] destinationOfSymbolicLinkAtPath: default_metallib_path error: &error];
565- if (default_metallib_path && [default_metallib_path length ] > 0 && ![[default_metallib_path substringToIndex: 1 ] isEqualToString: @" /" ]) {
566- // It is a relative path, adding the binary directory as directory prefix.
567- default_metallib_path = [NSString pathWithComponents: @[bin_dir, default_metallib_path]];
568- }
569- if (!default_metallib_path || ![[NSFileManager defaultManager ] isReadableFileAtPath: default_metallib_path]) {
570- // Link to the resource could not be resolved.
571- default_metallib_path = nil ;
572- } else {
573- GGML_LOG_INFO (" %s : symlink resolved '%s '\n " , __func__, [default_metallib_path UTF8String ]);
574- }
575- }
576- } else {
577- // The resource couldn't be found in the binary's directory.
578- default_metallib_path = nil ;
579- }
580- path_lib = default_metallib_path;
581- }
582-
583- if (path_lib != nil ) {
584- // pre-compiled library found
585- NSURL * libURL = [NSURL fileURLWithPath: path_lib];
586- GGML_LOG_INFO (" %s : loading '%s '\n " , __func__, [path_lib UTF8String ]);
587-
588- metal_library = [device newLibraryWithURL: libURL error: &error];
589- if (error) {
590- GGML_LOG_ERROR (" %s : error: %s \n " , __func__, [[error description ] UTF8String ]);
591- return NULL ;
592- }
593- } else {
594- GGML_LOG_INFO (" %s : default.metallib not found, loading from source\n " , __func__);
595-
596- NSString * path_source;
597- NSString * path_resource = [[NSProcessInfo processInfo ].environment objectForKey: @" GGML_METAL_PATH_RESOURCES" ];
598-
599- GGML_LOG_INFO (" %s : GGML_METAL_PATH_RESOURCES = %s \n " , __func__, path_resource ? [path_resource UTF8String ] : " nil" );
600-
601- if (path_resource) {
602- path_source = [path_resource stringByAppendingPathComponent: @" ggml-metal.metal" ];
603- } else {
604- path_source = [bundle pathForResource: @" ggml-metal" ofType: @" metal" ];
605- }
606-
607- if (path_source == nil ) {
608- GGML_LOG_WARN (" %s : error: could not use bundle path to find ggml-metal.metal, falling back to trying cwd\n " , __func__);
609- path_source = @" ggml-metal.metal" ;
610- }
611-
612- GGML_LOG_INFO (" %s : loading '%s '\n " , __func__, [path_source UTF8String ]);
613-
614- src = [NSString stringWithContentsOfFile: path_source encoding: NSUTF8StringEncoding error: &error];
615- if (error) {
616- GGML_LOG_ERROR (" %s : error: %s \n " , __func__, [[error description ] UTF8String ]);
617- return NULL ;
618- }
619- }
620- #endif
621-
622- if (!metal_library) {
623- @autoreleasepool {
624- // dictionary of preprocessor macros
625- NSMutableDictionary * prep = [NSMutableDictionary dictionary ];
626-
627- if (ctx_dev->use_bfloat ) {
628- [prep setObject: @" 1" forKey: @" GGML_METAL_USE_BF16" ];
629- }
630-
631- #if GGML_METAL_EMBED_LIBRARY
632- [prep setObject: @" 1" forKey: @" GGML_METAL_EMBED_LIBRARY" ];
633- #endif
634-
635- MTLCompileOptions * options = [MTLCompileOptions new ];
636- options.preprocessorMacros = prep;
637-
638- // [options setFastMathEnabled:false];
639-
640- metal_library = [device newLibraryWithSource: src options: options error: &error];
641- if (error) {
642- GGML_LOG_ERROR (" %s : error: %s \n " , __func__, [[error description ] UTF8String ]);
643- return NULL ;
644- }
645-
646- #if !__has_feature(objc_arc)
647- [options release ];
648- #endif
649- }
650- }
651-
652- #if GGML_METAL_EMBED_LIBRARY
653- [src release ];
654- #endif // GGML_METAL_EMBED_LIBRARY
672+ id <MTLLibrary > metal_library = ctx_dev->mtl_library ;
673+ if (metal_library == nil ) {
674+ GGML_LOG_ERROR (" %s : error: metal library is nil\n " , __func__);
675+ return NULL ;
655676 }
656677
657678 // print MTL GPU family:
0 commit comments