char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
3f5e94d7c
char/aliceai-t5-moe
1/* 2WebGPU backend implementation. 3Note: Use ClangFormat to format this file. 4*/ 5 6#include "ggml-webgpu.h" 7 8#include "ggml-backend-impl.h" 9#include "ggml-impl.h" 10#include "ggml-webgpu-shader-lib.hpp" 11#include "ggml.h" 12 13#ifdef __EMSCRIPTEN__ 14# include <emscripten/emscripten.h> 15#endif 16 17#include <webgpu/webgpu_cpp.h> 18 19#include <atomic> 20#include <cstdint> 21#include <cstring> 22#ifdef GGML_WEBGPU_GPU_PROFILE 23# include <iomanip> 24#endif 25#if defined(GGML_WEBGPU_DEBUG )|| defined(GGML_WEBGPU_CPU_PROFILE )|| defined(GGML_WEBGPU_GPU_PROFILE ) 26# include <iostream> 27#endif 28#include <memory> 29#include <mutex> 30#include <optional> 31#include <string> 32#include <utility> 33#include <vector> 34 35#define ROUNDUP_POW2 (x ,pow2 ) (((x) + ((pow2) - 1)) & ~((pow2) - 1)) 36#define CEIL_DIV (M ,N ) (((M) + (N) - 1) / (N)) 37 38// Return a rectangular grid of workgroups with minimal over-provisioned workgroups. 39// Assumes that the total number of workgroups does not exceed max_per_dim^2. 40static inline void compute_2d_workgroups (uint32_t total_wg ,uint32_t max_per_dim ,uint32_t & wg_x ,uint32_t & wg_y ) { 41wg_y = std::max (1u ,CEIL_DIV (total_wg ,max_per_dim )); 42wg_x = CEIL_DIV (total_wg ,wg_y ); 43} 44 45static inline uint32_t ggml_webgpu_u32_from_f32 (float value ) { 46uint32_t bits ; 47memcpy (& bits ,& value ,sizeof (bits )); 48return bits ; 49} 50 51#ifdef GGML_WEBGPU_DEBUG 52# define WEBGPU_LOG_DEBUG (msg ) std::cout << msg << std::endl 53# define WEBGPU_DEBUG_BUF_ELEMS 512 54#else 55# define WEBGPU_LOG_DEBUG (msg ) ((void) 0) 56#endif // GGML_WEBGPU_DEBUG 57 58#ifdef GGML_WEBGPU_CPU_PROFILE 59// total timing (aggregated) 60# define WEBGPU_CPU_PROFILE_TOTAL_START (id ) auto cpu_total_start_##id = std::chrono::high_resolution_clock::now(); 61 62# define WEBGPU_CPU_PROFILE_TOTAL_END (id ,ctx ) \ 63 auto cpu_total_end_##id = std::chrono::high_resolution_clock::now(); \ 64 double cpu_total_time_##id = \ 65 std::chrono::duration<double, std::milli>(cpu_total_end_##id - cpu_total_start_##id).count(); \ 66 (ctx)->cpu_time_ms[#id] += cpu_total_time_##id; 67// fine-grained timing (not included in totals) 68# define WEBGPU_CPU_PROFILE_DETAIL_START (id ) auto cpu_detail_start_##id = std::chrono::high_resolution_clock::now(); 69 70# define WEBGPU_CPU_PROFILE_DETAIL_END (id ,ctx ) \ 71 auto cpu_detail_end_##id = std::chrono::high_resolution_clock::now(); \ 72 double cpu_detail_time_##id = \ 73 std::chrono::duration<double, std::milli>(cpu_detail_end_##id - cpu_detail_start_##id).count(); \ 74 (ctx)->cpu_detail_ms[#id] += cpu_detail_time_##id; 75#else 76# define WEBGPU_CPU_PROFILE_TOTAL_START (id ) 77# define WEBGPU_CPU_PROFILE_TOTAL_END (id ,ctx ) 78# define WEBGPU_CPU_PROFILE_DETAIL_START (id ) 79# define WEBGPU_CPU_PROFILE_DETAIL_END (id ,ctx ) 80#endif // GGML_WEBGPU_CPU_PROFILE 81 82#ifdef GGML_WEBGPU_GPU_PROFILE 83# define WEBGPU_MAX_PROFILE_QUERY_COUNT 4096u 84# define WEBGPU_TIMESTAMP_QUERY_BUF_SIZE_BYTES (WEBGPU_MAX_PROFILE_QUERY_COUNT * sizeof(uint64_t)) 85#endif 86 87/* Constants */ 88 89#define WEBGPU_DEFAULT_COMMAND_SUBMIT_BATCH_SIZE 64u 90#define WEBGPU_NUM_PARAM_SLOT_SAFETY_MARGIN 10u 91#define WEBGPU_RUNTIME_WAIT_TIMEOUT_MS 30000u 92#define WEBGPU_RUNTIME_WAIT_TIMEOUT_NS (WEBGPU_RUNTIME_WAIT_TIMEOUT_MS * 1e6) 93#define WEBGPU_PARAMS_BUF_SIZE_BYTES 128 // enough for 32 parameters 94#define WEBGPU_SET_ROWS_ERROR_BUF_SIZE_BYTES 4 95#define WEBGPU_STORAGE_BUF_BINDING_MULT 4 // a storage buffer binding size must be a multiple of 4 96 97/* End Constants */ 98 99// This is a "fake" base pointer, since WebGPU buffers do not have pointers to 100// their locations. 101static void * const webgpu_ptr_base = (void * ) (uintptr_t )0x1000 ;// NOLINT 102 103static size_t ggml_webgpu_tensor_offset (const ggml_tensor * tensor ) { 104const ggml_tensor * base_tensor = tensor -> view_src ?tensor -> view_src :tensor ; 105return (size_t ) ((uintptr_t )base_tensor -> data - (uintptr_t )webgpu_ptr_base )+ tensor -> view_offs ; 106} 107 108/* Struct definitions */ 109 110// Forward reference 111static void ggml_webgpu_create_buffer (wgpu::Device & device , 112 wgpu::Buffer & buffer , 113size_t size , 114 wgpu::BufferUsage usage , 115const char * label ); 116 117// Slot-based parameter arena for compute graph encoding. Each encoded kernel 118// gets a unique uniform-buffer slice within the current batch, and the slot 119// cursor is reset immediately after that batch is submitted. 120struct webgpu_param_arena { 121 wgpu::Buffer buffer ; 122size_t slot_stride = 0 ; 123size_t slot_size = 0 ; 124uint32_t slot_count = 0 ; 125uint32_t next_slot = 0 ; 126 127void init (wgpu::Device device ,size_t slot_size ,uint32_t slot_count ,size_t alignment ) { 128this -> slot_stride = ROUNDUP_POW2 (slot_size ,alignment ); 129this -> slot_size = slot_size ; 130this -> slot_count = slot_count ; 131this -> next_slot = 0 ; 132 133ggml_webgpu_create_buffer (device ,buffer ,this -> slot_stride * slot_count , 134 wgpu::BufferUsage ::CopyDst | wgpu::BufferUsage ::Uniform ,"ggml_webgpu_param_arena" ); 135 } 136 137size_t alloc_slot (size_t size ) { 138GGML_ASSERT (size <=slot_size ); 139if (next_slot >=slot_count ) { 140GGML_ABORT ("ggml_webgpu: parameter arena exhausted while encoding a batch" ); 141 } 142 143return slot_stride * next_slot ++ ; 144 } 145 146void reset () {next_slot = 0 ; } 147 148void cleanup () { 149if (buffer ) { 150buffer .Destroy (); 151buffer = nullptr ; 152 } 153 } 154 155 ~webgpu_param_arena () {this -> cleanup (); } 156}; 157 158struct webgpu_encoded_op { 159uint32_t num_kernels = 0 ; 160#ifdef GGML_WEBGPU_GPU_PROFILE 161 std::vector < std::string > pipeline_names ; 162#endif 163}; 164 165struct webgpu_dispatch_desc { 166webgpu_pipeline pipeline ; 167 std::vector < uint32_t > params ; 168 std::vector < wgpu::BindGroupEntry > bind_group_entries ; 169 std::pair < uint32_t ,uint32_t > workgroups = {1 ,1 }; 170}; 171 172struct webgpu_capabilities { 173 wgpu::Limits limits ; 174bool supports_subgroups = false; 175bool supports_subgroup_matrix = false; 176bool supports_dot_product = false; 177 178uint32_t sg_mat_m = 0 ; 179uint32_t sg_mat_n = 0 ; 180uint32_t sg_mat_k = 0 ; 181 182uint32_t subgroup_size = 0 ; 183uint32_t min_subgroup_size = 0 ; 184uint32_t max_subgroup_size = 0 ; 185size_t memset_bytes_per_thread ; 186}; 187 188// Stores global webgpu members 189struct webgpu_global_context_struct { 190 wgpu::Instance instance ; 191 wgpu::Adapter adapter ; 192 wgpu::Device device ; 193 wgpu::Queue queue ; 194uint32_t command_submit_batch_size = WEBGPU_DEFAULT_COMMAND_SUBMIT_BATCH_SIZE ; 195uint32_t max_inflight_batches = UINT32_MAX ; 196 197webgpu_capabilities capabilities ; 198// Shared buffer to move data from device to host 199 wgpu::Buffer get_tensor_staging_buf ; 200// Global mutex for get_tensor 201 std::recursive_mutex mutex ; 202 203 wgpu::Buffer memset_params_buf ; 204webgpu_pipeline memset_pipeline ; 205 206 std::string vendor ; 207 208// TODO: We should rework the CPU profiling time handling to make it more useful. ref: https://github.com/ggml-org/llama.cpp/pull/22050 209#ifdef GGML_WEBGPU_CPU_PROFILE 210// Profiling: labeled CPU time in ms (total) 211 std::unordered_map < std::string ,double > cpu_time_ms ; 212// Profiling: detailed CPU time in ms 213 std::unordered_map < std::string ,double > cpu_detail_ms ; 214#endif 215 216#ifdef GGML_WEBGPU_DEBUG 217 wgpu::Buffer debug_host_buf ; 218 wgpu::Buffer debug_dev_buf ; 219#endif 220 221 ~webgpu_global_context_struct () { 222if (this -> get_tensor_staging_buf ) { 223this -> get_tensor_staging_buf .Destroy (); 224this -> get_tensor_staging_buf = nullptr ; 225 } 226if (this -> memset_params_buf ) { 227this -> memset_params_buf .Destroy (); 228this -> memset_params_buf = nullptr ; 229 } 230#ifdef GGML_WEBGPU_DEBUG 231if (this -> debug_host_buf ) { 232this -> debug_host_buf .Destroy (); 233this -> debug_host_buf = nullptr ; 234 } 235if (this -> debug_dev_buf ) { 236this -> debug_dev_buf .Destroy (); 237this -> debug_dev_buf = nullptr ; 238 } 239#endif 240 } 241}; 242 243typedef std::shared_ptr < webgpu_global_context_struct > webgpu_global_context ; 244 245// All the base objects needed to run operations on a WebGPU device 246struct webgpu_context_struct { 247// Points to global instances owned by ggml_backend_webgpu_reg_context 248webgpu_global_context global_ctx ; 249 250 std::unique_ptr < ggml_webgpu_shader_lib > shader_lib ; 251 252webgpu_param_arena param_arena ; 253 wgpu::Buffer set_rows_dev_error_buf ; 254 wgpu::Buffer set_rows_host_error_buf ; 255 wgpu::CommandEncoder active_command_encoder ; 256 wgpu::ComputePassEncoder active_compute_pass ; 257bool batch_compute_passes = true; 258 259size_t memset_bytes_per_thread ; 260 261#ifdef GGML_WEBGPU_GPU_PROFILE 262// Profiling: per-shader GPU time in ms 263 std::unordered_map < std::string ,double > shader_gpu_time_ms ; 264 wgpu::Buffer profile_timestamp_dev_buf ; 265 wgpu::Buffer profile_timestamp_host_buf ; 266 wgpu::QuerySet profile_timestamp_query_set ; 267uint32_t profile_timestamp_query_count = 0 ; 268#endif 269 270 ~webgpu_context_struct () { 271#ifdef GGML_WEBGPU_GPU_PROFILE 272if (this -> profile_timestamp_host_buf ) { 273this -> profile_timestamp_host_buf .Destroy (); 274this -> profile_timestamp_host_buf = nullptr ; 275 } 276if (this -> profile_timestamp_dev_buf ) { 277this -> profile_timestamp_dev_buf .Destroy (); 278this -> profile_timestamp_dev_buf = nullptr ; 279 } 280if (this -> profile_timestamp_query_set ) { 281this -> profile_timestamp_query_set .Destroy (); 282this -> profile_timestamp_query_set = nullptr ; 283 } 284#endif 285if (this -> set_rows_host_error_buf ) { 286this -> set_rows_host_error_buf .Destroy (); 287this -> set_rows_host_error_buf = nullptr ; 288 } 289if (this -> set_rows_dev_error_buf ) { 290this -> set_rows_dev_error_buf .Destroy (); 291this -> set_rows_dev_error_buf = nullptr ; 292 } 293 } 294}; 295 296typedef std::shared_ptr < webgpu_context_struct > webgpu_context ; 297 298// Metadata required for the ggml backend registration/discovery interface 299struct ggml_backend_webgpu_reg_context { 300// Since the Instance is a global entrypoint into the WebGPU API, it lives here 301webgpu_global_context webgpu_global_ctx ; 302size_t device_count ; 303const char * name ; 304}; 305 306// Per-device struct for the global logical device interface 307struct ggml_backend_webgpu_device_context { 308webgpu_global_context webgpu_global_ctx ; 309 std::string device_name ; 310 std::string device_desc ; 311}; 312 313// Per-thread data required to actually run WebGPU operations in a backend instance 314struct ggml_backend_webgpu_context { 315webgpu_context webgpu_ctx ; 316 std::string name ; 317}; 318 319// Per-thread data related to buffers 320struct ggml_backend_webgpu_buffer_context { 321 wgpu::Buffer buffer ; 322 std::string label ; 323webgpu_global_context global_ctx ; 324 325ggml_backend_webgpu_buffer_context (wgpu::Buffer buf , std::string lbl ,webgpu_global_context global_ctx_ ) : 326buffer (std::move (buf )), 327label (std::move (lbl )), 328global_ctx (std::move (global_ctx_ )) {} 329}; 330 331/* WebGPU object initializations */ 332 333static webgpu_pipeline ggml_webgpu_create_pipeline (wgpu::Device & device , 334const char * shader_code , 335const char * label , 336const std::vector < wgpu::ConstantEntry > & constants = {}) { 337wgpu::ShaderSourceWGSL shader_source ; 338shader_source . code = shader_code ; 339 340wgpu::ShaderModuleDescriptor shader_desc ; 341shader_desc . nextInChain = & shader_source ; 342 343wgpu::ShaderModule shader_module = device . CreateShaderModule ( & shader_desc ); 344 345wgpu::ComputePipelineDescriptor pipeline_desc ; 346pipeline_desc . label = label ; 347pipeline_desc . compute . module = shader_module ; 348pipeline_desc . compute . entryPoint = "main" ; // Entry point in the WGSL code 349pipeline_desc . layout = nullptr ; // nullptr means auto layout 350if ( constants . size () > 0 ) { 351pipeline_desc . compute . constants = constants . data (); 352pipeline_desc . compute . constantCount = constants . size (); 353} 354return { device . CreateComputePipeline ( & pipeline_desc ), label }; 355} 356 357static void ggml_webgpu_create_buffer (wgpu::Device & device , 358wgpu::Buffer & buffer , 359size_t size , 360wgpu::BufferUsage usage , 361const char * label ) { 362wgpu::BufferDescriptor buffer_desc ; 363buffer_desc . size = size ; 364buffer_desc . usage = usage ; 365buffer_desc . label = label ; 366buffer_desc . mappedAtCreation = false; 367 368// TODO: error handling 369buffer = device . CreateBuffer ( & buffer_desc ); 370} 371 372static wgpu::Buffer ggml_webgpu_tensor_buf ( const ggml_tensor * tensor ) { 373ggml_backend_webgpu_buffer_context * ctx = (ggml_backend_webgpu_buffer_context * ) tensor -> buffer -> context ; 374return ctx -> buffer ; 375} 376 377// Binding offset for a tensor: the largest aligned offset at or before the tensor whose 378// distance to the tensor is a whole number of type blocks, so shaders can index the 379// misalignment in elements even for block quantized types. 380static size_t ggml_webgpu_tensor_align_offset ( const ggml_tensor * t , size_t alignment ) { 381const size_t offset = ggml_webgpu_tensor_offset ( t ); 382const size_t type_size = ggml_type_size ( t -> type ); 383size_t aligned = offset & ~( alignment - 1 ); 384while (( offset - aligned ) % type_size != 0 ) { 385GGML_ASSERT ( aligned >= alignment ); 386aligned -= alignment ; 387} 388return aligned ; 389} 390 391static size_t ggml_webgpu_tensor_misalignment ( const ggml_tensor * t , size_t alignment ) { 392return ggml_webgpu_tensor_offset ( t ) - ggml_webgpu_tensor_align_offset ( t , alignment ); 393} 394 395static size_t ggml_webgpu_tensor_misalignment (webgpu_context & ctx , const ggml_tensor * t ) { 396return ggml_webgpu_tensor_misalignment ( t , ctx -> global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ); 397} 398 399static size_t ggml_webgpu_tensor_align_offset (webgpu_context & ctx , const ggml_tensor * t ) { 400return ggml_webgpu_tensor_align_offset ( t , ctx -> global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ); 401} 402 403static size_t ggml_webgpu_tensor_binding_size ( const ggml_tensor * t , size_t alignment ) { 404return ROUNDUP_POW2 ( ggml_nbytes ( t ) + ggml_webgpu_tensor_misalignment ( t , alignment ), 405WEBGPU_STORAGE_BUF_BINDING_MULT ); 406} 407 408static size_t ggml_webgpu_tensor_binding_size (webgpu_context & ctx , const ggml_tensor * t ) { 409return ggml_webgpu_tensor_binding_size ( t , ctx -> global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ); 410} 411 412static bool ggml_webgpu_tensor_binding_overlap ( const webgpu_global_context & global_ctx , 413const ggml_tensor * a , 414const ggml_tensor * b ) { 415if ( a -> buffer != b -> buffer ) { 416return false; 417} 418 419const size_t alignment = global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ; 420const size_t a_offset = ggml_webgpu_tensor_align_offset ( a , alignment ); 421const size_t b_offset = ggml_webgpu_tensor_align_offset ( b , alignment ); 422return a_offset < b_offset + ggml_webgpu_tensor_binding_size ( b , alignment ) && 423b_offset < a_offset + ggml_webgpu_tensor_binding_size ( a , alignment ); 424} 425 426static bool ggml_webgpu_tensor_binding_overlap_range ( const webgpu_global_context & global_ctx , 427ggml_tensor * tensor , 428ggml_backend_buffer_t buffer , 429size_t offset , 430size_t size ) { 431if ( tensor -> buffer != buffer ) { 432return false; 433} 434 435const size_t alignment = global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ; 436const size_t tensor_offset = ggml_webgpu_tensor_align_offset ( tensor , alignment ); 437return tensor_offset < offset + size && offset < tensor_offset + ggml_webgpu_tensor_binding_size ( tensor , alignment ); 438} 439 440struct ggml_webgpu_merged_binding_range { 441size_t offset ; 442size_t size ; 443}; 444 445static ggml_webgpu_merged_binding_range ggml_webgpu_tensor_merged_binding_range ( 446webgpu_context & ctx , 447std::initializer_list < ggml_tensor *> tensors ) { 448size_t merged_offset = SIZE_MAX ; 449size_t merged_end = 0 ; 450 451for (ggml_tensor * tensor : tensors ) { 452const size_t bind_offset = ggml_webgpu_tensor_align_offset ( ctx , tensor ); 453const size_t bind_end = bind_offset + ggml_webgpu_tensor_binding_size ( ctx , tensor ); 454 455merged_offset = std:: min ( merged_offset , bind_offset ); 456merged_end = std:: max ( merged_end , bind_end ); 457} 458 459return { merged_offset , merged_end - merged_offset }; 460} 461 462static uint32_t ggml_webgpu_tensor_merged_element_offset ( const ggml_tensor * tensor , 463const ggml_webgpu_merged_binding_range & merged_range ) { 464return (uint32_t) (( ggml_webgpu_tensor_offset ( tensor ) - merged_range . offset ) / ggml_type_size ( tensor -> type )); 465} 466 467static wgpu::BindGroupEntry ggml_webgpu_make_bind_group_entry (uint32_t binding , 468wgpu::Buffer buffer , 469uint64_t offset , 470uint64_t size ) { 471wgpu::BindGroupEntry entry = {}; 472entry . binding = binding ; 473entry . buffer = std:: move ( buffer ); 474entry . offset = offset ; 475entry . size = size ; 476return entry ; 477} 478 479static wgpu::BindGroupEntry ggml_webgpu_make_tensor_bind_group_entry (webgpu_context & ctx , 480uint32_t binding , 481ggml_tensor * tensor ) { 482return ggml_webgpu_make_bind_group_entry ( binding , ggml_webgpu_tensor_buf ( tensor ), 483ggml_webgpu_tensor_align_offset ( ctx , tensor ), 484ggml_webgpu_tensor_binding_size ( ctx , tensor )); 485} 486 487/** End WebGPU object initializations */ 488 489/** WebGPU Actions */ 490 491template < typename T > 492static void ggml_backend_webgpu_check_wait_status (wgpu::WaitStatus wait_status , 493T callback_status , 494T success_status , 495const char * wait_name , 496const char * failure_name , 497const char * callback_message ) { 498if ( wait_status == wgpu::WaitStatus:: TimedOut ) { 499GGML_ABORT ( "ggml_webgpu: %s timed out after %u ms\n" , wait_name , WEBGPU_RUNTIME_WAIT_TIMEOUT_MS ); 500} 501if ( wait_status == wgpu::WaitStatus:: Error ) { 502GGML_ABORT ( "ggml_webgpu: %s failed\n" , wait_name ); 503} 504if ( callback_status != success_status ) { 505GGML_ABORT ( "ggml_webgpu: %s failed with status %d: %s\n" , failure_name , static_cast < int > ( callback_status ), 506callback_message ); 507} 508} 509 510// TODO: these next two functions may want tuning across different platforms and workloads, 511static uint32_t ggml_backend_webgpu_get_max_inflight_batches () { 512return UINT32_MAX ; 513} 514 515static uint32_t ggml_backend_webgpu_get_command_submit_batch_size () { 516return WEBGPU_DEFAULT_COMMAND_SUBMIT_BATCH_SIZE ; 517} 518 519static void ggml_backend_webgpu_wait_queue (webgpu_global_context & ctx ) { 520wgpu::QueueWorkDoneStatus callback_status = wgpu::QueueWorkDoneStatus:: Error ; 521std::string callback_message ; 522 523const wgpu::WaitStatus wait_status = ctx -> instance . WaitAny ( 524ctx -> queue . OnSubmittedWorkDone ( 525wgpu::CallbackMode:: AllowSpontaneous , 526[ & callback_status , & callback_message ](wgpu::QueueWorkDoneStatus status , wgpu::StringView message ) { 527callback_status = status ; 528callback_message = std:: string ( message ); 529}), 530WEBGPU_RUNTIME_WAIT_TIMEOUT_NS ); 531 532ggml_backend_webgpu_check_wait_status ( wait_status , callback_status , wgpu::QueueWorkDoneStatus:: Success , 533"Queue wait" , "Queue work" , callback_message . c_str ()); 534} 535 536static void ggml_backend_webgpu_map_buffer (webgpu_global_context & ctx , 537wgpu::Buffer & buffer , 538wgpu::MapMode mode , 539size_t offset , 540size_t size ) { 541wgpu::MapAsyncStatus callback_status = wgpu::MapAsyncStatus:: Error ; 542std::string callback_message ; 543 544const wgpu::WaitStatus wait_status = ctx -> instance . WaitAny ( 545buffer . MapAsync ( mode , offset , size , wgpu::CallbackMode:: AllowSpontaneous , 546[ & callback_status , & callback_message ](wgpu::MapAsyncStatus status , wgpu::StringView message ) { 547callback_status = status ; 548callback_message = std:: string ( message ); 549}), 550WEBGPU_RUNTIME_WAIT_TIMEOUT_NS ); 551 552ggml_backend_webgpu_check_wait_status ( wait_status , callback_status , wgpu::MapAsyncStatus:: Success , 553"Buffer map wait" , "Buffer map" , callback_message . c_str ()); 554} 555 556static void ggml_backend_webgpu_submit_commands (webgpu_context & ctx , 557const wgpu::CommandBuffer commands , 558uint32_t & num_inflight_batches ) { 559if ( num_inflight_batches >= ctx -> global_ctx -> max_inflight_batches ) { 560ggml_backend_webgpu_wait_queue ( ctx -> global_ctx ); 561num_inflight_batches = 0 ; 562} 563 564ctx -> global_ctx -> queue . Submit ( 1 , & commands ); 565num_inflight_batches ++ ; 566} 567 568#ifdef GGML_WEBGPU_DEBUG 569// This function adds debugging information to shaders, as WebGPU does not support printing directly. 570// To use, add a bind group entry to the setup for the shader you are debugging, add the buffer and 571// debug statements in the shader, and then call this function after encoding the commands and submitting them. 572static void ggml_backend_webgpu_debug (webgpu_global_context & ctx ) { 573wgpu::CommandEncoder encoder = ctx -> device . CreateCommandEncoder (); 574encoder . CopyBufferToBuffer ( ctx -> debug_dev_buf , 0 , ctx -> debug_host_buf , 0 , ctx -> debug_host_buf . GetSize ()); 575wgpu::CommandBuffer commands = encoder . Finish (); 576ctx -> queue . Submit ( 1 , & commands ); 577ggml_backend_webgpu_map_buffer ( ctx , ctx -> debug_host_buf , wgpu::MapMode:: Read , 0 , ctx -> debug_host_buf . GetSize ()); 578const float * debug_data = ( const float * ) ctx -> debug_host_buf . GetConstMappedRange (); 579std:: cout << "debug[0]: " << debug_data [ 0 ] << "\n" ; 580ctx -> debug_host_buf . Unmap (); 581} 582#endif 583 584static webgpu_encoded_op ggml_backend_webgpu_build_multi (webgpu_context & ctx , 585const std::vector < webgpu_dispatch_desc > & dispatches ) { 586webgpu_encoded_op result = {}; 587std::vector < wgpu::BindGroup > bind_groups ; 588std::vector < size_t > param_offsets ; 589result . num_kernels = dispatches . size (); 590 591for (size_t i = 0 ; i < dispatches . size (); i ++ ) { 592const webgpu_dispatch_desc & dispatch = dispatches [ i ]; 593const size_t param_size = dispatch . params . size () * sizeof (uint32_t); 594const size_t param_offset = ctx -> param_arena . alloc_slot ( param_size ); 595 596std::vector < wgpu::BindGroupEntry > entries = dispatch . bind_group_entries ; 597uint32_t params_binding_num = entries . size (); 598entries . push_back ( ggml_webgpu_make_bind_group_entry ( params_binding_num , ctx -> param_arena . buffer , param_offset , 599ctx -> param_arena . slot_size )); 600 601wgpu::BindGroupDescriptor bind_group_desc ; 602bind_group_desc . layout = dispatch . pipeline . pipeline . GetBindGroupLayout ( 0 ); 603bind_group_desc . entryCount = entries . size (); 604bind_group_desc . entries = entries . data (); 605bind_group_desc . label = dispatch . pipeline . name . c_str (); 606bind_groups . push_back ( ctx -> global_ctx -> device . CreateBindGroup ( & bind_group_desc )); 607param_offsets . push_back ( param_offset ); 608} 609 610for (size_t i = 0 ; i < param_offsets . size (); i ++ ) { 611ctx -> global_ctx -> queue . WriteBuffer ( ctx -> param_arena . buffer , param_offsets [ i ], dispatches [ i ]. params . data (), 612dispatches [ i ]. params . size () * sizeof (uint32_t)); 613} 614 615#ifdef GGML_WEBGPU_GPU_PROFILE 616for (size_t i = 0 ; i < dispatches . size (); i ++ ) { 617GGML_ASSERT ( ctx -> profile_timestamp_query_count + 2 <= WEBGPU_MAX_PROFILE_QUERY_COUNT ); 618const uint32_t query_begin = ctx -> profile_timestamp_query_count ++ ; 619const uint32_t query_end = ctx -> profile_timestamp_query_count ++ ; 620 621wgpu::PassTimestampWrites ts_writes = {}; 622ts_writes . querySet = ctx -> profile_timestamp_query_set ; 623ts_writes . beginningOfPassWriteIndex = query_begin ; 624ts_writes . endOfPassWriteIndex = query_end ; 625wgpu::ComputePassDescriptor pass_desc = {}; 626pass_desc . timestampWrites = & ts_writes ; 627 628wgpu::ComputePassEncoder pass = ctx -> active_command_encoder . BeginComputePass ( & pass_desc ); 629 630pass . SetPipeline ( dispatches [ i ]. pipeline . pipeline ); 631pass . SetBindGroup ( 0 , bind_groups [ i ]); 632pass . DispatchWorkgroups ( dispatches [ i ]. workgroups . first , dispatches [ i ]. workgroups . second , 1 ); 633pass . End (); 634result . pipeline_names . push_back ( dispatches [ i ]. pipeline . name ); 635} 636#else 637for (size_t i = 0 ; i < dispatches . size (); i ++ ) { 638if ( ctx -> batch_compute_passes ) { 639ctx -> active_compute_pass . SetPipeline ( dispatches [ i ]. pipeline . pipeline ); 640ctx -> active_compute_pass . SetBindGroup ( 0 , bind_groups [ i ]); 641ctx -> active_compute_pass . DispatchWorkgroups ( dispatches [ i ]. workgroups . first , dispatches [ i ]. workgroups . second , 6421 ); 643} else { 644wgpu::ComputePassEncoder pass = ctx -> active_command_encoder . BeginComputePass (); 645pass . SetPipeline ( dispatches [ i ]. pipeline . pipeline ); 646pass . SetBindGroup ( 0 , bind_groups [ i ]); 647pass . DispatchWorkgroups ( dispatches [ i ]. workgroups . first , dispatches [ i ]. workgroups . second , 1 ); 648pass . End (); 649} 650} 651#endif 652 653return result ; 654} 655 656static webgpu_encoded_op ggml_backend_webgpu_build (webgpu_context & ctx , 657webgpu_pipeline & pipeline , 658std::vector < uint32_t > params , 659std::vector < wgpu::BindGroupEntry > bind_group_entries , 660uint32_t wg_x , 661uint32_t wg_y = 1 ) { 662return ggml_backend_webgpu_build_multi ( 663ctx , { 664{ pipeline , std:: move ( params ), std:: move ( bind_group_entries ), { wg_x , wg_y } }, 665}); 666} 667 668static void ggml_backend_webgpu_buffer_memset (webgpu_global_context & ctx , 669wgpu::Buffer & buf , 670uint32_t value , 671size_t offset , 672size_t size ) { 673std::vector < uint32_t > params = { (uint32_t) offset , (uint32_t) size , value }; 674std::vector < wgpu::BindGroupEntry > entries = { ggml_webgpu_make_bind_group_entry ( 0 , buf , 0 , buf . GetSize ()) }; 675size_t bytes_per_wg = 676ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup * ctx -> capabilities . memset_bytes_per_thread ; 677uint32_t wg_x = CEIL_DIV ( size + 3 , bytes_per_wg ); 678 679ctx -> queue . WriteBuffer ( ctx -> memset_params_buf , 0 , params . data (), params . size () * sizeof (uint32_t)); 680 681wgpu::BindGroupEntry params_entry = {}; 682params_entry . binding = 1 ; 683params_entry . buffer = ctx -> memset_params_buf ; 684params_entry . offset = 0 ; 685params_entry . size = WEBGPU_PARAMS_BUF_SIZE_BYTES ; 686entries . push_back ( params_entry ); 687 688wgpu::BindGroupDescriptor bind_group_desc ; 689bind_group_desc . layout = ctx -> memset_pipeline . pipeline . GetBindGroupLayout ( 0 ); 690bind_group_desc . entryCount = entries . size (); 691bind_group_desc . entries = entries . data (); 692bind_group_desc . label = ctx -> memset_pipeline . name . c_str (); 693wgpu::BindGroup bind_group = ctx -> device . CreateBindGroup ( & bind_group_desc ); 694 695wgpu::CommandEncoder encoder = ctx -> device . CreateCommandEncoder (); 696wgpu::ComputePassEncoder pass = encoder . BeginComputePass (); 697pass . SetPipeline ( ctx -> memset_pipeline . pipeline ); 698pass . SetBindGroup ( 0 , bind_group ); 699pass . DispatchWorkgroups ( wg_x , 1 , 1 ); 700pass . End (); 701 702wgpu::CommandBuffer command = encoder . Finish (); 703std::vector < wgpu::CommandBuffer > commands = { command }; 704ctx -> queue . Submit ( commands . size (), commands . data ()); 705} 706 707/** End WebGPU Actions */ 708 709/** GGML Backend Interface */ 710 711static const char * ggml_backend_webgpu_name (ggml_backend_t backend ) { 712ggml_backend_webgpu_context * ctx = (ggml_backend_webgpu_context * ) backend -> context ; 713return ctx -> name . c_str (); 714} 715 716static void ggml_backend_webgpu_free (ggml_backend_t backend ) { 717ggml_backend_webgpu_context * ctx = (ggml_backend_webgpu_context * ) backend -> context ; 718WEBGPU_LOG_DEBUG ( "ggml_backend_webgpu_free(" << ctx -> name << ")" ); 719 720#ifdef GGML_WEBGPU_CPU_PROFILE 721std:: cout << "\n[ggml_webgpu cpu profiling summary]\n" ; 722double total_cpu = 0.0 ; 723for ( const auto & kv : ctx -> webgpu_ctx -> global_ctx -> cpu_time_ms ) { 724total_cpu += kv . second ; 725} 726std:: cout << "ggml_webgpu: total cpu time: " << total_cpu << " ms\n" ; 727std:: cout << "ggml_webgpu: cpu breakdown:\n" ; 728for ( const auto & kv : ctx -> webgpu_ctx -> global_ctx -> cpu_time_ms ) { 729double pct = ( total_cpu > 0.0 ) ? ( kv . second / total_cpu * 100.0 ) : 0.0 ; 730std:: cout << "ggml_webgpu: " << kv . first << ": " << kv . second << " ms (" << pct << "%)\n" ; 731} 732if ( ctx -> webgpu_ctx -> global_ctx -> cpu_detail_ms . size () > 0 ) { 733std:: cout << "ggml_webgpu: cpu detailed breakdown:\n" ; 734} 735for ( const auto & kv : ctx -> webgpu_ctx -> global_ctx -> cpu_detail_ms ) { 736double pct = ( total_cpu > 0.0 ) ? ( kv . second / total_cpu * 100.0 ) : 0.0 ; 737std:: cout << "ggml_webgpu: " << kv . first << ": " << kv . second << " ms (" << pct << "%)\n" ; 738} 739#endif 740 741#ifdef GGML_WEBGPU_GPU_PROFILE 742std:: cout << "\n[ggml_webgpu gpu profiling summary]\n" ; 743double total_gpu = 0.0 ; 744for ( const auto & kv : ctx -> webgpu_ctx -> shader_gpu_time_ms ) { 745total_gpu += kv . second ; 746} 747std:: cout << "ggml_webgpu: total gpu time (all shaders): " << total_gpu << " ms\n" ; 748std:: cout << "\nggml_webgpu: gpu breakdown:\n" ; 749for ( const auto & kv : ctx -> webgpu_ctx -> shader_gpu_time_ms ) { 750double pct = ( total_gpu > 0.0 ) ? ( kv . second / total_gpu * 100.0 ) : 0.0 ; 751std:: cout << "ggml_webgpu: " << kv . first << ": " << kv . second << " ms (" << std:: fixed << std:: setprecision ( 2 ) 752<< pct << "%)\n" ; 753} 754#endif 755 756#if defined( GGML_WEBGPU_CPU_PROFILE ) && defined( GGML_WEBGPU_GPU_PROFILE ) 757std:: cout << "ggml_webgpu: gpu/cpu ratio: " << ( total_cpu > 0.0 ? total_gpu / total_cpu : 0.0 ) << "\n" ; 758#endif 759 760delete ctx ; 761delete backend ; 762} 763 764static webgpu_encoded_op ggml_webgpu_cpy (webgpu_context & ctx , ggml_tensor * src , ggml_tensor * dst ) { 765ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 766shader_lib_ctx . src0 = src ; 767shader_lib_ctx . dst = dst ; 768shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 769 770webgpu_pipeline pipeline = ctx -> shader_lib -> get_cpy_pipeline ( shader_lib_ctx ); 771 772auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 773 774uint32_t ne = (uint32_t) ggml_nelements ( dst ); 775 776std::vector < uint32_t > params = { 777ne , (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )), 778(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 779// Convert byte-strides to element-strides 780(uint32_t) ( src -> nb [ 0 ] / ggml_type_size ( src -> type )), (uint32_t) ( src -> nb [ 1 ] / ggml_type_size ( src -> type )), 781(uint32_t) ( src -> nb [ 2 ] / ggml_type_size ( src -> type )), (uint32_t) ( src -> nb [ 3 ] / ggml_type_size ( src -> type )), 782(uint32_t) ( dst -> nb [ 0 ] / ggml_type_size ( dst -> type )), (uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 783(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), (uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 784// Logical shapes 785(uint32_t) src -> ne [ 0 ], (uint32_t) src -> ne [ 1 ], (uint32_t) src -> ne [ 2 ], (uint32_t) dst -> ne [ 0 ], 786(uint32_t) dst -> ne [ 1 ], (uint32_t) dst -> ne [ 2 ] 787}; 788 789std::vector < wgpu::BindGroupEntry > entries = { 790ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src ), 791ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst ), 792}; 793 794uint32_t wg_x ; 795uint32_t wg_y ; 796uint32_t total_wg = CEIL_DIV ( ne , decisions -> wg_size ); 797compute_2d_workgroups ( total_wg , ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension , wg_x , wg_y ); 798return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 799} 800 801static webgpu_encoded_op ggml_webgpu_set (webgpu_context & ctx , 802ggml_tensor * src0 , 803ggml_tensor * src1 , 804ggml_tensor * dst ) { 805ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 806shader_lib_ctx . src0 = src0 ; 807shader_lib_ctx . src1 = src1 ; 808shader_lib_ctx . dst = dst ; 809shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 810 811webgpu_pipeline pipeline = ctx -> shader_lib -> get_set_pipeline ( shader_lib_ctx ); 812 813auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 814const bool inplace = decisions -> inplace ; 815 816const uint32_t ne = inplace ? (uint32_t) ggml_nelements ( src1 ) : (uint32_t) ggml_nelements ( dst ); 817const uint32_t dst_type_size = (uint32_t) ggml_type_size ( dst -> type ); 818 819std::vector < uint32_t > params = { 820ne , 821(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )), 822(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )), 823(uint32_t) ((( const int32_t * ) dst -> op_params )[ 3 ] / dst_type_size ), 824 825(uint32_t) ( src1 -> nb [ 0 ] / ggml_type_size ( src1 -> type )), 826(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 827(uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )), 828(uint32_t) ( src1 -> nb [ 3 ] / ggml_type_size ( src1 -> type )), 829 8301u , 831(uint32_t) ((( const int32_t * ) dst -> op_params )[ 0 ] / dst_type_size ), 832(uint32_t) ((( const int32_t * ) dst -> op_params )[ 1 ] / dst_type_size ), 833(uint32_t) ((( const int32_t * ) dst -> op_params )[ 2 ] / dst_type_size ), 834 835(uint32_t) src1 -> ne [ 0 ], 836(uint32_t) src1 -> ne [ 1 ], 837(uint32_t) src1 -> ne [ 2 ], 838(uint32_t) src1 -> ne [ 3 ], 839}; 840 841std::vector < wgpu::BindGroupEntry > entries ; 842uint32_t binding_index = 0 ; 843if (! inplace ) { 844entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 )); 845binding_index ++ ; 846} 847entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , binding_index , src1 )); 848entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , binding_index + 1 , dst )); 849 850uint32_t wg_x = CEIL_DIV ( ne , decisions -> wg_size ); 851return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x ); 852} 853 854static webgpu_encoded_op ggml_webgpu_pad (webgpu_context & ctx , ggml_tensor * src , ggml_tensor * dst ) { 855ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 856shader_lib_ctx . src0 = src ; 857shader_lib_ctx . dst = dst ; 858shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 859 860webgpu_pipeline pipeline = ctx -> shader_lib -> get_pad_pipeline ( shader_lib_ctx ); 861 862auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 863 864const uint32_t ne = (uint32_t) ggml_nelements ( dst ); 865 866std::vector < uint32_t > params = { 867ne , 868(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )), 869(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 870// Strides (in elements) 871(uint32_t) ( src -> nb [ 0 ] / ggml_type_size ( src -> type )), 872(uint32_t) ( src -> nb [ 1 ] / ggml_type_size ( src -> type )), 873(uint32_t) ( src -> nb [ 2 ] / ggml_type_size ( src -> type )), 874(uint32_t) ( src -> nb [ 3 ] / ggml_type_size ( src -> type )), 875// Shapes 876(uint32_t) src -> ne [ 0 ], 877(uint32_t) src -> ne [ 1 ], 878(uint32_t) src -> ne [ 2 ], 879(uint32_t) src -> ne [ 3 ], 880(uint32_t) dst -> ne [ 0 ], 881(uint32_t) dst -> ne [ 1 ], 882(uint32_t) dst -> ne [ 2 ], 883(uint32_t) dst -> ne [ 3 ], 884// Pad sizes 885(uint32_t) ggml_get_op_params_i32 ( dst , 0 ), 886(uint32_t) ggml_get_op_params_i32 ( dst , 1 ), 887(uint32_t) ggml_get_op_params_i32 ( dst , 2 ), 888(uint32_t) ggml_get_op_params_i32 ( dst , 3 ), 889(uint32_t) ggml_get_op_params_i32 ( dst , 4 ), 890(uint32_t) ggml_get_op_params_i32 ( dst , 5 ), 891(uint32_t) ggml_get_op_params_i32 ( dst , 6 ), 892(uint32_t) ggml_get_op_params_i32 ( dst , 7 ), 893}; 894 895std::vector < wgpu::BindGroupEntry > entries = { 896ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src ), 897ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst ), 898}; 899 900uint32_t wg_x = CEIL_DIV ( ne , decisions -> wg_size ); 901return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x ); 902} 903 904static webgpu_encoded_op ggml_webgpu_solve_tri (webgpu_context & ctx , 905ggml_tensor * src0 , 906ggml_tensor * src1 , 907ggml_tensor * dst ) { 908ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 909shader_lib_ctx . src0 = src0 ; 910shader_lib_ctx . src1 = src1 ; 911shader_lib_ctx . dst = dst ; 912shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 913shader_lib_ctx . wg_mem_limit_bytes = ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupStorageSize ; 914 915webgpu_pipeline pipeline = ctx -> shader_lib -> get_solve_tri_pipeline ( shader_lib_ctx ); 916 917auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 918 919std::vector < uint32_t > params = { 920(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )), 921(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )), 922(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 923 924(uint32_t) ( src0 -> nb [ 0 ] / ggml_type_size ( src0 -> type )), 925(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 926(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 927(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 928 929(uint32_t) ( src1 -> nb [ 0 ] / ggml_type_size ( src1 -> type )), 930(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 931(uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )), 932(uint32_t) ( src1 -> nb [ 3 ] / ggml_type_size ( src1 -> type )), 933 934(uint32_t) ( dst -> nb [ 0 ] / ggml_type_size ( dst -> type )), 935(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 936(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 937(uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 938 939(uint32_t) src1 -> ne [ 0 ], 940(uint32_t) dst -> ne [ 2 ], 941}; 942 943std::vector < wgpu::BindGroupEntry > entries = { 944ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 ), 945ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , src1 ), 946ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , dst ), 947}; 948 949const uint32_t wg_x = CEIL_DIV ((uint32_t) src1 -> ne [ 0 ], decisions -> wg_size ); 950const uint32_t wg_y = (uint32_t) ( dst -> ne [ 2 ] * dst -> ne [ 3 ]); 951return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 952} 953 954static webgpu_encoded_op ggml_webgpu_conv_2d (webgpu_context & ctx , 955ggml_tensor * src0 , 956ggml_tensor * src1 , 957ggml_tensor * dst ) { 958const int32_t s0 = ggml_get_op_params_i32 ( dst , 0 ); 959const int32_t s1 = ggml_get_op_params_i32 ( dst , 1 ); 960const int32_t p0 = ggml_get_op_params_i32 ( dst , 2 ); 961const int32_t p1 = ggml_get_op_params_i32 ( dst , 3 ); 962const int32_t d0 = ggml_get_op_params_i32 ( dst , 4 ); 963const int32_t d1 = ggml_get_op_params_i32 ( dst , 5 ); 964 965std::vector < uint32_t > params = { 966(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )), 967(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )), 968(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 969 970(uint32_t) ( src0 -> nb [ 0 ] / ggml_type_size ( src0 -> type )), 971(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 972(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 973(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 974 975(uint32_t) ( src1 -> nb [ 0 ] / ggml_type_size ( src1 -> type )), 976(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 977(uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )), 978(uint32_t) ( src1 -> nb [ 3 ] / ggml_type_size ( src1 -> type )), 979 980(uint32_t) ( dst -> nb [ 0 ] / ggml_type_size ( dst -> type )), 981(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 982(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 983(uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 984 985(uint32_t) src0 -> ne [ 0 ], 986(uint32_t) src0 -> ne [ 1 ], 987(uint32_t) src0 -> ne [ 2 ], 988 989(uint32_t) src1 -> ne [ 0 ], 990(uint32_t) src1 -> ne [ 1 ], 991 992(uint32_t) dst -> ne [ 0 ], 993(uint32_t) dst -> ne [ 1 ], 994(uint32_t) dst -> ne [ 2 ], 995(uint32_t) dst -> ne [ 3 ], 996 997(uint32_t) s0 , 998(uint32_t) s1 , 999(uint32_t) p0 , 1000(uint32_t) p1 , 1001(uint32_t) d0 , 1002(uint32_t) d1 , 1003}; 1004 1005std::vector < wgpu::BindGroupEntry > entries = { 1006ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 ), 1007ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , src1 ), 1008ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , dst ), 1009}; 1010 1011ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1012shader_lib_ctx . src0 = src0 ; 1013shader_lib_ctx . src1 = src1 ; 1014shader_lib_ctx . dst = dst ; 1015shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1016 1017webgpu_pipeline pipeline = ctx -> shader_lib -> get_conv2d_pipeline ( shader_lib_ctx ); 1018 1019auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 1020 1021uint32_t wg_x ; 1022uint32_t wg_y ; 1023uint32_t total_wg = CEIL_DIV ((uint32_t) ggml_nelements ( dst ), decisions -> wg_size ); 1024compute_2d_workgroups ( total_wg , ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension , wg_x , wg_y ); 1025 1026return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 1027} 1028 1029// Same param/binding layout as conv_2d; the shader differs 1030static webgpu_encoded_op ggml_webgpu_conv_2d_dw (webgpu_context & ctx , 1031ggml_tensor * src0 , 1032ggml_tensor * src1 , 1033ggml_tensor * dst ) { 1034const int32_t s0 = ggml_get_op_params_i32 ( dst , 0 ); 1035const int32_t s1 = ggml_get_op_params_i32 ( dst , 1 ); 1036const int32_t p0 = ggml_get_op_params_i32 ( dst , 2 ); 1037const int32_t p1 = ggml_get_op_params_i32 ( dst , 3 ); 1038const int32_t d0 = ggml_get_op_params_i32 ( dst , 4 ); 1039const int32_t d1 = ggml_get_op_params_i32 ( dst , 5 ); 1040 1041// Scalar params matching conv2d_dw.wgsl (weight src0 [KW,KH,1,C], input src1, output dst). 1042std::vector < uint32_t > params = { 1043(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )), 1044(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )), 1045(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 1046 1047(uint32_t) ggml_nelements ( dst ), 1048(uint32_t) dst -> ne [ 2 ], 1049(uint32_t) dst -> ne [ 0 ], 1050(uint32_t) dst -> ne [ 1 ], 1051(uint32_t) src1 -> ne [ 0 ], 1052(uint32_t) src1 -> ne [ 1 ], 1053(uint32_t) src0 -> ne [ 0 ], 1054(uint32_t) src0 -> ne [ 1 ], 1055 1056(uint32_t) s0 , 1057(uint32_t) s1 , 1058(uint32_t) p0 , 1059(uint32_t) p1 , 1060(uint32_t) d0 , 1061(uint32_t) d1 , 1062}; 1063 1064std::vector < wgpu::BindGroupEntry > entries = { 1065ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 ), 1066ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , src1 ), 1067ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , dst ), 1068}; 1069 1070ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1071shader_lib_ctx . src0 = src0 ; 1072shader_lib_ctx . src1 = src1 ; 1073shader_lib_ctx . dst = dst ; 1074shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1075 1076// Input layout: contiguous -> WHCN, contiguous-channels -> CWHN 1077const bool whcn = ggml_is_contiguous ( src1 ); 1078webgpu_pipeline pipeline = ctx -> shader_lib -> get_conv2d_dw_pipeline ( shader_lib_ctx , whcn ); 1079auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 1080 1081uint32_t wg_x ; 1082uint32_t wg_y ; 1083uint32_t total_wg = CEIL_DIV ((uint32_t) ggml_nelements ( dst ), decisions -> wg_size ); 1084compute_2d_workgroups ( total_wg , ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension , wg_x , wg_y ); 1085 1086return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 1087} 1088 1089static webgpu_encoded_op ggml_webgpu_im2col (webgpu_context & ctx , 1090ggml_tensor * src0 , 1091ggml_tensor * src1 , 1092ggml_tensor * dst ) { 1093const int32_t s0 = ggml_get_op_params_i32 ( dst , 0 ); 1094const int32_t s1 = ggml_get_op_params_i32 ( dst , 1 ); 1095const int32_t p0 = ggml_get_op_params_i32 ( dst , 2 ); 1096const int32_t p1 = ggml_get_op_params_i32 ( dst , 3 ); 1097const int32_t d0 = ggml_get_op_params_i32 ( dst , 4 ); 1098const int32_t d1 = ggml_get_op_params_i32 ( dst , 5 ); 1099const bool is_2D = ggml_get_op_params_i32 ( dst , 6 ) == 1 ; 1100 1101const uint32_t KW = src0 -> ne [ 0 ]; 1102const uint32_t KH = is_2D ? src0 -> ne [ 1 ] : 1 ; 1103const uint32_t IC = is_2D ? src0 -> ne [ 2 ] : src0 -> ne [ 1 ]; 1104 1105const uint32_t IW = src1 -> ne [ 0 ]; 1106const uint32_t IH = is_2D ? src1 -> ne [ 1 ] : 1 ; 1107const uint32_t N = is_2D ? src1 -> ne [ 3 ] : src1 -> ne [ 2 ]; 1108 1109const uint32_t OW = dst -> ne [ 1 ]; 1110const uint32_t OH = is_2D ? dst -> ne [ 2 ] : 1 ; 1111 1112const uint32_t si0 = (uint32_t) ( src1 -> nb [ 0 ] / ggml_type_size ( src1 -> type )); 1113const uint32_t si1 = is_2D ? (uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )) : 0 ; 1114const uint32_t si2 = is_2D ? (uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )) : 1115(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )); 1116const uint32_t si3 = is_2D ? (uint32_t) ( src1 -> nb [ 3 ] / ggml_type_size ( src1 -> type )) : 1117(uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )); 1118 1119const uint32_t so0 = (uint32_t) ( dst -> nb [ 0 ] / ggml_type_size ( dst -> type )); 1120const uint32_t so1 = (uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )); 1121const uint32_t so2 = is_2D ? (uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )) : 0 ; 1122const uint32_t so3 = is_2D ? (uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )) : 1123(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )); 1124 1125std::vector < uint32_t > params = { 1126(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )), 1127(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 1128 1129si0 , 1130si1 , 1131si2 , 1132si3 , 1133so0 , 1134so1 , 1135so2 , 1136so3 , 1137 1138KW , 1139KH , 1140IC , 1141 1142IW , 1143IH , 1144N , 1145 1146OW , 1147OH , 1148 1149(uint32_t) s0 , 1150(uint32_t) s1 , 1151(uint32_t) p0 , 1152(uint32_t) p1 , 1153(uint32_t) d0 , 1154(uint32_t) d1 , 1155}; 1156 1157std::vector < wgpu::BindGroupEntry > entries = { 1158ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src1 ), 1159ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst ), 1160}; 1161 1162ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1163shader_lib_ctx . src0 = src0 ; 1164shader_lib_ctx . src1 = src1 ; 1165shader_lib_ctx . dst = dst ; 1166shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1167 1168webgpu_pipeline pipeline = ctx -> shader_lib -> get_im2col_pipeline ( shader_lib_ctx ); 1169 1170auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 1171 1172uint32_t wg_x ; 1173uint32_t wg_y ; 1174uint32_t total_wg = CEIL_DIV ((uint32_t) ggml_nelements ( dst ), decisions -> wg_size ); 1175compute_2d_workgroups ( total_wg , ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension , wg_x , wg_y ); 1176 1177return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 1178} 1179 1180static webgpu_encoded_op ggml_webgpu_ssm_conv (webgpu_context & ctx , 1181ggml_tensor * src0 , 1182ggml_tensor * src1 , 1183ggml_tensor * dst ) { 1184ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1185shader_lib_ctx . src0 = src0 ; 1186shader_lib_ctx . src1 = src1 ; 1187shader_lib_ctx . dst = dst ; 1188shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1189 1190webgpu_pipeline pipeline = ctx -> shader_lib -> get_ssm_conv_pipeline ( shader_lib_ctx ); 1191auto * decisions = static_cast < ggml_webgpu_ssm_conv_shader_decisions *> ( pipeline . context . get ()); 1192 1193const uint32_t token_tiles = CEIL_DIV ((uint32_t) dst -> ne [ 1 ], decisions -> tokens_per_wg ); 1194 1195std::vector < uint32_t > params = { 1196(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )), 1197(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )), 1198(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 1199 1200(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 1201(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 1202(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 1203 1204(uint32_t) ( dst -> nb [ 0 ] / ggml_type_size ( dst -> type )), 1205(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 1206(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 1207 1208(uint32_t) src1 -> ne [ 0 ], 1209(uint32_t) src0 -> ne [ 1 ], 1210(uint32_t) dst -> ne [ 1 ], 1211(uint32_t) dst -> ne [ 2 ], 1212token_tiles , 1213}; 1214 1215std::vector < wgpu::BindGroupEntry > entries = { 1216ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 ), 1217ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , src1 ), 1218ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , dst ), 1219}; 1220 1221const uint32_t wg_x = CEIL_DIV ((uint32_t) src0 -> ne [ 1 ], decisions -> block_size ); 1222const uint32_t wg_y = token_tiles * (uint32_t) dst -> ne [ 2 ]; 1223return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 1224} 1225 1226static webgpu_encoded_op ggml_webgpu_ssm_scan (webgpu_context & ctx , 1227ggml_tensor * src0 , 1228ggml_tensor * src1 , 1229ggml_tensor * src2 , 1230ggml_tensor * src3 , 1231ggml_tensor * src4 , 1232ggml_tensor * src5 , 1233ggml_tensor * src6 , 1234ggml_tensor * dst ) { 1235ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1236shader_lib_ctx . src0 = src0 ; 1237shader_lib_ctx . src1 = src1 ; 1238shader_lib_ctx . src2 = src2 ; 1239shader_lib_ctx . src3 = src3 ; 1240shader_lib_ctx . src4 = src4 ; 1241shader_lib_ctx . src5 = src5 ; 1242shader_lib_ctx . dst = dst ; 1243shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1244shader_lib_ctx . supports_subgroups = ctx -> global_ctx -> capabilities . supports_subgroups ; 1245bool xbc_overlap = ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , src1 , src2 ) || 1246ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , src1 , src4 ) || 1247ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , src1 , src5 ) || 1248ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , src2 , src4 ) || 1249ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , src2 , src5 ) || 1250ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , src4 , src5 ); 1251bool a_overlap = false; 1252bool ids_overlap = false; 1253ggml_webgpu_merged_binding_range xbc_merged_range = {}; 1254if ( xbc_overlap ) { 1255xbc_merged_range = ggml_webgpu_tensor_merged_binding_range ( ctx , { src1 , src2 , src4 , src5 }); 1256a_overlap = ggml_webgpu_tensor_binding_overlap_range ( ctx -> global_ctx , src3 , src1 -> buffer , 1257xbc_merged_range . offset , xbc_merged_range . size ); 1258if ( a_overlap ) { 1259xbc_merged_range = ggml_webgpu_tensor_merged_binding_range ( ctx , { src1 , src2 , src3 , src4 , src5 }); 1260} 1261ids_overlap = ggml_webgpu_tensor_binding_overlap_range ( ctx -> global_ctx , src6 , src1 -> buffer , 1262xbc_merged_range . offset , xbc_merged_range . size ); 1263if ( ids_overlap ) { 1264xbc_merged_range = 1265a_overlap ? ggml_webgpu_tensor_merged_binding_range ( ctx , { src1 , src2 , src3 , src4 , src5 , src6 }) : 1266ggml_webgpu_tensor_merged_binding_range ( ctx , { src1 , src2 , src4 , src5 , src6 }); 1267} 1268} 1269 1270webgpu_pipeline pipeline = 1271ctx -> shader_lib -> get_ssm_scan_pipeline ( shader_lib_ctx , xbc_overlap , a_overlap , ids_overlap ); 1272auto * decisions = static_cast < ggml_webgpu_ssm_scan_shader_decisions *> ( pipeline . context . get ()); 1273xbc_overlap = decisions -> xbc_overlap ; 1274a_overlap = decisions -> a_overlap ; 1275ids_overlap = decisions -> ids_overlap ; 1276 1277uint32_t offset_x = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )); 1278uint32_t offset_dt = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src2 ) / ggml_type_size ( src2 -> type )); 1279uint32_t offset_A = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src3 ) / ggml_type_size ( src3 -> type )); 1280uint32_t offset_B = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src4 ) / ggml_type_size ( src4 -> type )); 1281uint32_t offset_C = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src5 ) / ggml_type_size ( src5 -> type )); 1282uint32_t offset_ids = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src6 ) / ggml_type_size ( src6 -> type )); 1283size_t xbc_bind_offset = 0 ; 1284size_t xbc_bind_size = 0 ; 1285if ( xbc_overlap ) { 1286xbc_bind_offset = xbc_merged_range . offset ; 1287xbc_bind_size = xbc_merged_range . size ; 1288offset_x = ggml_webgpu_tensor_merged_element_offset ( src1 , xbc_merged_range ); 1289offset_dt = ggml_webgpu_tensor_merged_element_offset ( src2 , xbc_merged_range ); 1290if ( a_overlap ) { 1291offset_A = ggml_webgpu_tensor_merged_element_offset ( src3 , xbc_merged_range ); 1292} 1293offset_B = ggml_webgpu_tensor_merged_element_offset ( src4 , xbc_merged_range ); 1294offset_C = ggml_webgpu_tensor_merged_element_offset ( src5 , xbc_merged_range ); 1295if ( ids_overlap ) { 1296offset_ids = ggml_webgpu_tensor_merged_element_offset ( src6 , xbc_merged_range ); 1297} 1298} 1299 1300std::vector < uint32_t > params = { 1301(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )), 1302offset_x , 1303offset_dt , 1304offset_A , 1305offset_B , 1306offset_C , 1307offset_ids , 1308(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 1309 1310(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 1311(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 1312(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 1313 1314(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 1315(uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )), 1316(uint32_t) ( src1 -> nb [ 3 ] / ggml_type_size ( src1 -> type )), 1317 1318(uint32_t) ( src2 -> nb [ 1 ] / ggml_type_size ( src2 -> type )), 1319(uint32_t) ( src2 -> nb [ 2 ] / ggml_type_size ( src2 -> type )), 1320 1321(uint32_t) src3 -> ne [ 0 ], 1322(uint32_t) ( src3 -> nb [ 1 ] / ggml_type_size ( src3 -> type )), 1323 1324(uint32_t) ( src4 -> nb [ 1 ] / ggml_type_size ( src4 -> type )), 1325(uint32_t) ( src4 -> nb [ 2 ] / ggml_type_size ( src4 -> type )), 1326(uint32_t) ( src4 -> nb [ 3 ] / ggml_type_size ( src4 -> type )), 1327 1328(uint32_t) ( src5 -> nb [ 1 ] / ggml_type_size ( src5 -> type )), 1329(uint32_t) ( src5 -> nb [ 2 ] / ggml_type_size ( src5 -> type )), 1330(uint32_t) ( src5 -> nb [ 3 ] / ggml_type_size ( src5 -> type )), 1331 1332(uint32_t) src0 -> ne [ 0 ], 1333(uint32_t) src0 -> ne [ 1 ], 1334(uint32_t) src0 -> ne [ 2 ], 1335(uint32_t) src4 -> ne [ 1 ], 1336(uint32_t) src1 -> ne [ 2 ], 1337(uint32_t) ggml_nelements ( src1 ), 1338(uint32_t) ggml_get_op_params_i32 ( dst , 0 ), 1339}; 1340 1341std::vector < wgpu::BindGroupEntry > entries = { 1342ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 ), 1343}; 1344if ( xbc_overlap ) { 1345entries . push_back ( 1346ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( src1 ), xbc_bind_offset , xbc_bind_size )); 1347if ( ids_overlap ) { 1348if (! a_overlap ) { 1349entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , src3 )); 1350} 1351entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , a_overlap ? 2 : 3 , dst )); 1352} else if ( a_overlap ) { 1353entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , src6 )); 1354entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 3 , dst )); 1355} else { 1356entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , src3 )); 1357entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 3 , src6 )); 1358entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 4 , dst )); 1359} 1360} else { 1361entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , src1 )); 1362entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , src2 )); 1363entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 3 , src3 )); 1364entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 4 , src4 )); 1365entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 5 , src5 )); 1366entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 6 , src6 )); 1367entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 7 , dst )); 1368} 1369 1370const uint32_t total_wg = (uint32_t) ( src0 -> ne [ 1 ] * src0 -> ne [ 2 ] * src1 -> ne [ 3 ]); 1371const uint32_t max_wg_per_dim = ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension ; 1372uint32_t wg_x ; 1373uint32_t wg_y ; 1374compute_2d_workgroups ( total_wg , max_wg_per_dim , wg_x , wg_y ); 1375 1376return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 1377} 1378 1379static webgpu_encoded_op ggml_webgpu_gated_delta_net (webgpu_context & ctx , 1380ggml_tensor * src0 , 1381ggml_tensor * src1 , 1382ggml_tensor * src2 , 1383ggml_tensor * src3 , 1384ggml_tensor * src4 , 1385ggml_tensor * src5 , 1386ggml_tensor * dst ) { 1387ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1388shader_lib_ctx . src0 = src0 ; 1389shader_lib_ctx . src1 = src1 ; 1390shader_lib_ctx . src2 = src2 ; 1391shader_lib_ctx . src3 = src3 ; 1392shader_lib_ctx . src4 = src4 ; 1393shader_lib_ctx . dst = dst ; 1394shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1395 1396webgpu_pipeline pipeline = ctx -> shader_lib -> get_gated_delta_net_pipeline ( shader_lib_ctx ); 1397 1398const uint32_t s_v = (uint32_t) src2 -> ne [ 0 ]; 1399const uint32_t h = (uint32_t) src2 -> ne [ 1 ]; 1400const uint32_t n_tokens = (uint32_t) src2 -> ne [ 2 ]; 1401const uint32_t n_seqs = (uint32_t) src2 -> ne [ 3 ]; 1402const uint32_t K = (uint32_t) ggml_get_op_params_i32 ( dst , 0 ); 1403const float scale = 1.0f / sqrtf ((float) s_v ); 1404uint32_t scale_u32 ; 1405memcpy ( & scale_u32 , & scale , sizeof ( scale_u32 )); 1406 1407std::vector < uint32_t > params = { 1408h , 1409n_tokens , 1410n_seqs , 1411s_v * h * n_tokens * n_seqs , 1412 1413(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 1414(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 1415(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 1416 1417(uint32_t) ( src2 -> nb [ 1 ] / ggml_type_size ( src2 -> type )), 1418(uint32_t) ( src2 -> nb [ 2 ] / ggml_type_size ( src2 -> type )), 1419(uint32_t) ( src2 -> nb [ 3 ] / ggml_type_size ( src2 -> type )), 1420 1421(uint32_t) ( src4 -> nb [ 1 ] / ggml_type_size ( src4 -> type )), 1422(uint32_t) ( src4 -> nb [ 2 ] / ggml_type_size ( src4 -> type )), 1423(uint32_t) ( src4 -> nb [ 3 ] / ggml_type_size ( src4 -> type )), 1424 1425(uint32_t) src0 -> ne [ 1 ], 1426(uint32_t) ( src2 -> ne [ 3 ] / src0 -> ne [ 3 ]), 1427K , 1428scale_u32 , 1429}; 1430 1431std::vector < wgpu::BindGroupEntry > entries = { 1432ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 ), ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , src1 ), 1433ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , src2 ), ggml_webgpu_make_tensor_bind_group_entry ( ctx , 3 , src3 ), 1434ggml_webgpu_make_tensor_bind_group_entry ( ctx , 4 , src4 ), ggml_webgpu_make_tensor_bind_group_entry ( ctx , 5 , src5 ), 1435ggml_webgpu_make_tensor_bind_group_entry ( ctx , 6 , dst ), 1436}; 1437 1438return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , h , n_seqs ); 1439} 1440 1441static std::optional < webgpu_encoded_op > ggml_webgpu_set_rows (webgpu_context & ctx , 1442ggml_tensor * src , 1443ggml_tensor * idx , 1444ggml_tensor * dst ) { 1445// For set rows specifically, we need to check if src and idx are empty 1446// tensors. 1447if ( ggml_is_empty ( src ) || ggml_is_empty ( idx )) { 1448return std:: nullopt ; 1449} 1450 1451ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1452shader_lib_ctx . src0 = src ; 1453shader_lib_ctx . src1 = idx ; 1454shader_lib_ctx . dst = dst ; 1455shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1456 1457webgpu_pipeline pipeline = ctx -> shader_lib -> get_set_rows_pipeline ( shader_lib_ctx ); 1458 1459auto * decisions = static_cast < ggml_webgpu_set_rows_shader_decisions *> ( pipeline . context . get ()); 1460 1461std::vector < uint32_t > params = { 1462(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )), 1463(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , idx ) / ggml_type_size ( idx -> type )), 1464(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 1465// Convert byte-strides to element-strides 1466(uint32_t) ( src -> nb [ 1 ] / ggml_type_size ( src -> type )), (uint32_t) ( src -> nb [ 2 ] / ggml_type_size ( src -> type )), 1467(uint32_t) ( src -> nb [ 3 ] / ggml_type_size ( src -> type )), (uint32_t) ( idx -> nb [ 0 ] / ggml_type_size ( idx -> type )), 1468(uint32_t) ( idx -> nb [ 1 ] / ggml_type_size ( idx -> type )), (uint32_t) ( idx -> nb [ 2 ] / ggml_type_size ( idx -> type )), 1469(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), (uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 1470(uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 1471// Shape of src 1472(uint32_t) src -> ne [ 0 ], (uint32_t) src -> ne [ 1 ], (uint32_t) src -> ne [ 2 ], (uint32_t) src -> ne [ 3 ], 1473// Shape of idx 1474(uint32_t) ( idx -> ne [ 1 ]), (uint32_t) ( idx -> ne [ 2 ]) 1475}; 1476 1477std::vector < wgpu::BindGroupEntry > entries ; 1478entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src )); 1479entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , idx )); 1480entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , dst )); 1481 1482if ( decisions -> i64_idx ) { 1483entries . push_back ( ggml_webgpu_make_bind_group_entry ( 3 , ctx -> set_rows_dev_error_buf , 0 , 1484ctx -> set_rows_dev_error_buf . GetSize ())); 1485} 1486 1487uint32_t threads ; 1488if ( ggml_is_quantized ( dst -> type )) { 1489const uint32_t blocks_per_row = src -> ne [ 0 ] / ggml_blck_size ( dst -> type ); 1490threads = 1491( src -> ne [ 1 ] * src -> ne [ 2 ] * src -> ne [ 3 ]) * ( decisions -> pair_blocks ? ( blocks_per_row / 2 ) : blocks_per_row ); 1492} else if ( decisions -> vec4 ) { 1493threads = ( src -> ne [ 1 ] * src -> ne [ 2 ] * src -> ne [ 3 ]) * ( src -> ne [ 0 ] / 4 ); 1494} else { 1495threads = src -> ne [ 0 ] * src -> ne [ 1 ] * src -> ne [ 2 ] * src -> ne [ 3 ]; 1496} 1497uint32_t wg_x = CEIL_DIV ( threads , decisions -> wg_size ); 1498return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , 1 ); 1499} 1500 1501// Workgroup size is a common constant 1502static std::vector < wgpu::ConstantEntry > ggml_webgpu_wg_size_entry (uint32_t wg_size ) { 1503std::vector < wgpu::ConstantEntry > constants ( 1 ); 1504constants [ 0 ]. key = "wg_size" ; 1505constants [ 0 ]. value = wg_size ; 1506return constants ; 1507} 1508 1509static webgpu_encoded_op ggml_webgpu_get_rows (webgpu_context & ctx , 1510ggml_tensor * src , 1511ggml_tensor * idx , 1512ggml_tensor * dst ) { 1513const bool float_parallel = src -> type == GGML_TYPE_F32 || src -> type == GGML_TYPE_F16 || src -> type == GGML_TYPE_I32 ; 1514 1515ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1516shader_lib_ctx . src0 = src ; 1517shader_lib_ctx . src1 = nullptr ; 1518shader_lib_ctx . dst = dst ; 1519shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1520 1521webgpu_pipeline pipeline = ctx -> shader_lib -> get_get_rows_pipeline ( shader_lib_ctx ); 1522auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 1523 1524std::vector < uint32_t > params = { (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )), 1525(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , idx ) / ggml_type_size ( idx -> type )), 1526(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 1527(uint32_t) ( src -> nb [ 1 ] / ggml_type_size ( src -> type )), 1528(uint32_t) ( src -> nb [ 2 ] / ggml_type_size ( src -> type )), 1529(uint32_t) ( src -> nb [ 3 ] / ggml_type_size ( src -> type )), 1530(uint32_t) ( idx -> nb [ 0 ] / ggml_type_size ( idx -> type )), 1531(uint32_t) ( idx -> nb [ 1 ] / ggml_type_size ( idx -> type )), 1532(uint32_t) ( idx -> nb [ 2 ] / ggml_type_size ( idx -> type )), 1533(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 1534(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 1535(uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 1536(uint32_t) dst -> ne [ 0 ], 1537(uint32_t) dst -> ne [ 1 ], 1538(uint32_t) dst -> ne [ 2 ], 1539(uint32_t) dst -> ne [ 3 ], 1540(uint32_t) ( idx -> ne [ 1 ]), 1541(uint32_t) ( idx -> ne [ 2 ]) }; 1542 1543std::vector < wgpu::BindGroupEntry > entries = { ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src ), 1544ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , idx ), 1545ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , dst ) }; 1546 1547uint32_t blocks_per_row = (uint32_t) ( dst -> ne [ 0 ] / ( src -> type == GGML_TYPE_F32 && dst -> ne [ 0 ] % 4 == 0 ? 4 : 1 )); 1548uint32_t total_rows = (uint32_t) ( dst -> ne [ 1 ] * dst -> ne [ 2 ] * dst -> ne [ 3 ]); 1549uint32_t total_threads = float_parallel ? blocks_per_row * total_rows : total_rows ; 1550uint32_t wg_x = CEIL_DIV ( total_threads , decisions -> wg_size ); 1551 1552return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x ); 1553} 1554 1555static void ggml_webgpu_quantize_q8_dispatch (webgpu_context & ctx , 1556ggml_tensor * src0 , 1557ggml_tensor * src1 , 1558ggml_tensor * dst , 1559std::vector < webgpu_dispatch_desc > & dispatches ) { 1560ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1561 1562shader_lib_ctx . src0 = src0 ; 1563shader_lib_ctx . src1 = src1 ; 1564shader_lib_ctx . dst = dst ; 1565shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1566shader_lib_ctx . supports_subgroups = ctx -> global_ctx -> capabilities . supports_subgroups ; 1567 1568webgpu_pipeline qq8_pipeline = ctx -> shader_lib -> get_quantize_q8_pipeline ( shader_lib_ctx ); 1569 1570// quantize_q8 pipeline 1571const size_t dst_offset = ggml_webgpu_tensor_offset ( dst ); 1572const size_t q8_src1_align_offset = ROUNDUP_POW2 ( 1573dst_offset + ggml_nbytes ( dst ), ctx -> global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ); 1574const size_t q8_src1_binding_size = ROUNDUP_POW2 ( 1575src1 -> ne [ 3 ] * src1 -> ne [ 2 ] * src1 -> ne [ 1 ] * ( 36 /* sizeof(q8_1) */ * ( src1 -> ne [ 0 ] / /* block_size */ 32 )), 1576WEBGPU_STORAGE_BUF_BINDING_MULT ); 1577 1578std::vector < uint32_t > q8_params = { 1579(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )), 1580(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 1581(uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )), 1582(uint32_t) ( src1 -> nb [ 3 ] / ggml_type_size ( src1 -> type )), 1583(uint32_t) src1 -> ne [ 0 ], 1584(uint32_t) src1 -> ne [ 1 ], 1585(uint32_t) src1 -> ne [ 2 ], 1586(uint32_t) src1 -> ne [ 3 ], 1587}; 1588 1589std::vector < wgpu::BindGroupEntry > q8_entries = { 1590ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src1 ), 1591ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( dst ), q8_src1_align_offset , q8_src1_binding_size ) 1592}; 1593 1594auto q8_decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( qq8_pipeline . context . get ()); 1595 1596uint32_t q8_wg_size = q8_decisions -> wg_size ; 1597uint32_t q8_wg_x = 1 ; 1598uint32_t q8_wg_y = 1 ; 1599const uint32_t wg_per_vec = ( src0 -> ne [ 0 ] / 4 + ( q8_wg_size - 1 )) / q8_wg_size ; 1600const uint32_t q8_total_wg = src1 -> ne [ 1 ] * src1 -> ne [ 2 ] * src1 -> ne [ 3 ] * wg_per_vec ; 1601const uint32_t max_wg_per_dim = ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension ; 1602compute_2d_workgroups ( q8_total_wg , max_wg_per_dim , q8_wg_x , q8_wg_y ); 1603 1604dispatches . push_back ({ 1605qq8_pipeline , std:: move ( q8_params ), std:: move ( q8_entries ), { q8_wg_x , q8_wg_y } 1606}); 1607} 1608 1609static webgpu_encoded_op ggml_webgpu_mul_mat (webgpu_context & ctx , 1610ggml_tensor * src0 , 1611ggml_tensor * src1 , 1612ggml_tensor * dst ) { 1613// Determine if this is a mat-vec operation 1614bool use_mat_vec = ( dst -> ne [ 1 ] <= 4 ); 1615 1616// use MMVQ path for mat-vec 1617bool use_mmvq = ggml_webgpu_can_use_mmvq ( src0 , src1 , ctx -> global_ctx -> capabilities . supports_dot_product , 1618ctx -> global_ctx -> vendor ); 1619 1620ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1621 1622shader_lib_ctx . src0 = src0 ; 1623shader_lib_ctx . src1 = src1 ; 1624shader_lib_ctx . dst = dst ; 1625shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1626shader_lib_ctx . supports_subgroups = ctx -> global_ctx -> capabilities . supports_subgroups ; 1627shader_lib_ctx . supports_subgroup_matrix = ctx -> global_ctx -> capabilities . supports_subgroup_matrix ; 1628shader_lib_ctx . sg_mat_m = ctx -> global_ctx -> capabilities . sg_mat_m ; 1629shader_lib_ctx . sg_mat_n = ctx -> global_ctx -> capabilities . sg_mat_n ; 1630shader_lib_ctx . sg_mat_k = ctx -> global_ctx -> capabilities . sg_mat_k ; 1631shader_lib_ctx . min_subgroup_size = ctx -> global_ctx -> capabilities . min_subgroup_size ; 1632shader_lib_ctx . max_subgroup_size = ctx -> global_ctx -> capabilities . max_subgroup_size ; 1633shader_lib_ctx . supports_dot_product = ctx -> global_ctx -> capabilities . supports_dot_product ; 1634shader_lib_ctx . vendor = ctx -> global_ctx -> vendor ; 1635 1636// Get or create pipeline 1637webgpu_pipeline pipeline ; 1638std::vector < webgpu_dispatch_desc > dispatches ; 1639const bool src_overlap = ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , src0 , src1 ) && ! use_mmvq ; 1640 1641if ( use_mat_vec ) { 1642if ( use_mmvq ) { 1643ggml_webgpu_quantize_q8_dispatch ( ctx , src0 , src1 , dst , dispatches ); 1644} 1645pipeline = ctx -> shader_lib -> get_mul_mat_vec_pipeline ( shader_lib_ctx , src_overlap ); 1646} else { 1647pipeline = ctx -> shader_lib -> get_mul_mat_fast_pipeline ( shader_lib_ctx , src_overlap ); 1648} 1649 1650uint32_t offset_src0 = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )); 1651uint32_t offset_src1 = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )); 1652size_t merged_offset = 0 ; 1653size_t merged_size = 0 ; 1654if ( src_overlap ) { 1655const ggml_webgpu_merged_binding_range merged_range = 1656ggml_webgpu_tensor_merged_binding_range ( ctx , { src0 , src1 }); 1657merged_offset = merged_range . offset ; 1658merged_size = merged_range . size ; 1659offset_src0 = ggml_webgpu_tensor_merged_element_offset ( src0 , merged_range ); 1660offset_src1 = ggml_webgpu_tensor_merged_element_offset ( src1 , merged_range ); 1661} 1662 1663// Build params 1664std::vector < uint32_t > params = { offset_src0 , 1665offset_src1 , 1666(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 1667(uint32_t) dst -> ne [ 0 ], 1668(uint32_t) dst -> ne [ 1 ], 1669(uint32_t) src0 -> ne [ 0 ], 1670(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 1671(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 1672(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 1673(uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )), 1674(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 1675(uint32_t) ( src1 -> nb [ 3 ] / ggml_type_size ( src1 -> type )), 1676(uint32_t) src0 -> ne [ 2 ], 1677(uint32_t) src0 -> ne [ 3 ], 1678(uint32_t) ( src1 -> ne [ 2 ] / src0 -> ne [ 2 ]), 1679(uint32_t) ( src1 -> ne [ 3 ] / src0 -> ne [ 3 ]) }; 1680 1681// Build bind group entries 1682std::vector < wgpu::BindGroupEntry > entries = {}; 1683if ( use_mmvq ) { 1684entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 )); 1685auto & mmvq_qq8_entry = dispatches [ 0 ]. bind_group_entries [ 1 ]; 1686entries . push_back ( ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( dst ), mmvq_qq8_entry . offset , 1687mmvq_qq8_entry . size )); 1688entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , dst )); 1689} else if ( src_overlap ) { 1690entries . push_back ( 1691ggml_webgpu_make_bind_group_entry ( 0 , ggml_webgpu_tensor_buf ( src0 ), merged_offset , merged_size )); 1692entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst )); 1693} else { 1694entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 )); 1695entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , src1 )); 1696entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , dst )); 1697} 1698 1699// Calculate workgroup dimensions 1700uint32_t wg_x = 1 ; 1701uint32_t wg_y = 1 ; 1702const uint32_t max_wg_per_dim = ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension ; 1703 1704if ( use_mat_vec ) { 1705auto * decisions = static_cast < ggml_webgpu_mul_mat_vec_shader_decisions *> ( pipeline . context . get ()); 1706 1707uint32_t batches = dst -> ne [ 2 ] * dst -> ne [ 3 ]; 1708uint32_t output_groups = CEIL_DIV ( dst -> ne [ 0 ], decisions -> outputs_per_wg ); 1709uint32_t total_wg = output_groups * batches ; 1710compute_2d_workgroups ( total_wg , max_wg_per_dim , wg_x , wg_y ); 1711} else { 1712auto * decisions = static_cast < ggml_webgpu_mul_mat_shader_decisions *> ( pipeline . context . get ()); 1713 1714// Fast-path tiled/subgroup calculations 1715uint32_t wg_m ; 1716uint32_t wg_n ; 1717if ( decisions -> use_subgroup_matrix ) { 1718uint32_t wg_m_sg_tile = 1719decisions -> subgroup_m * decisions -> subgroup_matrix_m * ctx -> global_ctx -> capabilities . sg_mat_m ; 1720wg_m = CEIL_DIV ( dst -> ne [ 0 ], wg_m_sg_tile ); 1721uint32_t wg_n_sg_tile = 1722decisions -> subgroup_n * decisions -> subgroup_matrix_n * ctx -> global_ctx -> capabilities . sg_mat_n ; 1723wg_n = CEIL_DIV ( dst -> ne [ 1 ], wg_n_sg_tile ); 1724} else { 1725uint32_t tile_m_s = decisions -> tile_m * decisions -> wg_size_m ; 1726uint32_t tile_n_s = decisions -> tile_n * decisions -> wg_size_n ; 1727wg_m = CEIL_DIV ( dst -> ne [ 0 ], tile_m_s ); 1728wg_n = CEIL_DIV ( dst -> ne [ 1 ], tile_n_s ); 1729} 1730uint32_t total_wg = wg_m * wg_n * dst -> ne [ 2 ] * dst -> ne [ 3 ]; 1731compute_2d_workgroups ( total_wg , max_wg_per_dim , wg_x , wg_y ); 1732} 1733 1734dispatches . push_back ({ 1735pipeline , std:: move ( params ), std:: move ( entries ), { wg_x , wg_y } 1736}); 1737 1738return ggml_backend_webgpu_build_multi ( ctx , dispatches ); 1739} 1740 1741static webgpu_encoded_op ggml_webgpu_mul_mat_id_vec (webgpu_context & ctx , 1742ggml_tensor * src0 , 1743ggml_tensor * src1 , 1744ggml_tensor * src2 , 1745ggml_tensor * dst ) { 1746const uint32_t param_n_expert = (uint32_t) src0 -> ne [ 2 ]; 1747const uint32_t param_n_expert_used = (uint32_t) dst -> ne [ 1 ]; 1748 1749ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1750shader_lib_ctx . src0 = src0 ; 1751shader_lib_ctx . src1 = src1 ; 1752shader_lib_ctx . src2 = src2 ; 1753shader_lib_ctx . dst = dst ; 1754shader_lib_ctx . supports_subgroups = ctx -> global_ctx -> capabilities . supports_subgroups ; 1755shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1756 1757webgpu_pipeline pipeline = ctx -> shader_lib -> get_mul_mat_id_vec_pipeline ( shader_lib_ctx ); 1758 1759std::vector < uint32_t > params = { 1760(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )), 1761(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )), 1762(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src2 ) / ggml_type_size ( src2 -> type )), 1763(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 1764(uint32_t) src0 -> ne [ 0 ], 1765(uint32_t) src0 -> ne [ 1 ], 1766param_n_expert , 1767param_n_expert_used , 1768(uint32_t) src1 -> ne [ 1 ], 1769(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 1770(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 1771(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 1772(uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )), 1773}; 1774 1775std::vector < wgpu::BindGroupEntry > entries = { 1776ggml_webgpu_make_bind_group_entry ( 0 , ggml_webgpu_tensor_buf ( src0 ), ggml_webgpu_tensor_align_offset ( ctx , src0 ), 1777ggml_webgpu_tensor_binding_size ( ctx , src0 )), 1778ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( src1 ), ggml_webgpu_tensor_align_offset ( ctx , src1 ), 1779ggml_webgpu_tensor_binding_size ( ctx , src1 )), 1780ggml_webgpu_make_bind_group_entry ( 2 , ggml_webgpu_tensor_buf ( src2 ), ggml_webgpu_tensor_align_offset ( ctx , src2 ), 1781ggml_webgpu_tensor_binding_size ( ctx , src2 )), 1782ggml_webgpu_make_bind_group_entry ( 3 , ggml_webgpu_tensor_buf ( dst ), ggml_webgpu_tensor_align_offset ( ctx , dst ), 1783ggml_webgpu_tensor_binding_size ( ctx , dst )), 1784}; 1785 1786uint32_t wg_x = 1 ; 1787uint32_t wg_y = 1 ; 1788 1789auto * decisions = static_cast < ggml_webgpu_mul_mat_vec_shader_decisions *> ( pipeline . context . get ()); 1790 1791const uint32_t max_wg_per_dim = ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension ; 1792uint32_t output_groups = CEIL_DIV ( dst -> ne [ 0 ], decisions -> outputs_per_wg ); 1793uint32_t total_wg = output_groups * param_n_expert_used ; 1794compute_2d_workgroups ( total_wg , max_wg_per_dim , wg_x , wg_y ); 1795 1796return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 1797} 1798 1799static webgpu_encoded_op ggml_webgpu_mul_mat_id (webgpu_context & ctx , 1800ggml_tensor * src0 , 1801ggml_tensor * src1 , 1802ggml_tensor * src2 , 1803ggml_tensor * dst ) { 1804// we can use mat-vec fast path 1805if ( dst -> ne [ 2 ] == 1 ) { 1806return ggml_webgpu_mul_mat_id_vec ( ctx , src0 , src1 , src2 , dst ); 1807} 1808 1809ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1810shader_lib_ctx . src0 = src0 ; 1811shader_lib_ctx . src1 = src1 ; 1812shader_lib_ctx . src2 = src2 ; 1813shader_lib_ctx . dst = dst ; 1814shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1815 1816// Get or create pipeline 1817webgpu_pipeline gather_pipeline ; 1818webgpu_pipeline main_pipeline ; 1819 1820std::vector < webgpu_dispatch_desc > dispatches ; 1821 1822gather_pipeline = ctx -> shader_lib -> get_mul_mat_id_gather_pipeline ( shader_lib_ctx ); 1823main_pipeline = ctx -> shader_lib -> get_mul_mat_id_pipeline ( shader_lib_ctx ); 1824 1825const uint32_t param_n_expert = (uint32_t) src0 -> ne [ 2 ]; 1826const uint32_t param_n_expert_used = (uint32_t) dst -> ne [ 1 ]; 1827const uint32_t param_n_tokens = (uint32_t) dst -> ne [ 2 ]; 1828 1829// params for mul_mat_id_gather.wgsl 1830std::vector < uint32_t > gather_params = { 1831(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src2 ) / ggml_type_size ( src2 -> type )), 1832param_n_expert , 1833param_n_expert_used , 1834param_n_tokens , 1835(uint32_t) ( src2 -> nb [ 1 ] / ggml_type_size ( src2 -> type )), 1836}; 1837 1838const size_t dst_offset = ggml_webgpu_tensor_offset ( dst ); 1839const size_t gathered_buf_nbytes = src0 -> ne [ 2 ] * src1 -> ne [ 2 ] * sizeof (uint32_t); 1840 1841const size_t gathered_expert_used_align_offset = ROUNDUP_POW2 ( 1842dst_offset + ggml_nbytes ( dst ), ctx -> global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ); 1843const size_t gathered_tokens_align_offset = 1844ROUNDUP_POW2 ( gathered_expert_used_align_offset + gathered_buf_nbytes , 1845ctx -> global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ); 1846const size_t gathered_count_ids_align_offset = 1847ROUNDUP_POW2 ( gathered_tokens_align_offset + gathered_buf_nbytes , 1848ctx -> global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ); 1849 1850const size_t gathered_binding_size = ROUNDUP_POW2 ( gathered_buf_nbytes , WEBGPU_STORAGE_BUF_BINDING_MULT ); 1851const size_t gathered_count_ids_binding_size = 1852ROUNDUP_POW2 ( src0 -> ne [ 2 ] * sizeof (uint32_t), WEBGPU_STORAGE_BUF_BINDING_MULT ); 1853 1854// bind group entries for mul_mat_id_gather.wgsl 1855std::vector < wgpu::BindGroupEntry > gather_entries = { 1856ggml_webgpu_make_bind_group_entry ( 0 , ggml_webgpu_tensor_buf ( src2 ), ggml_webgpu_tensor_align_offset ( ctx , src2 ), 1857ggml_webgpu_tensor_binding_size ( ctx , src2 )), 1858ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( dst ), gathered_expert_used_align_offset , 1859gathered_binding_size ), 1860ggml_webgpu_make_bind_group_entry ( 2 , ggml_webgpu_tensor_buf ( dst ), gathered_tokens_align_offset , 1861gathered_binding_size ), 1862ggml_webgpu_make_bind_group_entry ( 3 , ggml_webgpu_tensor_buf ( dst ), gathered_count_ids_align_offset , 1863gathered_count_ids_binding_size ), 1864}; 1865 1866// n_expert is much less than maxComputeWorkgroupsPerDimension (e.g., n_exeprt=256 at Qwen3.5-35B-A3B) 1867const uint32_t gather_wg_x = param_n_expert ; 1868 1869dispatches . push_back ({ 1870gather_pipeline , std:: move ( gather_params ), std:: move ( gather_entries ), { gather_wg_x , 1 } 1871}); 1872 1873// params for mul_mat_id.wgsl 1874std::vector < uint32_t > main_params = { 1875(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )), 1876(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )), 1877(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 1878(uint32_t) src0 -> ne [ 0 ], 1879(uint32_t) src0 -> ne [ 1 ], 1880param_n_expert , 1881param_n_expert_used , 1882param_n_tokens , 1883(uint32_t) src1 -> ne [ 1 ], 1884(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 1885(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 1886(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 1887(uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )), 1888}; 1889 1890// bind group entries for mul_mat_id.wgsl 1891std::vector < wgpu::BindGroupEntry > main_entries = { 1892ggml_webgpu_make_bind_group_entry ( 0 , ggml_webgpu_tensor_buf ( src0 ), ggml_webgpu_tensor_align_offset ( ctx , src0 ), 1893ggml_webgpu_tensor_binding_size ( ctx , src0 )), 1894ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( src1 ), ggml_webgpu_tensor_align_offset ( ctx , src1 ), 1895ggml_webgpu_tensor_binding_size ( ctx , src1 )), 1896ggml_webgpu_make_bind_group_entry ( 2 , ggml_webgpu_tensor_buf ( dst ), ggml_webgpu_tensor_align_offset ( ctx , dst ), 1897ggml_webgpu_tensor_binding_size ( ctx , dst )), 1898ggml_webgpu_make_bind_group_entry ( 3 , ggml_webgpu_tensor_buf ( dst ), gathered_expert_used_align_offset , 1899gathered_binding_size ), 1900ggml_webgpu_make_bind_group_entry ( 4 , ggml_webgpu_tensor_buf ( dst ), gathered_tokens_align_offset , 1901gathered_binding_size ), 1902ggml_webgpu_make_bind_group_entry ( 5 , ggml_webgpu_tensor_buf ( dst ), gathered_count_ids_align_offset , 1903gathered_count_ids_binding_size ), 1904}; 1905 1906// Calculate workgroup dimensions 1907uint32_t wg_x = 1 ; 1908uint32_t wg_y = 1 ; 1909 1910auto * main_decisions = static_cast < ggml_webgpu_mul_mat_shader_decisions *> ( main_pipeline . context . get ()); 1911 1912uint32_t wg_m ; 1913 1914uint32_t tile_m_s = main_decisions -> tile_m * main_decisions -> wg_size_m ; 1915uint32_t tile_n_s = main_decisions -> tile_n * main_decisions -> wg_size_n ; 1916wg_m = CEIL_DIV ( dst -> ne [ 0 ], tile_m_s ); 1917uint32_t total_gathered = dst -> ne [ 1 ] * dst -> ne [ 2 ]; 1918uint32_t max_active_experts = std:: min ((uint32_t) src0 -> ne [ 2 ], total_gathered ); 1919uint32_t max_wg_n = CEIL_DIV ( total_gathered , tile_n_s ) + max_active_experts ; 1920uint32_t total_wg = wg_m * max_wg_n ; 1921 1922compute_2d_workgroups ( total_wg , ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension , wg_x , wg_y ); 1923 1924dispatches . push_back ({ 1925main_pipeline , std:: move ( main_params ), std:: move ( main_entries ), { wg_x , wg_y } 1926}); 1927 1928return ggml_backend_webgpu_build_multi ( ctx , dispatches ); 1929} 1930 1931struct ggml_webgpu_flash_attn_op { 1932ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 1933std::vector < uint32_t > params ; 1934std::vector < wgpu::BindGroupEntry > entries ; 1935size_t kv_bind_offset = 0 ; 1936size_t kv_bind_size = 0 ; 1937bool has_mask = false; 1938bool has_sinks = false; 1939bool kv_overlap = false; 1940}; 1941 1942static bool ggml_webgpu_flash_attn_use_vec_path ( const webgpu_global_context & global_ctx , 1943const ggml_tensor * Q , 1944const ggml_tensor * K , 1945const ggml_tensor * V ) { 1946const size_t storage_offset_alignment = global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ; 1947 1948const bool k_float_vec4_aligned = ( K -> type != GGML_TYPE_F16 && K -> type != GGML_TYPE_F32 ) || 1949ggml_webgpu_flash_attn_float_vec4_aligned ( K , storage_offset_alignment ); 1950const bool v_float_vec4_aligned = ( V -> type != GGML_TYPE_F16 && V -> type != GGML_TYPE_F32 ) || 1951ggml_webgpu_flash_attn_float_vec4_aligned ( V , storage_offset_alignment ); 1952 1953const uint32_t k_vec_head_align = 1954ggml_is_quantized ( K -> type ) ? ggml_blck_size ( K -> type ) : GGML_WEBGPU_FLASH_ATTN_TILE_KV_VEC_WIDTH ; 1955const uint32_t v_vec_head_align = 1956ggml_is_quantized ( V -> type ) ? ggml_blck_size ( V -> type ) : GGML_WEBGPU_FLASH_ATTN_TILE_KV_VEC_WIDTH ; 1957const bool kv_vec_head_dims_aligned = Q -> ne [ 0 ] % k_vec_head_align == 0 && V -> ne [ 0 ] % v_vec_head_align == 0 ; 1958 1959return global_ctx -> capabilities . supports_subgroups && ( Q -> ne [ 1 ] < GGML_WEBGPU_FLASH_ATTN_VEC_MAX_SEQ_LEN ) && 1960kv_vec_head_dims_aligned && k_float_vec4_aligned && v_float_vec4_aligned ; 1961} 1962 1963static ggml_webgpu_flash_attn_op ggml_webgpu_flash_attn_prepare (webgpu_context & ctx , 1964ggml_tensor * Q , 1965ggml_tensor * K , 1966ggml_tensor * V , 1967ggml_tensor * mask , 1968ggml_tensor * sinks , 1969ggml_tensor * dst ) { 1970float scale = ggml_get_op_params_f32 ( dst , 0 ); 1971float max_bias = ggml_get_op_params_f32 ( dst , 1 ); 1972float logit_softcap = ggml_get_op_params_f32 ( dst , 2 ); 1973if ( logit_softcap != 0.0f ) { 1974scale /= logit_softcap ; 1975} 1976float n_head_log2 = float( 1u << (uint32_t) floor ( log2 ( Q -> ne [ 2 ]))); 1977float m0 = powf ( 2.0f , - ( max_bias ) / n_head_log2 ); 1978float m1 = powf ( 2.0f , - ( max_bias / 2.0f ) / n_head_log2 ); 1979 1980ggml_webgpu_flash_attn_op op = {}; 1981op . shader_lib_ctx . src0 = Q ; 1982op . shader_lib_ctx . src1 = K ; 1983op . shader_lib_ctx . src2 = V ; 1984op . shader_lib_ctx . src3 = mask ; 1985op . shader_lib_ctx . src4 = sinks ; 1986op . shader_lib_ctx . dst = dst ; 1987op . shader_lib_ctx . supports_subgroups = ctx -> global_ctx -> capabilities . supports_subgroups ; 1988op . shader_lib_ctx . supports_subgroup_matrix = ctx -> global_ctx -> capabilities . supports_subgroup_matrix ; 1989op . shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 1990op . shader_lib_ctx . wg_mem_limit_bytes = ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupStorageSize ; 1991op . shader_lib_ctx . sg_mat_m = ctx -> global_ctx -> capabilities . sg_mat_m ; 1992op . shader_lib_ctx . sg_mat_n = ctx -> global_ctx -> capabilities . sg_mat_n ; 1993op . shader_lib_ctx . sg_mat_k = ctx -> global_ctx -> capabilities . sg_mat_k ; 1994op . shader_lib_ctx . min_subgroup_size = ctx -> global_ctx -> capabilities . min_subgroup_size ; 1995op . shader_lib_ctx . max_subgroup_size = ctx -> global_ctx -> capabilities . max_subgroup_size ; 1996 1997op . has_mask = mask != nullptr ; 1998op . has_sinks = sinks != nullptr ; 1999op . kv_overlap = ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , K , V ); 2000 2001uint32_t offset_k = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , K ) / ggml_type_size ( K -> type )); 2002uint32_t offset_v = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , V ) / ggml_type_size ( V -> type )); 2003if ( op . kv_overlap ) { 2004const ggml_webgpu_merged_binding_range merged_range = ggml_webgpu_tensor_merged_binding_range ( ctx , { K , V }); 2005op . kv_bind_offset = merged_range . offset ; 2006op . kv_bind_size = merged_range . size ; 2007offset_k = ggml_webgpu_tensor_merged_element_offset ( K , merged_range ); 2008offset_v = ggml_webgpu_tensor_merged_element_offset ( V , merged_range ); 2009} 2010 2011op . params = { 2012(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , Q ) / ggml_type_size ( Q -> type )), 2013offset_k , 2014offset_v , 2015op . has_mask ? (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , mask ) / ggml_type_size ( mask -> type )) : 0 , 2016op . has_sinks ? (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , sinks ) / ggml_type_size ( sinks -> type )) : 0 , 2017(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2018(uint32_t) Q -> ne [ 2 ], // number of heads 2019(uint32_t) Q -> ne [ 1 ], // sequence length (Q) 2020(uint32_t) K -> ne [ 1 ], // sequence length (K/V) 2021(uint32_t) ( Q -> nb [ 1 ] / ggml_type_size ( Q -> type )), // stride (elements/blocks) of Q in dimension 1 2022(uint32_t) ( Q -> nb [ 2 ] / ggml_type_size ( Q -> type )), // stride (elements/blocks) of Q in dimension 2 2023(uint32_t) ( Q -> nb [ 3 ] / ggml_type_size ( Q -> type )), // stride (elements/blocks) of Q in dimension 3 2024(uint32_t) ( K -> nb [ 1 ] / ggml_type_size ( K -> type )), // stride (elements/blocks) of K in dimension 1 2025(uint32_t) ( K -> nb [ 2 ] / ggml_type_size ( K -> type )), // stride (elements/blocks) of K in dimension 2 2026(uint32_t) ( K -> nb [ 3 ] / ggml_type_size ( K -> type )), // stride (elements/blocks) of K in dimension 3 2027(uint32_t) ( V -> nb [ 1 ] / ggml_type_size ( V -> type )), // stride (elements/blocks) of V in dimension 1 2028(uint32_t) ( V -> nb [ 2 ] / ggml_type_size ( V -> type )), // stride (elements/blocks) of V in dimension 2 2029(uint32_t) ( V -> nb [ 3 ] / ggml_type_size ( V -> type )), // stride (elements/blocks) of V in dimension 3 2030op . has_mask ? (uint32_t) ( mask -> nb [ 3 ] / ggml_type_size ( mask -> type )) : 0 , // stride of mask dim 3 2031(uint32_t) ( Q -> ne [ 2 ] / K -> ne [ 2 ]), // repeat factor for K/V in dim 2 (MHA/MQA/GQA) 2032ggml_webgpu_u32_from_f32 ( scale ), // scale (possibly adjusted for logit softcap) 2033ggml_webgpu_u32_from_f32 ( max_bias ), 2034ggml_webgpu_u32_from_f32 ( logit_softcap ), 2035ggml_webgpu_u32_from_f32 ( n_head_log2 ), 2036ggml_webgpu_u32_from_f32 ( m0 ), 2037ggml_webgpu_u32_from_f32 ( m1 ) 2038}; 2039op . entries = { 2040ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , Q ), 2041}; 2042if ( op . kv_overlap ) { 2043op . entries . push_back ( 2044ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( K ), op . kv_bind_offset , op . kv_bind_size )); 2045} else { 2046op . entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , K )); 2047op . entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , V )); 2048} 2049uint32_t binding_index = op . kv_overlap ? 2u : 3u ; 2050if ( op . has_mask ) { 2051op . entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , binding_index ++ , mask )); 2052} 2053if ( op . has_sinks ) { 2054op . entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , binding_index ++ , sinks )); 2055} 2056op . entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , binding_index ++ , dst )); 2057 2058return op ; 2059} 2060 2061static uint32_t ggml_webgpu_flash_attn_vec_nwg (uint32_t vec_nwg_cap , uint32_t kv_tile , uint32_t seq_len_kv ) { 2062uint32_t nwg = 1u ; 2063const uint64_t kv_span = (uint64_t) kv_tile ; 2064while (( 2u * nwg * kv_span ) < (uint64_t) seq_len_kv && nwg < vec_nwg_cap ) { 2065nwg <<= 1 ; 2066} 2067return std:: min ( nwg , vec_nwg_cap ); 2068} 2069 2070static webgpu_encoded_op ggml_webgpu_flash_attn_direct (webgpu_context & ctx , const ggml_webgpu_flash_attn_op & op ) { 2071webgpu_pipeline pipeline = ctx -> shader_lib -> get_flash_attn_pipeline ( op . shader_lib_ctx , op . kv_overlap ); 2072auto * decisions = static_cast < ggml_webgpu_flash_attn_decisions *> ( pipeline . context . get ()); 2073uint32_t wg_per_head = CEIL_DIV ( op . shader_lib_ctx . src0 -> ne [ 1 ], decisions -> q_tile ); 2074uint32_t wg_x = wg_per_head * op . shader_lib_ctx . src0 -> ne [ 2 ] * op . shader_lib_ctx . src0 -> ne [ 3 ]; 2075return ggml_backend_webgpu_build ( ctx , pipeline , op . params , op . entries , wg_x ); 2076} 2077 2078static webgpu_encoded_op ggml_webgpu_flash_attn_vec (webgpu_context & ctx , 2079ggml_tensor * Q , 2080ggml_tensor * K , 2081ggml_tensor * V , 2082ggml_tensor * mask , 2083ggml_tensor * sinks , 2084ggml_tensor * dst , 2085ggml_webgpu_flash_attn_op op ) { 2086webgpu_pipeline pipeline = ctx -> shader_lib -> get_flash_attn_vec_pipeline ( op . shader_lib_ctx , op . kv_overlap ); 2087auto * decisions = static_cast < ggml_webgpu_flash_attn_vec_decisions *> ( pipeline . context . get ()); 2088 2089wgpu::Buffer blk_buf = {}; 2090uint64_t blk_size_bytes = 0 ; 2091uint32_t blk_nblk0 = 0 ; 2092uint32_t blk_nblk1 = 0 ; 2093uint32_t blk_batch_count = 0 ; 2094 2095const uint32_t vec_nwg_cap = ctx -> global_ctx -> capabilities . min_subgroup_size ; 2096uint32_t nwg = ggml_webgpu_flash_attn_vec_nwg ( vec_nwg_cap , decisions -> kv_tile , (uint32_t) K -> ne [ 1 ]); 2097const uint64_t nrows = (uint64_t) Q -> ne [ 1 ] * Q -> ne [ 2 ] * Q -> ne [ 3 ]; 2098const bool use_vec_reduce = nwg > 1u ; 2099GGML_ASSERT ( nrows <= UINT32_MAX ); 2100 2101uint64_t tmp_stats_base = 0 ; 2102uint64_t tmp_size_bytes = 0 ; 2103wgpu::Buffer tmp_buf = {}; 2104uint64_t tmp_bind_offset = 0 ; 2105uint64_t tmp_bind_size = 0 ; 2106const size_t align_bytes = ctx -> global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ; 2107const size_t dst_offset = ggml_webgpu_tensor_offset ( dst ); 2108size_t scratch_offset = ROUNDUP_POW2 ( dst_offset + ggml_nbytes ( dst ), align_bytes ); 2109 2110if ( use_vec_reduce ) { 2111const uint64_t tmp_data_elems = nrows * (uint64_t) V -> ne [ 0 ] * nwg ; 2112const uint64_t tmp_stats_elems = nrows * 2u * nwg ; 2113tmp_stats_base = tmp_data_elems ; 2114tmp_size_bytes = 2115ROUNDUP_POW2 (( tmp_data_elems + tmp_stats_elems ) * sizeof (float), WEBGPU_STORAGE_BUF_BINDING_MULT ); 2116GGML_ASSERT ( tmp_stats_base <= UINT32_MAX ); 2117tmp_buf = ggml_webgpu_tensor_buf ( dst ); 2118tmp_bind_offset = scratch_offset ; 2119tmp_bind_size = tmp_size_bytes ; 2120scratch_offset = ROUNDUP_POW2 ( scratch_offset + tmp_size_bytes , align_bytes ); 2121} else { 2122// nwg==1 writes final dst directly in vec-split; bind tmp to a tiny non-overlapping scratch region. 2123tmp_size_bytes = WEBGPU_STORAGE_BUF_BINDING_MULT ; 2124tmp_buf = ggml_webgpu_tensor_buf ( dst ); 2125tmp_bind_offset = scratch_offset ; 2126tmp_bind_size = tmp_size_bytes ; 2127scratch_offset = ROUNDUP_POW2 ( scratch_offset + tmp_size_bytes , align_bytes ); 2128} 2129 2130webgpu_pipeline blk_pipeline ; 2131std::vector < uint32_t > blk_params ; 2132std::vector < wgpu::BindGroupEntry > blk_entries ; 2133if ( op . has_mask ) { 2134blk_nblk0 = CEIL_DIV ((uint32_t) K -> ne [ 1 ], decisions -> kv_tile ); 2135blk_nblk1 = (uint32_t) Q -> ne [ 1 ]; 2136blk_buf = ggml_webgpu_tensor_buf ( dst ); 2137const uint32_t stride_mask3 = (uint32_t) ( mask -> nb [ 3 ] / ggml_type_size ( mask -> type )); 2138blk_batch_count = stride_mask3 > 0 ? (uint32_t) Q -> ne [ 3 ] : 1u ; 2139const uint64_t blk_elems = (uint64_t) blk_nblk0 * blk_nblk1 * blk_batch_count ; 2140blk_size_bytes = ROUNDUP_POW2 ( blk_elems * sizeof (uint32_t), WEBGPU_STORAGE_BUF_BINDING_MULT ); 2141const ggml_webgpu_shader_lib_context blk_shader_ctx = op . shader_lib_ctx ; 2142blk_pipeline = ctx -> shader_lib -> get_flash_attn_blk_pipeline ( blk_shader_ctx , decisions -> kv_tile ); 2143 2144blk_params = { 2145(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , mask ) / ggml_type_size ( mask -> type )), // offset_mask 2146(uint32_t) Q -> ne [ 1 ], // seq_len_q 2147(uint32_t) K -> ne [ 1 ], // seq_len_kv 2148stride_mask3 , // stride_mask3 2149blk_nblk0 , // nblk0 2150blk_nblk1 , // nblk1 2151}; 2152blk_entries = { 2153ggml_webgpu_make_bind_group_entry ( 0 , ggml_webgpu_tensor_buf ( mask ), 2154ggml_webgpu_tensor_align_offset ( ctx , mask ), 2155ggml_webgpu_tensor_binding_size ( ctx , mask )), 2156ggml_webgpu_make_bind_group_entry ( 1 , blk_buf , scratch_offset , blk_size_bytes ), 2157}; 2158scratch_offset = ROUNDUP_POW2 ( scratch_offset + blk_size_bytes , align_bytes ); 2159} 2160 2161std::vector < uint32_t > split_params = op . params ; 2162if ( op . has_mask ) { 2163split_params . push_back ( 0u ); // blk_base 2164split_params . push_back ( blk_nblk0 ); // blk_nblk0 2165split_params . push_back ( blk_nblk1 ); // blk_nblk1 2166} 2167split_params . push_back ( 0u ); // tmp_data_base 2168split_params . push_back ((uint32_t) tmp_stats_base ); // tmp_stats_base 2169split_params . push_back ( nwg ); // nwg 2170 2171std::vector < wgpu::BindGroupEntry > split_entries = { 2172ggml_webgpu_make_bind_group_entry ( 0 , ggml_webgpu_tensor_buf ( Q ), ggml_webgpu_tensor_align_offset ( ctx , Q ), 2173ggml_webgpu_tensor_binding_size ( ctx , Q )), 2174}; 2175if ( op . kv_overlap ) { 2176split_entries . push_back ( 2177ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( K ), op . kv_bind_offset , op . kv_bind_size )); 2178} else { 2179split_entries . push_back ( ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( K ), 2180ggml_webgpu_tensor_align_offset ( ctx , K ), 2181ggml_webgpu_tensor_binding_size ( ctx , K ))); 2182split_entries . push_back ( ggml_webgpu_make_bind_group_entry ( 2 , ggml_webgpu_tensor_buf ( V ), 2183ggml_webgpu_tensor_align_offset ( ctx , V ), 2184ggml_webgpu_tensor_binding_size ( ctx , V ))); 2185} 2186uint32_t split_binding_index = op . kv_overlap ? 2u : 3u ; 2187if ( op . has_mask ) { 2188split_entries . push_back ( ggml_webgpu_make_bind_group_entry ( split_binding_index ++ , ggml_webgpu_tensor_buf ( mask ), 2189ggml_webgpu_tensor_align_offset ( ctx , mask ), 2190ggml_webgpu_tensor_binding_size ( ctx , mask ))); 2191} 2192if ( op . has_sinks ) { 2193split_entries . push_back ( ggml_webgpu_make_bind_group_entry ( split_binding_index ++ , ggml_webgpu_tensor_buf ( sinks ), 2194ggml_webgpu_tensor_align_offset ( ctx , sinks ), 2195ggml_webgpu_tensor_binding_size ( ctx , sinks ))); 2196} 2197if ( op . has_mask ) { 2198split_entries . push_back ( 2199ggml_webgpu_make_bind_group_entry ( split_binding_index ++ , blk_buf , blk_entries [ 1 ]. offset , blk_size_bytes )); 2200} 2201split_entries . push_back ( 2202ggml_webgpu_make_bind_group_entry ( split_binding_index ++ , tmp_buf , tmp_bind_offset , tmp_bind_size )); 2203split_entries . push_back ( ggml_webgpu_make_bind_group_entry ( split_binding_index ++ , ggml_webgpu_tensor_buf ( dst ), 2204ggml_webgpu_tensor_align_offset ( ctx , dst ), 2205ggml_webgpu_tensor_binding_size ( ctx , dst ))); 2206 2207webgpu_pipeline reduce_pipeline ; 2208std::vector < uint32_t > reduce_params ; 2209std::vector < wgpu::BindGroupEntry > reduce_entries ; 2210if ( use_vec_reduce ) { 2211const uint32_t reduce_sg_size = ctx -> global_ctx -> capabilities . max_subgroup_size ; 2212const uint32_t reduce_wg_size = std:: max ( 2213reduce_sg_size , 2214(uint32_t) std:: min < uint64_t > ((uint64_t) nwg * reduce_sg_size , 2215ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup )); 2216ggml_webgpu_shader_lib_context reduce_shader_ctx = op . shader_lib_ctx ; 2217reduce_shader_ctx . max_wg_size = reduce_wg_size ; 2218reduce_pipeline = ctx -> shader_lib -> get_flash_attn_vec_reduce_pipeline ( reduce_shader_ctx ); 2219 2220reduce_params = { 2221(uint32_t) nrows , // nrows 2222(uint32_t) Q -> ne [ 1 ], // seq_len_q 2223(uint32_t) Q -> ne [ 2 ], // n_heads 2224(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), // offset_dst 2225nwg , // nwg 22260u , // tmp_data_base 2227(uint32_t) tmp_stats_base , // tmp_stats_base 2228}; 2229 2230reduce_entries = { 2231ggml_webgpu_make_bind_group_entry ( 0 , tmp_buf , tmp_bind_offset , tmp_size_bytes ), 2232ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( dst ), ggml_webgpu_tensor_align_offset ( ctx , dst ), 2233ggml_webgpu_tensor_binding_size ( ctx , dst )), 2234}; 2235} 2236 2237uint32_t wg_x = Q -> ne [ 1 ] * Q -> ne [ 2 ] * Q -> ne [ 3 ]; 2238const uint64_t split_wg_total = (uint64_t) wg_x * nwg ; 2239GGML_ASSERT ( split_wg_total <= UINT32_MAX ); 2240 2241std::vector < webgpu_dispatch_desc > dispatches ; 2242 2243if ( op . has_mask ) { 2244dispatches . push_back ({ 2245blk_pipeline , std:: move ( blk_params ), std:: move ( blk_entries ), { blk_nblk0 , blk_nblk1 * blk_batch_count } 2246}); 2247} 2248dispatches . push_back ({ 2249pipeline , std:: move ( split_params ), std:: move ( split_entries ), { (uint32_t) split_wg_total , 1u } 2250}); 2251if ( use_vec_reduce ) { 2252dispatches . push_back ({ 2253reduce_pipeline , std:: move ( reduce_params ), std:: move ( reduce_entries ), { (uint32_t) nrows , 1u } 2254}); 2255} 2256 2257return ggml_backend_webgpu_build_multi ( ctx , dispatches ); 2258} 2259 2260static webgpu_encoded_op ggml_webgpu_flash_attn (webgpu_context & ctx , 2261ggml_tensor * Q , 2262ggml_tensor * K , 2263ggml_tensor * V , 2264ggml_tensor * mask , 2265ggml_tensor * sinks , 2266ggml_tensor * dst ) { 2267ggml_webgpu_flash_attn_op op = ggml_webgpu_flash_attn_prepare ( ctx , Q , K , V , mask , sinks , dst ); 2268if ( ggml_webgpu_flash_attn_use_vec_path ( ctx -> global_ctx , Q , K , V )) { 2269return ggml_webgpu_flash_attn_vec ( ctx , Q , K , V , mask , sinks , dst , std:: move ( op )); 2270} 2271return ggml_webgpu_flash_attn_direct ( ctx , op ); 2272} 2273 2274static webgpu_encoded_op ggml_webgpu_unary_op (webgpu_context & ctx , ggml_tensor * src , ggml_tensor * dst ) { 2275bool is_unary = dst -> op == GGML_OP_UNARY ; 2276 2277ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2278shader_lib_ctx . src0 = src ; 2279shader_lib_ctx . src1 = nullptr ; 2280shader_lib_ctx . dst = dst ; 2281shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2282 2283webgpu_pipeline pipeline = ctx -> shader_lib -> get_unary_pipeline ( shader_lib_ctx ); 2284 2285auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 2286const bool inplace = decisions -> inplace ; 2287 2288uint32_t ne = (uint32_t) ggml_nelements ( dst ); 2289 2290std::vector < uint32_t > params = { ne , 2291(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )), 2292(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2293(uint32_t) ( src -> nb [ 0 ] / ggml_type_size ( src -> type )), 2294(uint32_t) ( src -> nb [ 1 ] / ggml_type_size ( src -> type )), 2295(uint32_t) ( src -> nb [ 2 ] / ggml_type_size ( src -> type )), 2296(uint32_t) ( src -> nb [ 3 ] / ggml_type_size ( src -> type )), 2297(uint32_t) src -> ne [ 0 ], 2298(uint32_t) src -> ne [ 1 ], 2299(uint32_t) src -> ne [ 2 ] }; 2300 2301ggml_tensor * effective_src = src ; 2302if ( is_unary ) { 2303ggml_unary_op unary_op = ggml_get_unary_op ( dst ); 2304switch ( unary_op ) { 2305case GGML_UNARY_OP_XIELU : 2306{ 2307// Get float parameters and reinterpret their bit patterns as uint32_t 2308// for passing through the params buffer 2309float alpha_n = ggml_get_op_params_f32 ( dst , 1 ); 2310float alpha_p = ggml_get_op_params_f32 ( dst , 2 ); 2311float beta = ggml_get_op_params_f32 ( dst , 3 ); 2312float eps = ggml_get_op_params_f32 ( dst , 4 ); 2313params . push_back ( ggml_webgpu_u32_from_f32 ( alpha_n )); 2314params . push_back ( ggml_webgpu_u32_from_f32 ( alpha_p )); 2315params . push_back ( ggml_webgpu_u32_from_f32 ( beta )); 2316params . push_back ( ggml_webgpu_u32_from_f32 ( eps )); 2317break ; 2318} 2319default : 2320break ; 2321} 2322} else if ( dst -> op == GGML_OP_CLAMP ) { 2323float clamp_min = ggml_get_op_params_f32 ( dst , 0 ); 2324float clamp_max = ggml_get_op_params_f32 ( dst , 1 ); 2325params . push_back ( ggml_webgpu_u32_from_f32 ( clamp_min )); 2326params . push_back ( ggml_webgpu_u32_from_f32 ( clamp_max )); 2327} else if ( dst -> op == GGML_OP_FILL ) { 2328float fill_val = ggml_get_op_params_f32 ( dst , 0 ); 2329params . push_back ( ggml_webgpu_u32_from_f32 ( fill_val )); 2330effective_src = dst ; // fill simply fills dst 2331} 2332 2333std::vector < wgpu::BindGroupEntry > entries = { 2334ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , effective_src ), 2335}; 2336if (! inplace ) { 2337entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst )); 2338} 2339 2340uint32_t wg_x , wg_y ; 2341uint32_t total_wg = CEIL_DIV ( ggml_nelements ( dst ), decisions -> wg_size ); 2342compute_2d_workgroups ( total_wg , ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension , wg_x , wg_y ); 2343return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 2344} 2345 2346static webgpu_encoded_op ggml_webgpu_binary_op (webgpu_context & ctx , 2347ggml_tensor * src0 , 2348ggml_tensor * src1 , 2349ggml_tensor * dst ) { 2350ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2351shader_lib_ctx . src0 = src0 ; 2352shader_lib_ctx . src1 = src1 ; 2353shader_lib_ctx . dst = dst ; 2354shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2355 2356const bool src_overlap = ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , src0 , src1 ); 2357webgpu_pipeline pipeline = ctx -> shader_lib -> get_binary_pipeline ( shader_lib_ctx , src_overlap ); 2358auto * decisions = static_cast < ggml_webgpu_binary_shader_decisions *> ( pipeline . context . get ()); 2359 2360uint32_t ne = (uint32_t) ggml_nelements ( dst ); 2361 2362size_t src0_webgpu_tensor_align_offset = ggml_webgpu_tensor_align_offset ( ctx , src0 ); 2363size_t src1_webgpu_tensor_align_offset = ggml_webgpu_tensor_align_offset ( ctx , src1 ); 2364 2365uint32_t offset_src0 = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )); 2366uint32_t offset_src1 = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )); 2367size_t merged_offset = 0 ; 2368size_t merged_size = 0 ; 2369if ( decisions -> src_overlap ) { 2370const ggml_webgpu_merged_binding_range merged_range = 2371ggml_webgpu_tensor_merged_binding_range ( ctx , { src0 , src1 }); 2372merged_offset = merged_range . offset ; 2373merged_size = merged_range . size ; 2374offset_src0 = ggml_webgpu_tensor_merged_element_offset ( src0 , merged_range ); 2375offset_src1 = ggml_webgpu_tensor_merged_element_offset ( src1 , merged_range ); 2376} 2377 2378std::vector < uint32_t > params = { 2379ne , 2380offset_src0 , 2381offset_src1 , 2382(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2383(uint32_t) ( src0 -> nb [ 0 ] / ggml_type_size ( src0 -> type )), 2384(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 2385(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 2386(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 2387(uint32_t) ( src1 -> nb [ 0 ] / ggml_type_size ( src1 -> type )), 2388(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 2389(uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )), 2390(uint32_t) ( src1 -> nb [ 3 ] / ggml_type_size ( src1 -> type )), 2391(uint32_t) src0 -> ne [ 0 ], 2392(uint32_t) src0 -> ne [ 1 ], 2393(uint32_t) src0 -> ne [ 2 ], 2394(uint32_t) src1 -> ne [ 0 ], 2395(uint32_t) src1 -> ne [ 1 ], 2396(uint32_t) src1 -> ne [ 2 ], 2397(uint32_t) src1 -> ne [ 3 ], 2398}; 2399 2400std::vector < wgpu::BindGroupEntry > entries ; 2401 2402if ( decisions -> src_overlap ) { 2403entries . push_back ( 2404ggml_webgpu_make_bind_group_entry ( 0 , ggml_webgpu_tensor_buf ( src0 ), merged_offset , merged_size )); 2405entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst )); 2406} else { 2407entries . push_back ( ggml_webgpu_make_bind_group_entry ( 0 , ggml_webgpu_tensor_buf ( src0 ), 2408src0_webgpu_tensor_align_offset , 2409ggml_webgpu_tensor_binding_size ( ctx , src0 ))); 2410entries . push_back ( ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( src1 ), 2411src1_webgpu_tensor_align_offset , 2412ggml_webgpu_tensor_binding_size ( ctx , src1 ))); 2413if (! decisions -> inplace && ! decisions -> overlap ) { 2414entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , dst )); 2415} 2416} 2417 2418uint32_t wg_x , wg_y ; 2419uint32_t total_wg = CEIL_DIV ( ggml_nelements ( dst ), decisions -> wg_size ); 2420compute_2d_workgroups ( total_wg , ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension , wg_x , wg_y ); 2421return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 2422} 2423 2424static webgpu_encoded_op ggml_webgpu_add_id (webgpu_context & ctx , 2425ggml_tensor * src0 , 2426ggml_tensor * src1 , 2427ggml_tensor * src2 , 2428ggml_tensor * dst ) { 2429ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2430shader_lib_ctx . src0 = src0 ; 2431shader_lib_ctx . src1 = src1 ; 2432shader_lib_ctx . src2 = src2 ; 2433shader_lib_ctx . dst = dst ; 2434shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2435 2436webgpu_pipeline pipeline = ctx -> shader_lib -> get_add_id_pipeline ( shader_lib_ctx ); 2437 2438auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 2439 2440std::vector < uint32_t > params = { 2441(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )), 2442(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )), 2443(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src2 ) / ggml_type_size ( src2 -> type )), 2444(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2445(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 2446(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 2447(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 2448(uint32_t) ( src2 -> nb [ 0 ] / ggml_type_size ( src2 -> type )), 2449(uint32_t) ( src2 -> nb [ 1 ] / ggml_type_size ( src2 -> type )), 2450(uint32_t) dst -> ne [ 0 ], 2451(uint32_t) dst -> ne [ 1 ], 2452(uint32_t) dst -> ne [ 2 ], 2453}; 2454 2455std::vector < wgpu::BindGroupEntry > entries ; 2456 2457entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 )); 2458entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , src1 )); 2459entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , src2 )); 2460 2461if (! decisions -> inplace ) { 2462entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 3 , dst )); 2463} 2464 2465uint32_t wg_x = 1 ; 2466uint32_t wg_y = 1 ; 2467uint32_t total_wg = ggml_nrows ( dst ); 2468const uint32_t max_wg_per_dim = ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension ; 2469compute_2d_workgroups ( total_wg , max_wg_per_dim , wg_x , wg_y ); 2470 2471return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 2472} 2473 2474static webgpu_encoded_op ggml_webgpu_concat (webgpu_context & ctx , 2475ggml_tensor * src0 , 2476ggml_tensor * src1 , 2477ggml_tensor * dst ) { 2478uint32_t ne = (uint32_t) ggml_nelements ( dst ); 2479uint32_t dim = (uint32_t) dst -> op_params [ 0 ]; 2480if ( ggml_nbytes ( src0 ) == 0 && ggml_nbytes ( src1 ) == 0 ) { 2481return {}; 2482} 2483 2484ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2485shader_lib_ctx . src0 = src0 ; 2486shader_lib_ctx . src1 = src1 ; 2487shader_lib_ctx . dst = dst ; 2488shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2489 2490const bool src_overlap = ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , src0 , src1 ) || 2491ggml_nbytes ( src0 ) == 0 || ggml_nbytes ( src1 ) == 0 ; 2492webgpu_pipeline pipeline = ctx -> shader_lib -> get_concat_pipeline ( shader_lib_ctx , src_overlap ); 2493auto * decisions = static_cast < ggml_webgpu_binary_shader_decisions *> ( pipeline . context . get ()); 2494 2495uint32_t offset_src0 = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )); 2496uint32_t offset_src1 = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )); 2497size_t merged_offset = 0 ; 2498size_t merged_size = 0 ; 2499if ( decisions -> src_overlap ) { 2500if ( ggml_nbytes ( src0 ) == 0 ) { 2501merged_offset = ggml_webgpu_tensor_align_offset ( ctx , src1 ); 2502merged_size = ggml_webgpu_tensor_binding_size ( ctx , src1 ); 2503offset_src0 = 0 ; 2504offset_src1 = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )); 2505} else if ( ggml_nbytes ( src1 ) == 0 ) { 2506merged_offset = ggml_webgpu_tensor_align_offset ( ctx , src0 ); 2507merged_size = ggml_webgpu_tensor_binding_size ( ctx , src0 ); 2508offset_src0 = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )); 2509offset_src1 = 0 ; 2510} else { 2511const ggml_webgpu_merged_binding_range merged_range = 2512ggml_webgpu_tensor_merged_binding_range ( ctx , { src0 , src1 }); 2513merged_offset = merged_range . offset ; 2514merged_size = merged_range . size ; 2515offset_src0 = ggml_webgpu_tensor_merged_element_offset ( src0 , merged_range ); 2516offset_src1 = ggml_webgpu_tensor_merged_element_offset ( src1 , merged_range ); 2517} 2518} 2519 2520std::vector < uint32_t > params = { ne , 2521offset_src0 , 2522offset_src1 , 2523(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2524(uint32_t) ( src0 -> nb [ 0 ] / ggml_type_size ( src0 -> type )), 2525(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 2526(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 2527(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 2528(uint32_t) ( src1 -> nb [ 0 ] / ggml_type_size ( src1 -> type )), 2529(uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )), 2530(uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )), 2531(uint32_t) ( src1 -> nb [ 3 ] / ggml_type_size ( src1 -> type )), 2532(uint32_t) dst -> ne [ 0 ], 2533(uint32_t) dst -> ne [ 1 ], 2534(uint32_t) dst -> ne [ 2 ], 2535dim , 2536(uint32_t) src0 -> ne [ dim ] }; 2537 2538std::vector < wgpu::BindGroupEntry > entries = {}; 2539if ( decisions -> src_overlap ) { 2540entries . push_back ( 2541ggml_webgpu_make_bind_group_entry ( 0 , ggml_webgpu_tensor_buf ( src0 ), merged_offset , merged_size )); 2542entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst )); 2543} else { 2544entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 )); 2545entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , src1 )); 2546entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , dst )); 2547} 2548 2549uint32_t wg_x = CEIL_DIV ( ne , decisions -> wg_size ); 2550return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x ); 2551} 2552 2553static webgpu_encoded_op ggml_webgpu_repeat (webgpu_context & ctx , ggml_tensor * src0 , ggml_tensor * dst ) { 2554uint32_t ne = (uint32_t) ggml_nelements ( dst ); 2555 2556std::vector < uint32_t > params = { ne , 2557(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / 2558ggml_type_size ( src0 -> type )), 2559(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2560(uint32_t) ( src0 -> nb [ 0 ] / ggml_type_size ( src0 -> type )), 2561(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 2562(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 2563(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 2564(uint32_t) ( src0 -> ne [ 0 ]), 2565(uint32_t) ( src0 -> ne [ 1 ]), 2566(uint32_t) ( src0 -> ne [ 2 ]), 2567(uint32_t) ( src0 -> ne [ 3 ]), 2568(uint32_t) ( dst -> ne [ 0 ]), 2569(uint32_t) ( dst -> ne [ 1 ]), 2570(uint32_t) ( dst -> ne [ 2 ]) }; 2571 2572std::vector < wgpu::BindGroupEntry > entries = { 2573ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 ), 2574ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst ), 2575}; 2576 2577ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2578shader_lib_ctx . src0 = src0 ; 2579shader_lib_ctx . dst = dst ; 2580shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2581 2582webgpu_pipeline pipeline = ctx -> shader_lib -> get_repeat_pipeline ( shader_lib_ctx ); 2583auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 2584uint32_t wg_x = CEIL_DIV ( ne , decisions -> wg_size ); 2585return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x ); 2586} 2587 2588static std::optional < webgpu_encoded_op > ggml_webgpu_rms_norm_mul (webgpu_context & ctx , 2589ggml_tensor * rn_src , 2590ggml_tensor * rn_dst , 2591ggml_tensor * mul_src0 , 2592ggml_tensor * mul_src1 , 2593ggml_tensor * dst ) { 2594ggml_tensor * mul_src ; 2595 2596if ( ggml_webgpu_tensor_equal ( rn_dst , mul_src0 )) { 2597mul_src = mul_src1 ; 2598} else if ( ggml_webgpu_tensor_equal ( rn_dst , mul_src1 )) { 2599mul_src = mul_src0 ; 2600} else { 2601GGML_ABORT ( "rms_norm must be equal to the one of mul_src0 and mul_src1" ); 2602} 2603 2604uint32_t offset_rn_src = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , rn_src ) / ggml_type_size ( rn_src -> type )); 2605uint32_t offset_mul_src = 2606(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , mul_src ) / ggml_type_size ( mul_src -> type )); 2607size_t merged_offset = 0 ; 2608size_t merged_size = 0 ; 2609 2610std::vector < uint32_t > params = { 2611offset_rn_src , 2612offset_mul_src , 2613(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2614(uint32_t) ( rn_src -> nb [ 1 ] / ggml_type_size ( rn_src -> type )), 2615(uint32_t) ( rn_src -> nb [ 2 ] / ggml_type_size ( rn_src -> type )), 2616(uint32_t) ( rn_src -> nb [ 3 ] / ggml_type_size ( rn_src -> type )), 2617(uint32_t) ( mul_src -> nb [ 1 ] / ggml_type_size ( mul_src -> type )), 2618(uint32_t) ( mul_src -> nb [ 2 ] / ggml_type_size ( mul_src -> type )), 2619(uint32_t) ( mul_src -> nb [ 3 ] / ggml_type_size ( mul_src -> type )), 2620(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 2621(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 2622(uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 2623(uint32_t) mul_src -> ne [ 0 ], 2624(uint32_t) mul_src -> ne [ 1 ], 2625(uint32_t) mul_src -> ne [ 2 ], 2626(uint32_t) mul_src -> ne [ 3 ], 2627(uint32_t) dst -> ne [ 0 ], 2628(uint32_t) dst -> ne [ 1 ], 2629(uint32_t) dst -> ne [ 2 ], 2630ggml_webgpu_u32_from_f32 ( ggml_get_op_params_f32 ( rn_dst , 0 )) // epsilon, treated as f32 in the shader 2631}; 2632 2633std::vector < wgpu::BindGroupEntry > entries ; 2634 2635ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2636shader_lib_ctx . src0 = rn_src ; 2637shader_lib_ctx . src1 = mul_src ; 2638shader_lib_ctx . dst = dst ; 2639shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2640 2641const bool src_overlap = ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , rn_src , mul_src ); 2642webgpu_pipeline pipeline = ctx -> shader_lib -> get_rms_norm_mul_pipeline ( shader_lib_ctx , src_overlap ); 2643auto * decisions = static_cast < ggml_webgpu_rms_norm_mul_shader_decisions *> ( pipeline . context . get ()); 2644 2645if ( decisions -> src_overlap ) { 2646const ggml_webgpu_merged_binding_range merged_range = 2647ggml_webgpu_tensor_merged_binding_range ( ctx , { rn_src , mul_src }); 2648merged_offset = merged_range . offset ; 2649merged_size = merged_range . size ; 2650offset_rn_src = ggml_webgpu_tensor_merged_element_offset ( rn_src , merged_range ); 2651offset_mul_src = ggml_webgpu_tensor_merged_element_offset ( mul_src , merged_range ); 2652params [ 0 ] = offset_rn_src ; 2653params [ 1 ] = offset_mul_src ; 2654} 2655 2656if ( decisions -> inplace || decisions -> overlap ) { 2657entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , rn_src )); 2658entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , mul_src )); 2659} else if ( decisions -> src_overlap ) { 2660entries . push_back ( 2661ggml_webgpu_make_bind_group_entry ( 0 , ggml_webgpu_tensor_buf ( rn_src ), merged_offset , merged_size )); 2662entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst )); 2663} else { 2664entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , rn_src )); 2665entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , mul_src )); 2666entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , dst )); 2667} 2668 2669return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , ggml_nrows ( dst )); 2670} 2671 2672static webgpu_encoded_op ggml_webgpu_row_norm (webgpu_context & ctx , ggml_tensor * src , ggml_tensor * dst ) { 2673std::vector < uint32_t > params = { 2674(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )), 2675(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2676(uint32_t) ( src -> nb [ 1 ] / ggml_type_size ( src -> type )), 2677(uint32_t) ( src -> nb [ 2 ] / ggml_type_size ( src -> type )), 2678(uint32_t) ( src -> nb [ 3 ] / ggml_type_size ( src -> type )), 2679(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 2680(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 2681(uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 2682(uint32_t) src -> ne [ 0 ], 2683(uint32_t) src -> ne [ 1 ], 2684(uint32_t) src -> ne [ 2 ], 2685ggml_webgpu_u32_from_f32 ( ggml_get_op_params_f32 ( dst , 0 )) // epsilon, treated as f32 in the shader 2686}; 2687 2688ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2689shader_lib_ctx . src0 = src ; 2690shader_lib_ctx . dst = dst ; 2691shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2692 2693webgpu_pipeline pipeline = ctx -> shader_lib -> get_row_norm_pipeline ( shader_lib_ctx ); 2694auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 2695 2696std::vector < wgpu::BindGroupEntry > entries = { ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src ) }; 2697if (! decisions -> inplace ) { 2698entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst )); 2699} 2700return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , ggml_nrows ( src )); 2701} 2702 2703static webgpu_encoded_op ggml_webgpu_rope (webgpu_context & ctx , 2704ggml_tensor * src0 , 2705ggml_tensor * src1 , 2706ggml_tensor * src2 , 2707ggml_tensor * dst ) { 2708ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2709shader_lib_ctx . src0 = src0 ; 2710shader_lib_ctx . src1 = src1 ; 2711shader_lib_ctx . src2 = src2 ; 2712shader_lib_ctx . dst = dst ; 2713shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2714 2715webgpu_pipeline pipeline = ctx -> shader_lib -> get_rope_pipeline ( shader_lib_ctx ); 2716 2717auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 2718 2719const bool inplace = decisions -> inplace ; 2720const int has_freq_factor = ( src2 != nullptr ); 2721 2722const int n_dims = ((int32_t * ) dst -> op_params )[ 1 ]; 2723const int mode = ((int32_t * ) dst -> op_params )[ 2 ]; 2724const int n_ctx_orig = ((int32_t * ) dst -> op_params )[ 4 ]; 2725const int n_offs = ((int32_t * ) dst -> op_params )[ 15 ]; 2726 2727float freq_base ; 2728float freq_scale ; 2729float ext_factor ; 2730float attn_factor ; 2731float beta_fast ; 2732float beta_slow ; 2733memcpy ( & freq_base , (int32_t * ) dst -> op_params + 5 , sizeof (float)); 2734memcpy ( & freq_scale , (int32_t * ) dst -> op_params + 6 , sizeof (float)); 2735memcpy ( & ext_factor , (int32_t * ) dst -> op_params + 7 , sizeof (float)); 2736memcpy ( & attn_factor , (int32_t * ) dst -> op_params + 8 , sizeof (float)); 2737memcpy ( & beta_fast , (int32_t * ) dst -> op_params + 9 , sizeof (float)); 2738memcpy ( & beta_slow , (int32_t * ) dst -> op_params + 10 , sizeof (float)); 2739 2740int sections [ 4 ]; 2741memcpy ( sections , (int32_t * ) dst -> op_params + 11 , 4 * sizeof (int)); 2742 2743float theta_scale = powf ( freq_base , -2.0f / n_dims ); 2744 2745float corr_dims [ 2 ]; 2746ggml_rope_yarn_corr_dims ( n_dims , n_ctx_orig , freq_base , beta_fast , beta_slow , corr_dims ); 2747 2748std::vector < uint32_t > params = { 2749(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )), 2750(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )), 2751src2 != nullptr ? (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src2 ) / ggml_type_size ( src2 -> type )) : 0 , 2752(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2753(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 2754(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 2755(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 2756(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 2757(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 2758(uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 2759(uint32_t) ggml_nelements ( src0 ) / 2 , 2760(uint32_t) src0 -> ne [ 0 ], 2761(uint32_t) src0 -> ne [ 1 ], 2762(uint32_t) src0 -> ne [ 2 ], 2763(uint32_t) n_dims , 2764(uint32_t) mode , 2765ggml_webgpu_u32_from_f32 ( theta_scale ), 2766ggml_webgpu_u32_from_f32 ( attn_factor ), 2767ggml_webgpu_u32_from_f32 ( freq_scale ), 2768ggml_webgpu_u32_from_f32 ( ext_factor ), 2769ggml_webgpu_u32_from_f32 ( corr_dims [ 0 ]), 2770ggml_webgpu_u32_from_f32 ( corr_dims [ 1 ]), 2771(uint32_t) sections [ 0 ], 2772(uint32_t) sections [ 1 ], 2773(uint32_t) sections [ 2 ], 2774(uint32_t) sections [ 3 ], 2775(uint32_t) n_offs 2776}; 2777 2778std::vector < wgpu::BindGroupEntry > entries = { ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 ), 2779ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , src1 ) }; 2780uint32_t dst_binding = 2 ; 2781if ( has_freq_factor ) { 2782dst_binding = 3 ; 2783entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 2 , src2 )); 2784} 2785if (! inplace ) { 2786entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , dst_binding , dst )); 2787} 2788 2789uint32_t wg_x = CEIL_DIV ( ggml_nelements ( dst ), decisions -> wg_size ); 2790return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x ); 2791} 2792 2793static webgpu_encoded_op ggml_webgpu_glu (webgpu_context & ctx , 2794ggml_tensor * src0 , 2795ggml_tensor * src1 , 2796ggml_tensor * dst ) { 2797ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2798shader_lib_ctx . src0 = src0 ; 2799shader_lib_ctx . src1 = src1 ; 2800shader_lib_ctx . dst = dst ; 2801shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2802 2803const bool src_overlap = src1 != nullptr && ggml_webgpu_tensor_binding_overlap ( ctx -> global_ctx , src0 , src1 ); 2804webgpu_pipeline pipeline = ctx -> shader_lib -> get_glu_pipeline ( shader_lib_ctx , src_overlap ); 2805 2806auto * decisions = static_cast < ggml_webgpu_glu_shader_decisions *> ( pipeline . context . get ()); 2807 2808const int split = ( src1 != nullptr ); 2809 2810uint32_t offset_src0 = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )); 2811uint32_t offset_src1 = 2812src1 != nullptr ? (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )) : 0 ; 2813size_t merged_offset = 0 ; 2814size_t merged_size = 0 ; 2815if ( decisions -> src_overlap ) { 2816const ggml_webgpu_merged_binding_range merged_range = 2817ggml_webgpu_tensor_merged_binding_range ( ctx , { src0 , src1 }); 2818merged_offset = merged_range . offset ; 2819merged_size = merged_range . size ; 2820offset_src0 = ggml_webgpu_tensor_merged_element_offset ( src0 , merged_range ); 2821offset_src1 = ggml_webgpu_tensor_merged_element_offset ( src1 , merged_range ); 2822} 2823 2824std::vector < uint32_t > params = { 2825offset_src0 , 2826offset_src1 , 2827(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2828(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 2829(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 2830(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 2831src1 != nullptr ? (uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )) : 2832(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 2833src1 != nullptr ? (uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )) : 2834(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 2835src1 != nullptr ? (uint32_t) ( src1 -> nb [ 3 ] / ggml_type_size ( src1 -> type )) : 2836(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 2837(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 2838(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 2839(uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 2840(uint32_t) ggml_nelements ( dst ), 2841(uint32_t) dst -> ne [ 0 ], 2842(uint32_t) dst -> ne [ 1 ], 2843(uint32_t) dst -> ne [ 2 ], 2844(uint32_t) ((int32_t * ) dst -> op_params )[ 1 ], // swapped 2845ggml_webgpu_u32_from_f32 ( ggml_get_op_params_f32 ( dst , 2 )), // alpha, for swiglu_oai 2846ggml_webgpu_u32_from_f32 ( ggml_get_op_params_f32 ( dst , 3 )), // limit 2847}; 2848 2849std::vector < wgpu::BindGroupEntry > entries ; 2850uint32_t dst_binding = 1 ; 2851if ( decisions -> src_overlap ) { 2852entries . push_back ( 2853ggml_webgpu_make_bind_group_entry ( 0 , ggml_webgpu_tensor_buf ( src0 ), merged_offset , merged_size )); 2854} else { 2855entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src0 )); 2856} 2857if ( split && ! decisions -> src_overlap ) { 2858dst_binding = 2 ; 2859entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , src1 )); 2860} 2861entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , dst_binding , dst )); 2862 2863uint32_t wg_x = CEIL_DIV ( ggml_nelements ( dst ), decisions -> wg_size ); 2864return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x ); 2865} 2866 2867static webgpu_encoded_op ggml_webgpu_scale (webgpu_context & ctx , ggml_tensor * src , ggml_tensor * dst ) { 2868ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2869shader_lib_ctx . src0 = src ; 2870shader_lib_ctx . src1 = nullptr ; 2871shader_lib_ctx . dst = dst ; 2872shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2873 2874webgpu_pipeline pipeline = ctx -> shader_lib -> get_scale_pipeline ( shader_lib_ctx ); 2875auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 2876 2877// params unchanged 2878std::vector < uint32_t > params = { 2879(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )), 2880(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2881(uint32_t) ( src -> nb [ 1 ] / ggml_type_size ( src -> type )), 2882(uint32_t) ( src -> nb [ 2 ] / ggml_type_size ( src -> type )), 2883(uint32_t) ( src -> nb [ 3 ] / ggml_type_size ( src -> type )), 2884(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 2885(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 2886(uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 2887(uint32_t) ggml_nelements ( dst ), 2888(uint32_t) src -> ne [ 0 ], 2889(uint32_t) src -> ne [ 1 ], 2890(uint32_t) src -> ne [ 2 ], 2891ggml_webgpu_u32_from_f32 ( ggml_get_op_params_f32 ( dst , 0 )), // scale 2892ggml_webgpu_u32_from_f32 ( ggml_get_op_params_f32 ( dst , 1 )) // bias 2893}; 2894 2895// bindgroups unchanged 2896std::vector < wgpu::BindGroupEntry > entries = { ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src ) }; 2897 2898if (! decisions -> inplace ) { 2899entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst )); 2900} 2901 2902uint32_t wg_x , wg_y ; 2903uint32_t total_wg = CEIL_DIV ( ggml_nelements ( dst ), decisions -> wg_size ); 2904compute_2d_workgroups ( total_wg , ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension , wg_x , wg_y ); 2905return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 2906} 2907 2908static webgpu_encoded_op ggml_webgpu_soft_max (webgpu_context & ctx , 2909ggml_tensor * src0 , 2910ggml_tensor * src1 , 2911ggml_tensor * src2 , 2912ggml_tensor * dst ) { 2913ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2914shader_lib_ctx . src0 = src0 ; 2915shader_lib_ctx . src1 = src1 ; 2916shader_lib_ctx . src2 = src2 ; 2917shader_lib_ctx . dst = dst ; 2918shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2919 2920webgpu_pipeline pipeline = ctx -> shader_lib -> get_soft_max_pipeline ( shader_lib_ctx ); 2921auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 2922 2923const bool inplace = decisions -> inplace ; 2924const int has_mask = ( src1 != nullptr ); 2925const int has_sink = ( src2 != nullptr ); 2926float max_bias = ggml_get_op_params_f32 ( dst , 1 ); 2927float n_head_log2 = float( 1u << (uint32_t) floor ( log2 ( src0 -> ne [ 2 ]))); 2928float m0 = powf ( 2.0f , - ( max_bias ) / n_head_log2 ); 2929float m1 = powf ( 2.0f , - ( max_bias / 2.0f ) / n_head_log2 ); 2930 2931std::vector < uint32_t > params = { 2932(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src0 ) / ggml_type_size ( src0 -> type )), 2933has_mask ? (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src1 ) / ggml_type_size ( src1 -> type )) : 0 , 2934has_sink ? (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src2 ) / ggml_type_size ( src2 -> type )) : 0 , 2935(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2936(uint32_t) ( src0 -> nb [ 1 ] / ggml_type_size ( src0 -> type )), 2937(uint32_t) ( src0 -> nb [ 2 ] / ggml_type_size ( src0 -> type )), 2938(uint32_t) ( src0 -> nb [ 3 ] / ggml_type_size ( src0 -> type )), 2939has_mask ? (uint32_t) ( src1 -> nb [ 1 ] / ggml_type_size ( src1 -> type )) : 0 , 2940has_mask ? (uint32_t) ( src1 -> nb [ 2 ] / ggml_type_size ( src1 -> type )) : 0 , 2941has_mask ? (uint32_t) ( src1 -> nb [ 3 ] / ggml_type_size ( src1 -> type )) : 0 , 2942(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 2943(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 2944(uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 2945(uint32_t) src0 -> ne [ 0 ], 2946(uint32_t) src0 -> ne [ 1 ], 2947(uint32_t) src0 -> ne [ 2 ], 2948has_mask ? (uint32_t) src1 -> ne [ 2 ] : 0 , 2949has_mask ? (uint32_t) src1 -> ne [ 3 ] : 0 , 2950ggml_webgpu_u32_from_f32 ( ggml_get_op_params_f32 ( dst , 0 )), // scale 2951ggml_webgpu_u32_from_f32 ( max_bias ), 2952ggml_webgpu_u32_from_f32 ( n_head_log2 ), 2953ggml_webgpu_u32_from_f32 ( m0 ), 2954ggml_webgpu_u32_from_f32 ( m1 ) 2955}; 2956 2957std::vector < wgpu::BindGroupEntry > entries = { ggml_webgpu_make_bind_group_entry ( 29580 , ggml_webgpu_tensor_buf ( src0 ), ggml_webgpu_tensor_align_offset ( ctx , src0 ), 2959ggml_webgpu_tensor_binding_size ( ctx , src0 )) }; 2960uint32_t binding_num = 1 ; 2961if ( has_mask ) { 2962entries . push_back ( ggml_webgpu_make_bind_group_entry ( binding_num , ggml_webgpu_tensor_buf ( src1 ), 2963ggml_webgpu_tensor_align_offset ( ctx , src1 ), 2964ggml_webgpu_tensor_binding_size ( ctx , src1 ))); 2965binding_num ++ ; 2966} 2967if ( has_sink ) { 2968entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , binding_num , src2 )); 2969binding_num ++ ; 2970} 2971if (! inplace ) { 2972entries . push_back ( ggml_webgpu_make_tensor_bind_group_entry ( ctx , binding_num , dst )); 2973} 2974 2975return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , ggml_nrows ( dst )); 2976} 2977 2978static webgpu_encoded_op ggml_webgpu_argmax (webgpu_context & ctx , ggml_tensor * src , ggml_tensor * dst ) { 2979std::vector < uint32_t > params = { (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )), 2980(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 2981(uint32_t) src -> ne [ 0 ] }; 2982 2983std::vector < wgpu::BindGroupEntry > entries = { ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src ), 2984ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst ) }; 2985 2986ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 2987shader_lib_ctx . src0 = src ; 2988shader_lib_ctx . dst = dst ; 2989shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 2990 2991webgpu_pipeline pipeline = ctx -> shader_lib -> get_argmax_pipeline ( shader_lib_ctx ); 2992uint32_t wg_x = ggml_nelements ( dst ); 2993return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x ); 2994} 2995 2996static webgpu_encoded_op ggml_webgpu_argsort (webgpu_context & ctx , ggml_tensor * src , ggml_tensor * dst ) { 2997bool is_top_k = dst -> op == GGML_OP_TOP_K ; 2998 2999ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 3000shader_lib_ctx . src0 = src ; 3001shader_lib_ctx . src1 = nullptr ; 3002shader_lib_ctx . dst = dst ; 3003shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 3004shader_lib_ctx . wg_mem_limit_bytes = ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupStorageSize ; 3005 3006webgpu_pipeline argsort_pipeline = ctx -> shader_lib -> get_argsort_pipeline ( shader_lib_ctx ); 3007auto * argsort_decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( argsort_pipeline . context . get ()); 3008 3009webgpu_pipeline argsort_merge_pipeline = ctx -> shader_lib -> get_argsort_merge_pipeline ( shader_lib_ctx ); 3010 3011const uint32_t src_ne0 = (uint32_t) src -> ne [ 0 ]; 3012const uint32_t nrows = (uint32_t) ggml_nrows ( src ); 3013const uint32_t npr = CEIL_DIV ( src_ne0 , argsort_decisions -> wg_size ); 3014const uint32_t block_size = 3015is_top_k ? std:: min ( argsort_decisions -> wg_size , (uint32_t) dst -> ne [ 0 ]) : argsort_decisions -> wg_size ; 3016uint32_t out_ne0 = src_ne0 ; 3017if ( is_top_k ) { 3018if ( npr > 1 ) { 3019const uint32_t last_tile = src_ne0 - ( npr - 1 ) * argsort_decisions -> wg_size ; 3020out_ne0 = ( npr - 1 ) * block_size + std:: min ( last_tile , block_size ); 3021} else { 3022out_ne0 = block_size ; 3023} 3024} 3025 3026uint32_t merge_len = block_size ; 3027uint32_t merge_passes = 0 ; 3028while ( merge_len < out_ne0 ) { 3029merge_len <<= 1 ; 3030merge_passes ++ ; 3031} 3032 3033const bool start_in_tmp = ( merge_passes % 2 ) == 1 ; 3034 3035const size_t dst_offset = ggml_webgpu_tensor_offset ( dst ); 3036const size_t idx_nbytes = out_ne0 * ggml_nrows ( dst ) * sizeof (int32_t); 3037const size_t tmp_offset = 3038ROUNDUP_POW2 ( dst_offset + idx_nbytes , ctx -> global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ); 3039const size_t tmp_binding_size = ROUNDUP_POW2 ( idx_nbytes , WEBGPU_STORAGE_BUF_BINDING_MULT ); 3040const size_t dst_binding_size = 3041ROUNDUP_POW2 ( idx_nbytes + ggml_webgpu_tensor_misalignment ( ctx , dst ), WEBGPU_STORAGE_BUF_BINDING_MULT ); 3042 3043const uint32_t offset_src = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )); 3044const uint32_t offset_dst = (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )); 3045const uint32_t offset_tmp = 0 ; 3046const uint32_t stride_src1 = (uint32_t) ( src -> nb [ 1 ] / ggml_type_size ( src -> type )); 3047const uint32_t stride_src2 = (uint32_t) ( src -> nb [ 2 ] / ggml_type_size ( src -> type )); 3048const uint32_t stride_src3 = (uint32_t) ( src -> nb [ 3 ] / ggml_type_size ( src -> type )); 3049const uint32_t stride_idx1 = out_ne0 ; 3050const uint32_t stride_idx2 = out_ne0 * (uint32_t) dst -> ne [ 1 ]; 3051const uint32_t stride_idx3 = stride_idx2 * (uint32_t) dst -> ne [ 2 ]; 3052 3053std::vector < webgpu_dispatch_desc > dispatches ; 3054 3055const uint32_t init_offset = start_in_tmp ? offset_tmp : offset_dst ; 3056const size_t init_align_offset = start_in_tmp ? tmp_offset : ggml_webgpu_tensor_align_offset ( ctx , dst ); 3057const size_t init_binding_size = start_in_tmp ? tmp_binding_size : dst_binding_size ; 3058 3059std::vector < uint32_t > init_params = { 3060offset_src , init_offset , stride_src1 , stride_src2 , stride_src3 , stride_idx1 , 3061stride_idx2 , stride_idx3 , src_ne0 , (uint32_t) src -> ne [ 1 ], (uint32_t) src -> ne [ 2 ], out_ne0 , 3062block_size , npr , nrows 3063}; 3064 3065uint32_t wg_x_init ; 3066uint32_t wg_y_init ; 3067const uint32_t total_wg_init = npr * nrows ; 3068const uint32_t max_wg_per_dim = ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension ; 3069compute_2d_workgroups ( total_wg_init , max_wg_per_dim , wg_x_init , wg_y_init ); 3070 3071std::vector < wgpu::BindGroupEntry > init_entries = { 3072ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src ), 3073ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( dst ), init_align_offset , init_binding_size ) 3074}; 3075 3076dispatches . push_back ({ 3077argsort_pipeline , std:: move ( init_params ), std:: move ( init_entries ), { wg_x_init , wg_y_init } 3078}); 3079 3080if ( merge_passes == 0 ) { 3081return ggml_backend_webgpu_build_multi ( ctx , dispatches ); 3082} 3083 3084bool in_is_tmp = start_in_tmp ; 3085uint32_t len = block_size ; 3086while ( len < out_ne0 ) { 3087const uint32_t nm = CEIL_DIV ( out_ne0 , 2 * len ); 3088 3089const bool out_is_tmp = ! in_is_tmp ; 3090const uint32_t offset_in = in_is_tmp ? offset_tmp : offset_dst ; 3091const uint32_t offset_out = out_is_tmp ? offset_tmp : offset_dst ; 3092const size_t align_in = in_is_tmp ? tmp_offset : ggml_webgpu_tensor_align_offset ( ctx , dst ); 3093const size_t align_out = out_is_tmp ? tmp_offset : ggml_webgpu_tensor_align_offset ( ctx , dst ); 3094const size_t size_in = in_is_tmp ? tmp_binding_size : dst_binding_size ; 3095const size_t size_out = out_is_tmp ? tmp_binding_size : dst_binding_size ; 3096const uint32_t top_k_out = ( is_top_k && nm == 1 ) ? (uint32_t) dst -> ne [ 0 ] : out_ne0 ; 3097const uint32_t stride_out1 = top_k_out ; 3098const uint32_t stride_out2 = top_k_out * (uint32_t) dst -> ne [ 1 ]; 3099const uint32_t stride_out3 = stride_out2 * (uint32_t) dst -> ne [ 2 ]; 3100 3101std::vector < uint32_t > merge_params = { offset_src , 3102offset_in , 3103offset_out , 3104stride_src1 , 3105stride_src2 , 3106stride_src3 , 3107stride_idx1 , 3108stride_idx2 , 3109stride_idx3 , 3110stride_out1 , 3111stride_out2 , 3112stride_out3 , 3113out_ne0 , 3114(uint32_t) src -> ne [ 1 ], 3115(uint32_t) src -> ne [ 2 ], 3116top_k_out , 3117len , 3118nm , 3119nrows }; 3120 3121std::vector < wgpu::BindGroupEntry > merge_entries = { 3122ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src ), 3123ggml_webgpu_make_bind_group_entry ( 1 , ggml_webgpu_tensor_buf ( dst ), align_in , size_in ), 3124ggml_webgpu_make_bind_group_entry ( 2 , ggml_webgpu_tensor_buf ( dst ), align_out , size_out ) 3125}; 3126 3127uint32_t wg_x_merge ; 3128uint32_t wg_y_merge ; 3129const uint32_t total_wg_merge = nm * nrows ; 3130compute_2d_workgroups ( total_wg_merge , max_wg_per_dim , wg_x_merge , wg_y_merge ); 3131 3132dispatches . push_back ({ 3133argsort_merge_pipeline , std:: move ( merge_params ), std:: move ( merge_entries ), { wg_x_merge , wg_y_merge } 3134}); 3135 3136len <<= 1 ; 3137in_is_tmp = ! in_is_tmp ; 3138} 3139 3140return ggml_backend_webgpu_build_multi ( ctx , dispatches ); 3141} 3142 3143static webgpu_encoded_op ggml_webgpu_cumsum (webgpu_context & ctx , ggml_tensor * src , ggml_tensor * dst ) { 3144std::vector < uint32_t > params = { (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )), 3145(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 3146(uint32_t) src -> ne [ 0 ] }; 3147 3148std::vector < wgpu::BindGroupEntry > entries = { ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src ), 3149ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst ) }; 3150 3151ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 3152shader_lib_ctx . src0 = src ; 3153shader_lib_ctx . src1 = nullptr ; 3154shader_lib_ctx . dst = dst ; 3155shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 3156 3157webgpu_pipeline pipeline = ctx -> shader_lib -> get_cumsum_pipeline ( shader_lib_ctx ); 3158uint32_t wg_x = ggml_nrows ( dst ); 3159return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x ); 3160} 3161 3162static webgpu_encoded_op ggml_webgpu_sum_rows (webgpu_context & ctx , ggml_tensor * src , ggml_tensor * dst ) { 3163bool total_sum = dst -> op == GGML_OP_SUM ; 3164std::vector < uint32_t > params = { (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )), 3165(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 3166total_sum ? 0 : (uint32_t) ( src -> nb [ 1 ] / ggml_type_size ( src -> type )), 3167total_sum ? 0 : (uint32_t) ( src -> nb [ 2 ] / ggml_type_size ( src -> type )), 3168total_sum ? 0 : (uint32_t) ( src -> nb [ 3 ] / ggml_type_size ( src -> type )), 3169total_sum ? static_cast < uint32_t > ( ggml_nelements ( src )) : (uint32_t) src -> ne [ 0 ], 3170total_sum ? 1 : (uint32_t) src -> ne [ 1 ], 3171total_sum ? 1 : (uint32_t) src -> ne [ 2 ] }; 3172 3173std::vector < wgpu::BindGroupEntry > entries = { ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src ), 3174ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst ) }; 3175 3176ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 3177shader_lib_ctx . src0 = src ; 3178shader_lib_ctx . dst = dst ; 3179shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 3180 3181webgpu_pipeline pipeline = ctx -> shader_lib -> get_sum_rows_pipeline ( shader_lib_ctx ); 3182 3183uint32_t wg_x = total_sum ? 1 : ggml_nrows ( dst ); 3184return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x ); 3185} 3186 3187static bool ggml_webgpu_can_fuse_rms_norm_mul ( const struct ggml_cgraph * cgraph , int node_idx ) { 3188if (! ggml_can_fuse ( cgraph , node_idx , { GGML_OP_RMS_NORM , GGML_OP_MUL })) { 3189return false; 3190} 3191 3192// additional constraints specific to this fusion 3193const ggml_tensor * rms_norm = cgraph -> nodes [ node_idx ]; 3194const ggml_tensor * mul = cgraph -> nodes [ node_idx + 1 ]; 3195 3196GGML_ASSERT ( rms_norm -> src [ 0 ] -> type == GGML_TYPE_F32 ); 3197GGML_ASSERT ( rms_norm -> type == GGML_TYPE_F32 ); 3198// rms_norm only supports f32 3199if ( mul -> src [ 0 ] -> type != GGML_TYPE_F32 || mul -> src [ 1 ] -> type != GGML_TYPE_F32 || mul -> type != GGML_TYPE_F32 ) { 3200return false; 3201} 3202// if rms_norm is the B operand, then we don't handle broadcast 3203if ( rms_norm == mul -> src [ 1 ] && ! ggml_are_same_shape ( mul -> src [ 0 ], rms_norm )) { 3204return false; 3205} 3206// rms_norm shader assumes contiguous rows 3207if (! ggml_is_contiguous_rows ( mul -> src [ 0 ]) || ! ggml_is_contiguous_rows ( mul -> src [ 1 ])) { 3208return false; 3209} 3210 3211return true; 3212} 3213 3214static webgpu_encoded_op ggml_webgpu_upscale (webgpu_context ctx , ggml_tensor * src , ggml_tensor * dst ) { 3215const uint32_t mode_flags = (uint32_t) ggml_get_op_params_i32 ( dst , 0 ); 3216std::vector < uint32_t > params = { (uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , src ) / ggml_type_size ( src -> type )), 3217(uint32_t) ( ggml_webgpu_tensor_misalignment ( ctx , dst ) / ggml_type_size ( dst -> type )), 3218 3219(uint32_t) ( src -> nb [ 0 ] / ggml_type_size ( src -> type )), 3220(uint32_t) ( src -> nb [ 1 ] / ggml_type_size ( src -> type )), 3221(uint32_t) ( src -> nb [ 2 ] / ggml_type_size ( src -> type )), 3222(uint32_t) ( src -> nb [ 3 ] / ggml_type_size ( src -> type )), 3223 3224(uint32_t) ( dst -> nb [ 0 ] / ggml_type_size ( dst -> type )), 3225(uint32_t) ( dst -> nb [ 1 ] / ggml_type_size ( dst -> type )), 3226(uint32_t) ( dst -> nb [ 2 ] / ggml_type_size ( dst -> type )), 3227(uint32_t) ( dst -> nb [ 3 ] / ggml_type_size ( dst -> type )), 3228 3229(uint32_t) src -> ne [ 0 ], 3230(uint32_t) src -> ne [ 1 ], 3231(uint32_t) src -> ne [ 2 ], 3232(uint32_t) src -> ne [ 3 ], 3233 3234(uint32_t) dst -> ne [ 0 ], 3235(uint32_t) dst -> ne [ 1 ], 3236(uint32_t) dst -> ne [ 2 ], 3237(uint32_t) dst -> ne [ 3 ], 3238 3239mode_flags }; 3240 3241std::vector < wgpu::BindGroupEntry > entries = { ggml_webgpu_make_tensor_bind_group_entry ( ctx , 0 , src ), 3242ggml_webgpu_make_tensor_bind_group_entry ( ctx , 1 , dst ) }; 3243 3244ggml_webgpu_shader_lib_context shader_lib_ctx = {}; 3245shader_lib_ctx . src0 = src ; 3246shader_lib_ctx . dst = dst ; 3247shader_lib_ctx . max_wg_size = ctx -> global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 3248 3249webgpu_pipeline pipeline = ctx -> shader_lib -> get_upscale_pipeline ( shader_lib_ctx ); 3250auto * decisions = static_cast < ggml_webgpu_generic_shader_decisions *> ( pipeline . context . get ()); 3251 3252uint32_t wg_x ; 3253uint32_t wg_y ; 3254uint32_t total_wg = CEIL_DIV ((uint32_t) ggml_nelements ( dst ), decisions -> wg_size ); 3255compute_2d_workgroups ( total_wg , ctx -> global_ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension , wg_x , wg_y ); 3256 3257return ggml_backend_webgpu_build ( ctx , pipeline , params , entries , wg_x , wg_y ); 3258} 3259 3260// Returns the encoded command, or std::nullopt if the operation is a no-op 3261static std::optional < webgpu_encoded_op > ggml_webgpu_encode (webgpu_context ctx , 3262ggml_cgraph * cgraph , 3263int node_idx , 3264int & num_encoded_ops ) { 3265ggml_tensor ** nodes = cgraph -> nodes ; 3266ggml_tensor * node = nodes [ node_idx ]; 3267 3268if ( ggml_is_empty ( node )) { 3269return std:: nullopt ; 3270} 3271if (( node -> flags & GGML_TENSOR_FLAG_COMPUTE ) == 0 ) { 3272return std:: nullopt ; 3273} 3274WEBGPU_LOG_DEBUG ( "ggml_webgpu_encode(" << node << ", " << ggml_op_name ( node -> op ) << ")" ); 3275 3276ggml_tensor * src0 = node -> src [ 0 ]; 3277ggml_tensor * src1 = node -> src [ 1 ]; 3278ggml_tensor * src2 = node -> src [ 2 ]; 3279 3280switch ( node -> op ) { 3281// no-ops 3282case GGML_OP_NONE : 3283case GGML_OP_VIEW : 3284case GGML_OP_PERMUTE : 3285case GGML_OP_TRANSPOSE : 3286case GGML_OP_RESHAPE : 3287return std:: nullopt ; 3288case GGML_OP_CPY : 3289case GGML_OP_CONT : 3290return ggml_webgpu_cpy ( ctx , src0 , node ); 3291case GGML_OP_SET : 3292return ggml_webgpu_set ( ctx , src0 , src1 , node ); 3293case GGML_OP_SET_ROWS : 3294return ggml_webgpu_set_rows ( ctx , src0 , src1 , node ); 3295case GGML_OP_GET_ROWS : 3296return ggml_webgpu_get_rows ( ctx , src0 , src1 , node ); 3297case GGML_OP_MUL_MAT : 3298return ggml_webgpu_mul_mat ( ctx , src0 , src1 , node ); 3299case GGML_OP_MUL_MAT_ID : 3300return ggml_webgpu_mul_mat_id ( ctx , src0 , src1 , src2 , node ); 3301case GGML_OP_FLASH_ATTN_EXT : 3302return ggml_webgpu_flash_attn ( ctx , src0 , src1 , src2 , node -> src [ 3 ], node -> src [ 4 ], node ); 3303case GGML_OP_ADD : 3304case GGML_OP_SUB : 3305case GGML_OP_MUL : 3306case GGML_OP_DIV : 3307return ggml_webgpu_binary_op ( ctx , src0 , src1 , node ); 3308case GGML_OP_ADD_ID : 3309return ggml_webgpu_add_id ( ctx , src0 , src1 , src2 , node ); 3310case GGML_OP_CONCAT : 3311return ggml_webgpu_concat ( ctx , src0 , src1 , node ); 3312case GGML_OP_REPEAT : 3313return ggml_webgpu_repeat ( ctx , src0 , node ); 3314case GGML_OP_RMS_NORM : 3315if ( ggml_webgpu_can_fuse_rms_norm_mul ( cgraph , node_idx )) { 3316num_encoded_ops = 2 ; 3317ggml_tensor * mul_node = nodes [ node_idx + 1 ]; 3318return ggml_webgpu_rms_norm_mul ( ctx , src0 , node , mul_node -> src [ 0 ], mul_node -> src [ 1 ], mul_node ); 3319} else { 3320return ggml_webgpu_row_norm ( ctx , src0 , node ); 3321} 3322case GGML_OP_NORM : 3323case GGML_OP_L2_NORM : 3324return ggml_webgpu_row_norm ( ctx , src0 , node ); 3325case GGML_OP_ROPE : 3326return ggml_webgpu_rope ( ctx , src0 , src1 , src2 , node ); 3327case GGML_OP_GLU : 3328return ggml_webgpu_glu ( ctx , src0 , src1 , node ); 3329case GGML_OP_SCALE : 3330return ggml_webgpu_scale ( ctx , src0 , node ); 3331case GGML_OP_SOFT_MAX : 3332return ggml_webgpu_soft_max ( ctx , src0 , src1 , src2 , node ); 3333case GGML_OP_UNARY : 3334case GGML_OP_CLAMP : 3335case GGML_OP_FILL : 3336case GGML_OP_LOG : 3337case GGML_OP_SQR : 3338case GGML_OP_SQRT : 3339case GGML_OP_SIN : 3340case GGML_OP_COS : 3341case GGML_OP_DIAG : 3342case GGML_OP_TRI : 3343return ggml_webgpu_unary_op ( ctx , src0 , node ); 3344case GGML_OP_SOLVE_TRI : 3345return ggml_webgpu_solve_tri ( ctx , src0 , src1 , node ); 3346case GGML_OP_SSM_CONV : 3347return ggml_webgpu_ssm_conv ( ctx , src0 , src1 , node ); 3348case GGML_OP_SSM_SCAN : 3349return ggml_webgpu_ssm_scan ( ctx , src0 , src1 , src2 , node -> src [ 3 ], node -> src [ 4 ], node -> src [ 5 ], node -> src [ 6 ], 3350node ); 3351case GGML_OP_GATED_DELTA_NET : 3352return ggml_webgpu_gated_delta_net ( ctx , src0 , src1 , src2 , node -> src [ 3 ], node -> src [ 4 ], node -> src [ 5 ], node ); 3353case GGML_OP_PAD : 3354return ggml_webgpu_pad ( ctx , src0 , node ); 3355case GGML_OP_ARGMAX : 3356return ggml_webgpu_argmax ( ctx , src0 , node ); 3357case GGML_OP_ARGSORT : 3358case GGML_OP_TOP_K : 3359// we reuse the same argsort implementation for top_k 3360return ggml_webgpu_argsort ( ctx , src0 , node ); 3361case GGML_OP_CUMSUM : 3362return ggml_webgpu_cumsum ( ctx , src0 , node ); 3363case GGML_OP_SUM : 3364case GGML_OP_SUM_ROWS : 3365return ggml_webgpu_sum_rows ( ctx , src0 , node ); 3366case GGML_OP_CONV_2D : 3367return ggml_webgpu_conv_2d ( ctx , src0 , src1 , node ); 3368case GGML_OP_CONV_2D_DW : 3369return ggml_webgpu_conv_2d_dw ( ctx , src0 , src1 , node ); 3370case GGML_OP_IM2COL : 3371return ggml_webgpu_im2col ( ctx , src0 , src1 , node ); 3372case GGML_OP_UPSCALE : 3373return ggml_webgpu_upscale ( ctx , src0 , node ); 3374default : 3375return std:: nullopt ; 3376} 3377} 3378 3379#ifdef GGML_WEBGPU_GPU_PROFILE 3380static void ggml_backend_webgpu_collect_profile_results (webgpu_context & ctx , 3381const std::vector < std::string > & pipeline_names , 3382uint32_t & num_inflight_batches ) { 3383if ( pipeline_names . empty ()) { 3384return ; 3385} 3386 3387wgpu::CommandEncoder encoder = ctx -> global_ctx -> device . CreateCommandEncoder (); 3388encoder . ResolveQuerySet ( ctx -> profile_timestamp_query_set , 0 , ctx -> profile_timestamp_query_count , 3389ctx -> profile_timestamp_dev_buf , 0 ); 3390encoder . CopyBufferToBuffer ( ctx -> profile_timestamp_dev_buf , 0 , ctx -> profile_timestamp_host_buf , 0 , 3391ctx -> profile_timestamp_query_count * sizeof (uint64_t)); 3392 3393wgpu::CommandBuffer profile_commands = encoder . Finish (); 3394ggml_backend_webgpu_submit_commands ( ctx , profile_commands , num_inflight_batches ); 3395 3396const size_t mapped_size = ctx -> profile_timestamp_query_count * sizeof (uint64_t); 3397GGML_ASSERT ( ctx -> profile_timestamp_query_count == 2 * pipeline_names . size ()); 3398 3399ggml_backend_webgpu_map_buffer ( ctx -> global_ctx , ctx -> profile_timestamp_host_buf , wgpu::MapMode:: Read , 0 , 3400mapped_size ); 3401const uint64_t * ts_data = ( const uint64_t * ) ctx -> profile_timestamp_host_buf . GetConstMappedRange ( 0 , mapped_size ); 3402 3403for (size_t i = 0 ; i < pipeline_names . size (); ++ i ) { 3404// WebGPU timestamps are in ns; convert to ms. 3405const double elapsed_ms = double( ts_data [ 2 * i + 1 ] - ts_data [ 2 * i ]) * 1e-6 ; 3406ctx -> shader_gpu_time_ms [ pipeline_names [ i ]] += elapsed_ms ; 3407} 3408 3409ctx -> profile_timestamp_host_buf . Unmap (); 3410} 3411#endif 3412 3413// Don't bother checking set_rows index overflow for now, since practically the WebGPU doesn't need to support 3414// models that would require it right now. 3415static void ggml_backend_webgpu_check_set_rows (webgpu_context & ctx , uint32_t & num_inflight_batches ) { 3416#ifdef GGML_WEBGPU_CHECK_SET_ROWS 3417wgpu::CommandEncoder encoder = ctx -> global_ctx -> device . CreateCommandEncoder (); 3418encoder . CopyBufferToBuffer ( ctx -> set_rows_dev_error_buf , 0 , ctx -> set_rows_host_error_buf , 0 , 3419ctx -> set_rows_host_error_buf . GetSize ()); 3420wgpu::CommandBuffer commands = encoder . Finish (); 3421ggml_backend_webgpu_submit_commands ( ctx , commands , num_inflight_batches ); 3422ggml_backend_webgpu_map_buffer ( ctx -> global_ctx , ctx -> set_rows_host_error_buf , wgpu::MapMode:: Read , 0 , 3423ctx -> set_rows_host_error_buf . GetSize ()); 3424const uint32_t * error_data = ( const uint32_t * ) ctx -> set_rows_host_error_buf . GetConstMappedRange (); 3425if ( * error_data ) { 3426GGML_ABORT ( "ggml_webgpu: SET_ROWS index > 2^32, unsupported." ); 3427} 3428ctx -> set_rows_host_error_buf . Unmap (); 3429#else 3430GGML_UNUSED ( ctx ); 3431GGML_UNUSED ( num_inflight_batches ); 3432#endif 3433} 3434 3435static ggml_status ggml_backend_webgpu_graph_compute (ggml_backend_t backend , struct ggml_cgraph * cgraph ) { 3436WEBGPU_LOG_DEBUG ( "ggml_backend_webgpu_graph_compute(" << cgraph -> n_nodes << " nodes)" ); 3437 3438ggml_backend_webgpu_context * backend_ctx = (ggml_backend_webgpu_context * ) backend -> context ; 3439webgpu_context ctx = backend_ctx -> webgpu_ctx ; 3440 3441WEBGPU_CPU_PROFILE_TOTAL_START ( graph_compute ); 3442 3443std::vector < webgpu_encoded_op > commands ; 3444 3445uint32_t num_batched_kernels = 0 ; 3446uint32_t num_inflight_batches = 0 ; 3447bool contains_set_rows = false; 3448int num_encoded_ops = 1 ; 3449int node_idx = 0 ; 3450 3451#ifdef GGML_WEBGPU_GPU_PROFILE 3452ctx -> profile_timestamp_query_count = 0 ; 3453std::vector < std::string > profile_pipeline_names ; 3454#endif 3455 3456ctx -> active_command_encoder = ctx -> global_ctx -> device . CreateCommandEncoder (); 3457if ( ctx -> batch_compute_passes ) { 3458ctx -> active_compute_pass = ctx -> active_command_encoder . BeginComputePass (); 3459} 3460 3461while ( node_idx < cgraph -> n_nodes ) { 3462if ( cgraph -> nodes [ node_idx ] -> op == GGML_OP_SET_ROWS ) { 3463contains_set_rows = true; 3464} 3465if (auto cmd = ggml_webgpu_encode ( ctx , cgraph , node_idx , num_encoded_ops )) { 3466commands . push_back ( * cmd ); 3467num_batched_kernels += cmd . value (). num_kernels ; 3468#ifdef GGML_WEBGPU_GPU_PROFILE 3469profile_pipeline_names . insert ( profile_pipeline_names . end (), cmd -> pipeline_names . begin (), 3470cmd -> pipeline_names . end ()); 3471#endif 3472} 3473 3474if ( num_batched_kernels >= ctx -> global_ctx -> command_submit_batch_size ) { 3475if ( ctx -> active_compute_pass ) { 3476ctx -> active_compute_pass . End (); 3477} 3478num_batched_kernels = 0 ; 3479wgpu::CommandBuffer batch_commands = ctx -> active_command_encoder . Finish (); 3480ggml_backend_webgpu_submit_commands ( ctx , batch_commands , num_inflight_batches ); 3481 3482// reset state for next batch 3483ctx -> active_command_encoder = ctx -> global_ctx -> device . CreateCommandEncoder (); 3484if ( ctx -> batch_compute_passes ) { 3485ctx -> active_compute_pass = ctx -> active_command_encoder . BeginComputePass (); 3486} 3487ctx -> param_arena . reset (); 3488commands . clear (); 3489#ifdef GGML_WEBGPU_GPU_PROFILE 3490// flush before the next batch can overflow the QuerySet 3491if ( ctx -> profile_timestamp_query_count + 2 * ctx -> global_ctx -> command_submit_batch_size >= 3492WEBGPU_MAX_PROFILE_QUERY_COUNT ) { 3493ggml_backend_webgpu_collect_profile_results ( ctx , profile_pipeline_names , num_inflight_batches ); 3494// reset profile timestamp state 3495ctx -> profile_timestamp_query_count = 0 ; 3496profile_pipeline_names . clear (); 3497} 3498#endif 3499} 3500 3501node_idx += num_encoded_ops ; 3502num_encoded_ops = 1 ; 3503} 3504 3505if ( ctx -> active_compute_pass ) { 3506ctx -> active_compute_pass . End (); 3507ctx -> active_compute_pass = nullptr ; 3508} 3509 3510if ( num_batched_kernels > 0 ) { 3511wgpu::CommandBuffer batch_commands = ctx -> active_command_encoder . Finish (); 3512ggml_backend_webgpu_submit_commands ( ctx , batch_commands , num_inflight_batches ); 3513ctx -> param_arena . reset (); 3514commands . clear (); 3515} 3516ctx -> active_command_encoder = nullptr ; 3517 3518#ifdef GGML_WEBGPU_GPU_PROFILE 3519ggml_backend_webgpu_collect_profile_results ( ctx , profile_pipeline_names , num_inflight_batches ); 3520#endif 3521 3522if ( contains_set_rows ) { 3523ggml_backend_webgpu_check_set_rows ( ctx , num_inflight_batches ); 3524} 3525 3526WEBGPU_CPU_PROFILE_TOTAL_END ( graph_compute , ctx -> global_ctx ); 3527return GGML_STATUS_SUCCESS ; 3528} 3529 3530struct ggml_backend_webgpu_event_context { 3531webgpu_global_context global_ctx ; 3532wgpu::Future future ; 3533bool recorded = false; 3534}; 3535 3536static ggml_backend_event_t ggml_backend_webgpu_device_event_new (ggml_backend_dev_t device ) { 3537ggml_backend_webgpu_device_context * dev_ctx = (ggml_backend_webgpu_device_context * ) device -> context ; 3538 3539auto * event_ctx = new ggml_backend_webgpu_event_context(); 3540event_ctx -> global_ctx = dev_ctx -> webgpu_global_ctx ; 3541 3542auto * event = new ggml_backend_event; 3543event -> device = device ; 3544event -> context = event_ctx ; 3545return event ; 3546} 3547 3548static void ggml_backend_webgpu_device_event_free (ggml_backend_dev_t dev , ggml_backend_event_t event ) { 3549GGML_UNUSED ( dev ); 3550delete static_cast < ggml_backend_webgpu_event_context *> ( event -> context ); 3551delete event ; 3552} 3553 3554static void ggml_backend_webgpu_device_event_synchronize (ggml_backend_dev_t dev , ggml_backend_event_t event ) { 3555GGML_UNUSED ( dev ); 3556ggml_backend_webgpu_event_context * event_ctx = (ggml_backend_webgpu_event_context * ) event -> context ; 3557if (! event_ctx -> recorded ) { 3558return ; 3559} 3560wgpu::WaitStatus status = 3561event_ctx -> global_ctx -> instance . WaitAny ( event_ctx -> future , WEBGPU_RUNTIME_WAIT_TIMEOUT_NS ); 3562if ( status == wgpu::WaitStatus:: TimedOut ) { 3563GGML_ABORT ( "ggml_webgpu: event_synchronize timed out after %u ms\n" , WEBGPU_RUNTIME_WAIT_TIMEOUT_MS ); 3564} 3565event_ctx -> recorded = false; 3566} 3567 3568static void ggml_backend_webgpu_event_record (ggml_backend_t backend , ggml_backend_event_t event ) { 3569ggml_backend_webgpu_context * backend_ctx = (ggml_backend_webgpu_context * ) backend -> context ; 3570ggml_backend_webgpu_event_context * event_ctx = (ggml_backend_webgpu_event_context * ) event -> context ; 3571 3572event_ctx -> future = backend_ctx -> webgpu_ctx -> global_ctx -> queue . OnSubmittedWorkDone ( 3573wgpu::CallbackMode:: AllowSpontaneous , [](wgpu::QueueWorkDoneStatus, wgpu::StringView) {}); 3574event_ctx -> recorded = true; 3575} 3576 3577static void ggml_backend_webgpu_event_wait (ggml_backend_t backend , ggml_backend_event_t event ) { 3578GGML_UNUSED ( backend ); 3579ggml_backend_webgpu_device_event_synchronize ( nullptr , event ); 3580} 3581 3582static void ggml_backend_webgpu_set_tensor_async (ggml_backend_t backend , 3583ggml_tensor * tensor , 3584const void * data , 3585size_t offset , 3586size_t size ) { 3587GGML_UNUSED ( backend ); 3588auto * buf_ctx = (ggml_backend_webgpu_buffer_context * ) tensor -> buffer -> context ; 3589size_t total_offset = ggml_webgpu_tensor_offset ( tensor ) + offset ; 3590 3591// Write aligned portion 3592buf_ctx -> global_ctx -> queue . WriteBuffer ( buf_ctx -> buffer , total_offset , data , ( size / 4 ) * 4 ); 3593 3594if ( size % 4 != 0 ) { 3595// If size is not a multiple of 4, we need to memset the remaining bytes 3596size_t remaining_size = size % 4 ; 3597 3598// pack the remaining bytes into a uint32_t 3599uint32_t val32 = 0 ; 3600 3601for (size_t i = 0 ; i < remaining_size ; i ++ ) { 3602((uint8_t * ) & val32 )[ i ] = (( const uint8_t * ) data )[ size - remaining_size + i ]; 3603} 3604// memset the remaining bytes 3605ggml_backend_webgpu_buffer_memset ( buf_ctx -> global_ctx , buf_ctx -> buffer , val32 , 3606total_offset + ( size - remaining_size ), remaining_size ); 3607} 3608} 3609 3610static void ggml_backend_webgpu_synchronize (ggml_backend_t backend ) { 3611ggml_backend_webgpu_context * backend_ctx = (ggml_backend_webgpu_context * ) backend -> context ; 3612ggml_backend_webgpu_wait_queue ( backend_ctx -> webgpu_ctx -> global_ctx ); 3613} 3614 3615static ggml_backend_i ggml_backend_webgpu_i = { 3616/* .get_name = */ ggml_backend_webgpu_name , 3617/* .free = */ ggml_backend_webgpu_free , 3618/* .set_tensor_async = */ ggml_backend_webgpu_set_tensor_async , 3619/* .get_tensor_async = */ NULL , 3620/* .set_tensor_2d_async = */ NULL , 3621/* .get_tensor_2d_async = */ NULL , 3622/* .cpy_tensor_async = */ NULL , 3623/* .synchronize = */ ggml_backend_webgpu_synchronize , 3624/* .graph_plan_create = */ NULL , 3625/* .graph_plan_free = */ NULL , 3626/* .graph_plan_update = */ NULL , 3627/* .graph_plan_compute = */ NULL , 3628/* .graph_compute = */ ggml_backend_webgpu_graph_compute , 3629/* .event_record = */ ggml_backend_webgpu_event_record , 3630/* .event_wait = */ ggml_backend_webgpu_event_wait , 3631/* .graph_optimize = */ NULL , 3632}; 3633 3634/* End GGML Backend Interface */ 3635 3636/* GGML Backend Buffer Interface */ 3637 3638static void ggml_backend_webgpu_buffer_free_buffer (ggml_backend_buffer_t buffer ) { 3639ggml_backend_webgpu_buffer_context * ctx = static_cast < ggml_backend_webgpu_buffer_context *> ( buffer -> context ); 3640if ( ctx != nullptr && ctx -> buffer != nullptr ) { 3641ctx -> buffer . Destroy (); 3642delete ctx ; 3643} 3644} 3645 3646// Returns the "fake" base pointer. 3647static void * ggml_backend_webgpu_buffer_get_base (ggml_backend_buffer_t buffer ) { 3648GGML_UNUSED ( buffer ); 3649return webgpu_ptr_base ; 3650} 3651 3652static void ggml_backend_webgpu_buffer_memset_tensor (ggml_backend_buffer_t buffer , 3653ggml_tensor * tensor , 3654uint8_t value , 3655size_t offset , 3656size_t size ) { 3657if ( size == 0 ) { 3658WEBGPU_LOG_DEBUG ( 3659"ggml_backend_webgpu_buffer_memset_tensor: size is zero, " 3660"nothing to do." ); 3661return ; 3662} 3663 3664WEBGPU_CPU_PROFILE_TOTAL_START ( memset_tensor ); 3665 3666ggml_backend_webgpu_buffer_context * buf_ctx = (ggml_backend_webgpu_buffer_context * ) buffer -> context ; 3667 3668WEBGPU_LOG_DEBUG ( "ggml_backend_webgpu_buffer_memset_tensor(" << buf_ctx -> label << ", " << tensor << ", " << value 3669<< ", " << offset << ", " << size << ")" ); 3670 3671size_t total_offset = ggml_webgpu_tensor_offset ( tensor ) + offset ; 3672 3673// This is a trick to set all bytes of a u32 to the same 1 byte value. 3674uint32_t val32 = (uint32_t) value * 0x01010101 ; 3675ggml_backend_webgpu_buffer_memset ( buf_ctx -> global_ctx , buf_ctx -> buffer , val32 , total_offset , size ); 3676WEBGPU_CPU_PROFILE_TOTAL_END ( memset_tensor , buf_ctx -> global_ctx ); 3677} 3678 3679static void ggml_backend_webgpu_buffer_set_tensor (ggml_backend_buffer_t buffer , 3680ggml_tensor * tensor , 3681const void * data , 3682size_t offset , 3683size_t size ) { 3684WEBGPU_CPU_PROFILE_TOTAL_START ( set_tensor ); 3685ggml_backend_webgpu_buffer_context * buf_ctx = (ggml_backend_webgpu_buffer_context * ) buffer -> context ; 3686 3687WEBGPU_LOG_DEBUG ( "ggml_backend_webgpu_buffer_set_tensor(" << buf_ctx -> label << ", " << tensor << ", " << data 3688<< ", " << offset << ", " << size << ")" ); 3689 3690size_t total_offset = ggml_webgpu_tensor_offset ( tensor ) + offset ; 3691 3692buf_ctx -> global_ctx -> queue . WriteBuffer ( buf_ctx -> buffer , total_offset , data , ( size / 4 ) * 4 ); 3693 3694if ( size % 4 != 0 ) { 3695// If size is not a multiple of 4, we need to memset the remaining bytes 3696size_t remaining_size = size % 4 ; 3697 3698// pack the remaining bytes into a uint32_t 3699uint32_t val32 = 0 ; 3700 3701for (size_t i = 0 ; i < remaining_size ; i ++ ) { 3702((uint8_t * ) & val32 )[ i ] = (( const uint8_t * ) data )[ size - remaining_size + i ]; 3703} 3704// memset the remaining bytes 3705ggml_backend_webgpu_buffer_memset ( buf_ctx -> global_ctx , buf_ctx -> buffer , val32 , 3706total_offset + ( size - remaining_size ), remaining_size ); 3707} 3708WEBGPU_CPU_PROFILE_TOTAL_END ( set_tensor , buf_ctx -> global_ctx ); 3709} 3710 3711static void ggml_backend_webgpu_buffer_get_tensor (ggml_backend_buffer_t buffer , 3712const ggml_tensor * tensor , 3713void * data , 3714size_t offset , 3715size_t size ) { 3716WEBGPU_CPU_PROFILE_TOTAL_START ( get_tensor ); 3717ggml_backend_webgpu_buffer_context * buf_ctx = (ggml_backend_webgpu_buffer_context * ) buffer -> context ; 3718WEBGPU_LOG_DEBUG ( "ggml_backend_webgpu_buffer_get_tensor(" << buf_ctx -> label << ", " << tensor << ", " << data 3719<< ", " << offset << ", " << size << ")" ); 3720wgpu::Device device = buf_ctx -> global_ctx -> device ; 3721 3722size_t total_offset = ggml_webgpu_tensor_offset ( tensor ) + offset ; 3723 3724size_t local_offset = total_offset % 4 ; 3725if ( local_offset != 0 ) { 3726// If offset is not a multiple of 4, we need to round it down to the previous 3727// multiple of 4 3728total_offset -= local_offset ; 3729} 3730 3731size_t final_size = size + local_offset ; 3732if ( final_size % 4 != 0 ) { 3733// If size is not a multiple of 4, we need to round it up to the next 3734// multiple of 4 3735final_size += 4 - ( final_size % 4 ); 3736} 3737 3738std::lock_guard < std::recursive_mutex > lock ( buf_ctx -> global_ctx -> mutex ); 3739 3740if ( buf_ctx -> global_ctx -> get_tensor_staging_buf == nullptr || 3741buf_ctx -> global_ctx -> get_tensor_staging_buf . GetSize () < final_size ) { 3742// Create a new staging buffer if it doesn't exist or is too small 3743if ( buf_ctx -> global_ctx -> get_tensor_staging_buf ) { 3744buf_ctx -> global_ctx -> get_tensor_staging_buf . Destroy (); 3745} 3746ggml_webgpu_create_buffer ( device , buf_ctx -> global_ctx -> get_tensor_staging_buf , final_size , 3747wgpu::BufferUsage:: CopyDst | wgpu::BufferUsage:: MapRead , "get_tensor_staging_buf" ); 3748} 3749 3750// Copy the data from the buffer to the staging buffer 3751wgpu::CommandEncoder encoder = device . CreateCommandEncoder (); 3752encoder . CopyBufferToBuffer ( buf_ctx -> buffer , total_offset , buf_ctx -> global_ctx -> get_tensor_staging_buf , 0 , 3753final_size ); 3754wgpu::CommandBuffer commands = encoder . Finish (); 3755 3756// Submit the command buffer to the queue 3757buf_ctx -> global_ctx -> queue . Submit ( 1 , & commands ); 3758 3759// Map the staging buffer to read the data 3760ggml_backend_webgpu_map_buffer ( buf_ctx -> global_ctx , buf_ctx -> global_ctx -> get_tensor_staging_buf , 3761wgpu::MapMode:: Read , 0 , final_size ); 3762// Must specify size here since the staging buffer might be larger than the tensor size 3763const void * mapped_range = buf_ctx -> global_ctx -> get_tensor_staging_buf . GetConstMappedRange ( 0 , final_size ); 3764 3765// Copy the data from the mapped range to the output buffer 3766std:: memcpy ( data , ( const void * ) (( const char * ) mapped_range + local_offset ), size ); 3767buf_ctx -> global_ctx -> get_tensor_staging_buf . Unmap (); 3768WEBGPU_CPU_PROFILE_TOTAL_END ( get_tensor , buf_ctx -> global_ctx ); 3769} 3770 3771static void ggml_backend_webgpu_buffer_clear (ggml_backend_buffer_t buffer , uint8_t value ) { 3772WEBGPU_LOG_DEBUG ( "ggml_backend_webgpu_buffer_clear(" << buffer << ", " << (uint32_t) value << ")" ); 3773WEBGPU_CPU_PROFILE_TOTAL_START ( clear ); 3774ggml_backend_webgpu_buffer_context * buf_ctx = (ggml_backend_webgpu_buffer_context * ) buffer -> context ; 3775ggml_backend_webgpu_buffer_memset ( buf_ctx -> global_ctx , buf_ctx -> buffer , value , 0 , buffer -> size ); 3776WEBGPU_CPU_PROFILE_TOTAL_END ( clear , buf_ctx -> global_ctx ); 3777} 3778 3779static ggml_backend_buffer_i ggml_backend_webgpu_buffer_interface = { 3780/* .free_buffer = */ ggml_backend_webgpu_buffer_free_buffer , 3781/* .get_base = */ ggml_backend_webgpu_buffer_get_base , 3782/* .init_tensor = */ NULL , // TODO: optional, needed? 3783/* .memset_tensor = */ ggml_backend_webgpu_buffer_memset_tensor , 3784/* .set_tensor = */ ggml_backend_webgpu_buffer_set_tensor , 3785/* .get_tensor = */ ggml_backend_webgpu_buffer_get_tensor , 3786/* .set_tensor_2d = */ NULL , 3787/* .get_tensor_2d = */ NULL , 3788/* .cpy_tensor = */ NULL , // TODO: optional, implement this 3789/* .clear = */ ggml_backend_webgpu_buffer_clear , 3790/* .reset = */ NULL , // TODO: optional, think it coordinates with 3791// .init_tensor 3792}; 3793 3794/* End GGML Backend Buffer Interface */ 3795 3796/* GGML Backend Buffer Type Interface */ 3797 3798static const char * ggml_backend_webgpu_buffer_type_get_name (ggml_backend_buffer_type_t buft ) { 3799ggml_backend_webgpu_device_context * ctx = static_cast < ggml_backend_webgpu_device_context *> ( buft -> device -> context ); 3800return ctx -> device_name . c_str (); 3801} 3802 3803static ggml_backend_buffer_t ggml_backend_webgpu_buffer_type_alloc_buffer (ggml_backend_buffer_type_t buft , 3804size_t size ) { 3805static std::atomic < int > buffer_count ; 3806int buffer_id = buffer_count ++ ; 3807std::string buf_name = "tensor_buf" + std:: to_string ( buffer_id ); 3808WEBGPU_LOG_DEBUG ( "ggml_backend_webgpu_buffer_type_alloc_buffer_" << buffer_id << ": " << size << " bytes" ); 3809 3810ggml_backend_webgpu_device_context * ctx = static_cast < ggml_backend_webgpu_device_context *> ( buft -> device -> context ); 3811wgpu::Buffer buf ; 3812ggml_webgpu_create_buffer ( ctx -> webgpu_global_ctx -> device , buf , ROUNDUP_POW2 ( size , WEBGPU_STORAGE_BUF_BINDING_MULT ), 3813wgpu::BufferUsage:: Storage | wgpu::BufferUsage:: CopySrc | wgpu::BufferUsage:: CopyDst , 3814buf_name . c_str ()); 3815 3816ggml_backend_webgpu_buffer_context * buf_ctx = 3817new ggml_backend_webgpu_buffer_context( buf , buf_name , ctx -> webgpu_global_ctx ); 3818 3819return ggml_backend_buffer_init ( buft , ggml_backend_webgpu_buffer_interface , buf_ctx , size ); 3820} 3821 3822static size_t ggml_backend_webgpu_buffer_type_get_alignment (ggml_backend_buffer_type_t buft ) { 3823ggml_backend_webgpu_device_context * dev_ctx = 3824static_cast < ggml_backend_webgpu_device_context *> ( buft -> device -> context ); 3825return dev_ctx -> webgpu_global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ; 3826} 3827 3828// maxBufferSize might be larger, but you can't bind more than 3829// maxStorageBufferBindingSize to a single binding. 3830static size_t ggml_backend_webgpu_buffer_type_get_max_size (ggml_backend_buffer_type_t buft ) { 3831ggml_backend_webgpu_device_context * dev_ctx = 3832static_cast < ggml_backend_webgpu_device_context *> ( buft -> device -> context ); 3833return dev_ctx -> webgpu_global_ctx -> capabilities . limits . maxStorageBufferBindingSize ; 3834} 3835 3836static size_t ggml_backend_webgpu_buffer_type_get_alloc_size (ggml_backend_buffer_type_t buft , 3837const ggml_tensor * tensor ) { 3838ggml_backend_webgpu_device_context * ctx = static_cast < ggml_backend_webgpu_device_context *> ( buft -> device -> context ); 3839size_t res = ggml_nbytes ( tensor ); 3840switch ( tensor -> op ) { 3841case GGML_OP_ARGSORT : 3842res = ROUNDUP_POW2 ( res * 2 + ctx -> webgpu_global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment , 3843WEBGPU_STORAGE_BUF_BINDING_MULT ); 3844break ; 3845case GGML_OP_TOP_K : 3846{ 3847const ggml_tensor * src0 = tensor -> src [ 0 ]; 3848if ( src0 ) { 3849const size_t full = sizeof (int32_t) * ggml_nelements ( src0 ); 3850res = ROUNDUP_POW2 ( 3851full * 2 + ctx -> webgpu_global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment , 3852WEBGPU_STORAGE_BUF_BINDING_MULT ); 3853} 3854} 3855break ; 3856case GGML_OP_FLASH_ATTN_EXT : 3857{ 3858const ggml_tensor * Q = tensor -> src [ 0 ]; 3859const ggml_tensor * K = tensor -> src [ 1 ]; 3860const ggml_tensor * V = tensor -> src [ 2 ]; 3861const ggml_tensor * mask = tensor -> src [ 3 ]; 3862const auto & capabilities = ctx -> webgpu_global_ctx -> capabilities ; 3863if ( ggml_webgpu_flash_attn_use_vec_path ( ctx -> webgpu_global_ctx , Q , K , V )) { 3864const bool kv_direct = 3865ggml_webgpu_flash_attn_k_direct ( Q , K , GGML_WEBGPU_FLASH_ATTN_TILE_KV_VEC_WIDTH ) || 3866ggml_webgpu_flash_attn_v_direct ( Q , V , GGML_WEBGPU_FLASH_ATTN_TILE_KV_VEC_WIDTH ); 3867const uint32_t kv_tile = ggml_webgpu_flash_attn_get_vec_kv_tile ( 3868capabilities . limits . maxComputeWorkgroupStorageSize , (uint32_t) Q -> ne [ 0 ], (uint32_t) V -> ne [ 0 ], 3869mask != nullptr , kv_direct ); 3870 3871const uint32_t vec_nwg_cap = capabilities . min_subgroup_size ; 3872uint32_t nwg = ggml_webgpu_flash_attn_vec_nwg ( vec_nwg_cap , kv_tile , (uint32_t) K -> ne [ 1 ]); 3873 3874const size_t align = capabilities . limits . minStorageBufferOffsetAlignment ; 3875const uint64_t nrows = (uint64_t) Q -> ne [ 1 ] * Q -> ne [ 2 ] * Q -> ne [ 3 ]; 3876if ( nwg > 1u ) { 3877const uint64_t tmp_data_elems = nrows * (uint64_t) V -> ne [ 0 ] * nwg ; 3878const uint64_t tmp_stats_elems = nrows * 2u * nwg ; 3879const size_t tmp_size_bytes = ROUNDUP_POW2 (( tmp_data_elems + tmp_stats_elems ) * sizeof (float), 3880WEBGPU_STORAGE_BUF_BINDING_MULT ); 3881res += tmp_size_bytes + align ; 3882} else { 3883res += WEBGPU_STORAGE_BUF_BINDING_MULT + align ; 3884} 3885if ( mask != nullptr ) { 3886const uint32_t blk_nblk0 = CEIL_DIV ((uint32_t) K -> ne [ 1 ], kv_tile ); 3887const uint32_t blk_nblk1 = CEIL_DIV ((uint32_t) Q -> ne [ 1 ], 1u ); 3888const uint32_t stride_mask3 = (uint32_t) ( mask -> nb [ 3 ] / ggml_type_size ( mask -> type )); 3889const uint32_t blk_batch_count = stride_mask3 > 0 ? (uint32_t) Q -> ne [ 3 ] : 1u ; 3890const uint64_t blk_elems = (uint64_t) blk_nblk0 * blk_nblk1 * blk_batch_count ; 3891const size_t blk_size_bytes = 3892ROUNDUP_POW2 ( blk_elems * sizeof (uint32_t), WEBGPU_STORAGE_BUF_BINDING_MULT ); 3893res += blk_size_bytes + align ; 3894} 3895res = ROUNDUP_POW2 ( res , WEBGPU_STORAGE_BUF_BINDING_MULT ); 3896} 3897} 3898break ; 3899case GGML_OP_MUL_MAT : 3900{ 3901const ggml_tensor * src0 = tensor -> src [ 0 ]; 3902const ggml_tensor * src1 = tensor -> src [ 1 ]; 3903bool use_mmvq = 3904ggml_webgpu_can_use_mmvq ( src0 , src1 , ctx -> webgpu_global_ctx -> capabilities . supports_dot_product , 3905ctx -> webgpu_global_ctx -> vendor ); 3906if ( use_mmvq ) { 3907const size_t q8_src1_size = src1 -> ne [ 3 ] * src1 -> ne [ 2 ] * src1 -> ne [ 1 ] * 3908( 36 /* sizeof(q8_1) */ * ( src1 -> ne [ 0 ] / /* block_size */ 32 )); 3909res = ROUNDUP_POW2 ( res + q8_src1_size + 3910ctx -> webgpu_global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment , 3911WEBGPU_STORAGE_BUF_BINDING_MULT ); 3912} 3913} 3914break ; 3915case GGML_OP_MUL_MAT_ID : 3916{ 3917const ggml_tensor * src0 = tensor -> src [ 0 ]; 3918const ggml_tensor * src1 = tensor -> src [ 1 ]; 3919if ( src0 && src1 ) { 3920const size_t gathered_size = sizeof (uint32_t) * tensor -> src [ 0 ] -> ne [ 2 ] * tensor -> src [ 1 ] -> ne [ 2 ]; 3921const size_t gathered_count_ids_size = sizeof (uint32_t) * tensor -> src [ 0 ] -> ne [ 2 ]; 3922res = ROUNDUP_POW2 ( 3923res + gathered_size * 2 + gathered_count_ids_size + 3924ctx -> webgpu_global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment * 3 , 3925WEBGPU_STORAGE_BUF_BINDING_MULT ); 3926} 3927} 3928break ; 3929default : 3930break ; 3931} 3932return res ; 3933} 3934 3935/* End GGML Backend Buffer Type Interface */ 3936 3937/* GGML Backend Device Interface */ 3938 3939static const char * ggml_backend_webgpu_device_get_name (ggml_backend_dev_t dev ) { 3940ggml_backend_webgpu_device_context * ctx = static_cast < ggml_backend_webgpu_device_context *> ( dev -> context ); 3941return ctx -> device_name . c_str (); 3942} 3943 3944static const char * ggml_backend_webgpu_device_get_description (ggml_backend_dev_t dev ) { 3945ggml_backend_webgpu_device_context * ctx = static_cast < ggml_backend_webgpu_device_context *> ( dev -> context ); 3946return ctx -> device_desc . c_str (); 3947} 3948 3949static void ggml_backend_webgpu_device_get_memory (ggml_backend_dev_t dev , size_t * free , size_t * total ) { 3950ggml_backend_webgpu_device_context * ctx = static_cast < ggml_backend_webgpu_device_context *> ( dev -> context ); 3951// TODO: for now, return maxBufferSize as both free and total memory 3952// Track https://github.com/gpuweb/gpuweb/issues/5505 for updates. 3953uint64_t max_buffer_size = ctx -> webgpu_global_ctx -> capabilities . limits . maxBufferSize ; 3954// If we're on a 32-bit system, clamp to UINTPTR_MAX 3955#if UINTPTR_MAX < UINT64_MAX 3956uint64_t max_ptr_size = static_cast < uint64_t > ( UINTPTR_MAX ); 3957if ( max_buffer_size > max_ptr_size ) { 3958max_buffer_size = max_ptr_size ; 3959} 3960#endif 3961* free = static_cast < size_t > ( max_buffer_size ); 3962* total = static_cast < size_t > ( max_buffer_size ); 3963} 3964 3965static enum ggml_backend_dev_type ggml_backend_webgpu_device_get_type (ggml_backend_dev_t dev ) { 3966GGML_UNUSED ( dev ); 3967return GGML_BACKEND_DEVICE_TYPE_GPU ; 3968} 3969 3970static void ggml_backend_webgpu_device_get_props (ggml_backend_dev_t dev , struct ggml_backend_dev_props * props ) { 3971props -> name = ggml_backend_webgpu_device_get_name ( dev ); 3972props -> description = ggml_backend_webgpu_device_get_description ( dev ); 3973props -> type = ggml_backend_webgpu_device_get_type ( dev ); 3974ggml_backend_webgpu_device_get_memory ( dev , & props -> memory_free , & props -> memory_total ); 3975props -> caps = { 3976/* .async = */ false, 3977/* .host_buffer = */ false, 3978/* .buffer_from_host_ptr = */ false, 3979/* .events = */ false, 3980/* .mmap_support = */ true, 3981}; 3982} 3983 3984static ggml_guid_t ggml_backend_webgpu_guid (void) { 3985static ggml_guid guid = { 0x67 , 0xc7 , 0xa4 , 0xb1 , 0x78 , 0x74 , 0x4f , 0x51 , 39860x9d , 0x65 , 0x44 , 0x6d , 0xe4 , 0x1b , 0x82 , 0x9a }; 3987return & guid ; 3988} 3989 3990static void ggml_webgpu_init_memset_pipeline (webgpu_global_context & ctx ) { 3991// we use the maximum workgroup size for the memset pipeline 3992size_t max_threads = ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup * 3993ctx -> capabilities . limits . maxComputeWorkgroupsPerDimension ; 3994// Size the bytes_per_thread so that the largest buffer size can be handled 3995ctx -> capabilities . memset_bytes_per_thread = 3996CEIL_DIV ( ctx -> capabilities . limits . maxStorageBufferBindingSize , max_threads ); 3997std::vector < wgpu::ConstantEntry > constants ( 2 ); 3998constants [ 0 ]. key = "wg_size" ; 3999constants [ 0 ]. value = ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 4000constants [ 1 ]. key = "bytes_per_thread" ; 4001constants [ 1 ]. value = ctx -> capabilities . memset_bytes_per_thread ; 4002ctx -> memset_pipeline = ggml_webgpu_create_pipeline ( ctx -> device , wgsl_memset , "memset" , constants ); 4003} 4004 4005static void ggml_backend_webgpu_request_adapter (wgpu::Instance & instance , wgpu::Adapter & adapter ) { 4006wgpu::RequestAdapterOptions options = {}; 4007 4008#ifndef __EMSCRIPTEN__ 4009// TODO: track need for these toggles: https://issues.chromium.org/issues/42251215 4010const char * const adapterEnabledToggles [] = { "vulkan_enable_f16_on_nvidia" , "use_vulkan_memory_model" }; 4011wgpu::DawnTogglesDescriptor adapterTogglesDesc ; 4012adapterTogglesDesc . enabledToggles = adapterEnabledToggles ; 4013adapterTogglesDesc . enabledToggleCount = 2 ; 4014options . nextInChain = & adapterTogglesDesc ; 4015#endif 4016 4017instance . WaitAny ( 4018instance . RequestAdapter ( 4019& options , wgpu::CallbackMode:: AllowSpontaneous , 4020[ & adapter ](wgpu::RequestAdapterStatus status , wgpu::Adapter _adapter , wgpu::StringView message ) { 4021if ( status != wgpu::RequestAdapterStatus:: Success ) { 4022GGML_LOG_ERROR ( "ggml_webgpu: Failed to get an adapter: %s\n" , std:: string ( message ). c_str ()); 4023return ; 4024} 4025adapter = std:: move ( _adapter ); 4026}), 4027UINT64_MAX ); 4028} 4029 4030static void create_webgpu_device (ggml_backend_webgpu_reg_context * ctx ) { 4031ggml_backend_webgpu_request_adapter ( ctx -> webgpu_global_ctx -> instance , ctx -> webgpu_global_ctx -> adapter ); 4032GGML_ASSERT ( ctx -> webgpu_global_ctx -> adapter != nullptr ); 4033 4034ctx -> webgpu_global_ctx -> adapter . GetLimits ( & ctx -> webgpu_global_ctx -> capabilities . limits ); 4035 4036wgpu::AdapterInfo info {}; 4037#ifndef __EMSCRIPTEN__ 4038wgpu::AdapterPropertiesSubgroupMatrixConfigs subgroup_matrix_configs {}; 4039if ( ctx -> webgpu_global_ctx -> adapter . HasFeature (wgpu::FeatureName:: ChromiumExperimentalSubgroupMatrix )) { 4040info . nextInChain = & subgroup_matrix_configs ; 4041} 4042#endif 4043ctx -> webgpu_global_ctx -> adapter . GetInfo ( & info ); 4044ctx -> webgpu_global_ctx -> command_submit_batch_size = ggml_backend_webgpu_get_command_submit_batch_size (); 4045ctx -> webgpu_global_ctx -> max_inflight_batches = ggml_backend_webgpu_get_max_inflight_batches (); 4046ctx -> webgpu_global_ctx -> vendor = info . vendor ; 4047ctx -> webgpu_global_ctx -> capabilities . supports_subgroups = 4048ctx -> webgpu_global_ctx -> adapter . HasFeature (wgpu::FeatureName:: Subgroups ); 4049// for dot4I8packed 4050ctx -> webgpu_global_ctx -> capabilities . supports_dot_product = ctx -> webgpu_global_ctx -> instance . HasWGSLLanguageFeature ( 4051wgpu::WGSLLanguageFeatureName:: Packed4x8IntegerDotProduct ); 4052 4053bool valid_subgroup_matrix_config = false; 4054#ifndef __EMSCRIPTEN__ 4055// Accept f16 subgroup matrix configurations (square or non-square). 4056// NVIDIA GPUs typically report square configs (e.g. 16x16x16), 4057// while Intel Xe2 GPUs report non-square configs (e.g. 8x16x16). 4058// The shaders are already parameterized to handle any M/N/K dimensions. 4059if ( ctx -> webgpu_global_ctx -> adapter . HasFeature (wgpu::FeatureName:: ChromiumExperimentalSubgroupMatrix )) { 4060for (size_t i = 0 ; i < subgroup_matrix_configs . configCount ; i ++ ) { 4061const wgpu::SubgroupMatrixConfig config = subgroup_matrix_configs . configs [ i ]; 4062if ( config . componentType == wgpu::SubgroupMatrixComponentType:: F16 && 4063config . resultComponentType == wgpu::SubgroupMatrixComponentType:: F16 ) { 4064ctx -> webgpu_global_ctx -> capabilities . sg_mat_m = config . M ; 4065ctx -> webgpu_global_ctx -> capabilities . sg_mat_n = config . N ; 4066ctx -> webgpu_global_ctx -> capabilities . sg_mat_k = config . K ; 4067valid_subgroup_matrix_config = true; 4068break ; 4069} 4070} 4071} 4072#endif 4073ctx -> webgpu_global_ctx -> capabilities . supports_subgroup_matrix = valid_subgroup_matrix_config ; 4074 4075// Runtime subgroup size can be any supported size in this range. Shaders 4076// that allocate per-lane register arrays must size them for the minimum. 4077ctx -> webgpu_global_ctx -> capabilities . min_subgroup_size = info . subgroupMinSize ; 4078ctx -> webgpu_global_ctx -> capabilities . max_subgroup_size = info . subgroupMaxSize ; 4079// Initialize device 4080std::vector < wgpu::FeatureName > required_features = { wgpu::FeatureName:: ShaderF16 }; 4081 4082#ifndef __EMSCRIPTEN__ 4083required_features . push_back (wgpu::FeatureName:: ImplicitDeviceSynchronization ); 4084if ( ctx -> webgpu_global_ctx -> capabilities . supports_subgroup_matrix ) { 4085required_features . push_back (wgpu::FeatureName:: ChromiumExperimentalSubgroupMatrix ); 4086} 4087#endif 4088 4089if ( ctx -> webgpu_global_ctx -> capabilities . supports_subgroups ) { 4090required_features . push_back (wgpu::FeatureName:: Subgroups ); 4091} 4092 4093#ifdef GGML_WEBGPU_GPU_PROFILE 4094required_features . push_back (wgpu::FeatureName:: TimestampQuery ); 4095#endif 4096 4097wgpu::DeviceDescriptor dev_desc ; 4098dev_desc . requiredLimits = & ctx -> webgpu_global_ctx -> capabilities . limits ; 4099dev_desc . requiredFeatures = required_features . data (); 4100dev_desc . requiredFeatureCount = required_features . size (); 4101dev_desc . SetDeviceLostCallback ( 4102wgpu::CallbackMode:: AllowSpontaneous , 4103[]( const wgpu::Device & device , wgpu::DeviceLostReason reason , wgpu::StringView message ) { 4104if ( reason == wgpu::DeviceLostReason:: Destroyed ) { 4105return ; 4106} 4107GGML_UNUSED ( device ); 4108GGML_LOG_ERROR ( "ggml_webgpu: Device lost! Reason: %d, Message: %s\n" , static_cast < int > ( reason ), 4109std:: string ( message ). c_str ()); 4110}); 4111dev_desc . SetUncapturedErrorCallback ( 4112[]( const wgpu::Device & device , wgpu::ErrorType reason , wgpu::StringView message ) { 4113GGML_UNUSED ( device ); 4114GGML_ABORT ( "ggml_webgpu: Device error! Reason: %d, Message: %s\n" , static_cast < int > ( reason ), 4115std:: string ( message ). c_str ()); 4116}); 4117 4118#ifndef __EMSCRIPTEN__ 4119// Enable Dawn-specific toggles to increase native performance 4120// TODO: Maybe WebGPU needs a "fast" mode where you can request compilers skip adding checks like these, 4121// only for native performance? 4122const char * const deviceEnabledToggles [] = { "disable_robustness" , "disable_workgroup_init" , 4123"disable_polyfills_on_integer_div_and_mod" }; 4124const char * const deviceDisabledToggles [] = { "timestamp_quantization" }; 4125wgpu::DawnTogglesDescriptor deviceTogglesDesc ; 4126deviceTogglesDesc . enabledToggles = deviceEnabledToggles ; 4127deviceTogglesDesc . enabledToggleCount = 3 ; 4128deviceTogglesDesc . disabledToggles = deviceDisabledToggles ; 4129deviceTogglesDesc . disabledToggleCount = 1 ; 4130 4131dev_desc . nextInChain = & deviceTogglesDesc ; 4132#endif 4133 4134ctx -> webgpu_global_ctx -> instance . WaitAny ( 4135ctx -> webgpu_global_ctx -> adapter . RequestDevice ( 4136& dev_desc , wgpu::CallbackMode:: AllowSpontaneous , 4137[ ctx ](wgpu::RequestDeviceStatus status , wgpu::Device device , wgpu::StringView message ) { 4138if ( status != wgpu::RequestDeviceStatus:: Success ) { 4139GGML_LOG_ERROR ( "ggml_webgpu: Failed to get a device: %s\n" , std:: string ( message ). c_str ()); 4140return ; 4141} 4142ctx -> webgpu_global_ctx -> device = std:: move ( device ); 4143}), 4144UINT64_MAX ); 4145GGML_ASSERT ( ctx -> webgpu_global_ctx -> device != nullptr ); 4146 4147ggml_webgpu_init_memset_pipeline ( ctx -> webgpu_global_ctx ); 4148ggml_webgpu_create_buffer ( ctx -> webgpu_global_ctx -> device , ctx -> webgpu_global_ctx -> memset_params_buf , 4149WEBGPU_PARAMS_BUF_SIZE_BYTES , wgpu::BufferUsage:: CopyDst | wgpu::BufferUsage:: Uniform , 4150"memset_params_buf" ); 4151ctx -> webgpu_global_ctx -> queue = ctx -> webgpu_global_ctx -> device . GetQueue (); 4152 4153GGML_LOG_INFO ( 4154"ggml_webgpu: adapter_info: vendor_id: %u | vendor: %s | architecture: %s | device_id: %u | name: %s | " 4155"device_desc: %s\n" , 4156info . vendorID , std:: string ( info . vendor ). c_str (), std:: string ( info . architecture ). c_str (), info . deviceID , 4157std:: string ( info . device ). c_str (), std:: string ( info . description ). c_str ()); 4158} 4159 4160static webgpu_context initialize_webgpu_context (ggml_backend_dev_t dev ) { 4161ggml_backend_webgpu_device_context * dev_ctx = (ggml_backend_webgpu_device_context * ) dev -> context ; 4162webgpu_context webgpu_ctx = std:: make_shared < webgpu_context_struct > (); 4163webgpu_ctx -> global_ctx = dev_ctx -> webgpu_global_ctx ; 4164webgpu_ctx -> shader_lib = std:: make_unique < ggml_webgpu_shader_lib > ( dev_ctx -> webgpu_global_ctx -> device ); 4165webgpu_ctx -> param_arena . init ( 4166webgpu_ctx -> global_ctx -> device , WEBGPU_PARAMS_BUF_SIZE_BYTES , 4167webgpu_ctx -> global_ctx -> command_submit_batch_size + WEBGPU_NUM_PARAM_SLOT_SAFETY_MARGIN , 4168webgpu_ctx -> global_ctx -> capabilities . limits . minUniformBufferOffsetAlignment ); 4169ggml_webgpu_create_buffer ( webgpu_ctx -> global_ctx -> device , webgpu_ctx -> set_rows_dev_error_buf , 4170WEBGPU_SET_ROWS_ERROR_BUF_SIZE_BYTES , 4171wgpu::BufferUsage:: Storage | wgpu::BufferUsage:: CopySrc , "set_rows_dev_error_buf" ); 4172ggml_webgpu_create_buffer ( webgpu_ctx -> global_ctx -> device , webgpu_ctx -> set_rows_host_error_buf , 4173WEBGPU_SET_ROWS_ERROR_BUF_SIZE_BYTES , 4174wgpu::BufferUsage:: CopyDst | wgpu::BufferUsage:: MapRead , "set_rows_host_error_buf" ); 4175 4176#ifdef GGML_WEBGPU_GPU_PROFILE 4177webgpu_ctx -> batch_compute_passes = false; 4178ggml_webgpu_create_buffer ( 4179webgpu_ctx -> global_ctx -> device , webgpu_ctx -> profile_timestamp_dev_buf , WEBGPU_TIMESTAMP_QUERY_BUF_SIZE_BYTES , 4180wgpu::BufferUsage:: QueryResolve | wgpu::BufferUsage:: CopySrc , "profile_timestamp_dev_buf" ); 4181ggml_webgpu_create_buffer ( webgpu_ctx -> global_ctx -> device , webgpu_ctx -> profile_timestamp_host_buf , 4182WEBGPU_TIMESTAMP_QUERY_BUF_SIZE_BYTES , 4183wgpu::BufferUsage:: CopyDst | wgpu::BufferUsage:: MapRead , "profile_timestamp_host_buf" ); 4184wgpu::QuerySetDescriptor query_set_desc = {}; 4185query_set_desc . type = wgpu::QueryType:: Timestamp ; 4186query_set_desc . count = WEBGPU_MAX_PROFILE_QUERY_COUNT ; 4187webgpu_ctx -> profile_timestamp_query_set = webgpu_ctx -> global_ctx -> device . CreateQuerySet ( & query_set_desc ); 4188#endif 4189 4190#ifdef GGML_WEBGPU_DEBUG 4191// Initialize debug buffers 4192ggml_webgpu_create_buffer ( webgpu_ctx -> global_ctx -> device , webgpu_ctx -> global_ctx -> debug_host_buf , 4193WEBGPU_DEBUG_BUF_ELEMS * sizeof (uint32_t), 4194wgpu::BufferUsage:: CopyDst | wgpu::BufferUsage:: MapRead , "debug_host_buf" ); 4195ggml_webgpu_create_buffer ( webgpu_ctx -> global_ctx -> device , webgpu_ctx -> global_ctx -> debug_dev_buf , 4196WEBGPU_DEBUG_BUF_ELEMS * sizeof (uint32_t), 4197wgpu::BufferUsage:: Storage | wgpu::BufferUsage:: CopySrc , "debug_dev_buf" ); 4198#endif 4199return webgpu_ctx ; 4200} 4201 4202static ggml_backend_t ggml_backend_webgpu_backend_init (ggml_backend_dev_t dev , const char * params ) { 4203GGML_UNUSED ( params ); 4204 4205WEBGPU_LOG_DEBUG ( "ggml_backend_webgpu_backend_init()" ); 4206 4207ggml_backend_webgpu_device_context * dev_ctx = static_cast < ggml_backend_webgpu_device_context *> ( dev -> context ); 4208 4209auto * backend_ctx = new ggml_backend_webgpu_context(); 4210backend_ctx -> name = GGML_WEBGPU_NAME + std:: string ( ": " ) + dev_ctx -> device_name ; 4211backend_ctx -> webgpu_ctx = initialize_webgpu_context ( dev ); 4212 4213// See GGML Backend Interface section 4214auto * backend = new ggml_backend(); 4215* backend = { 4216/* .guid = */ ggml_backend_webgpu_guid (), 4217/* .interface = */ ggml_backend_webgpu_i , 4218/* .device = */ dev , 4219/* .context = */ backend_ctx , 4220}; 4221return backend ; 4222} 4223 4224static ggml_backend_buffer_type_t ggml_backend_webgpu_device_get_buffer_type (ggml_backend_dev_t dev ) { 4225// See GGML Backend Buffer Type Interface section 4226 4227static struct ggml_backend_buffer_type ggml_backend_webgpu_buffer_type = { 4228/* .iface = */ { 4229/* .get_name = */ ggml_backend_webgpu_buffer_type_get_name , 4230/* .alloc_buffer = */ ggml_backend_webgpu_buffer_type_alloc_buffer , 4231/* .get_alignment = */ ggml_backend_webgpu_buffer_type_get_alignment , 4232/* .get_max_size = */ ggml_backend_webgpu_buffer_type_get_max_size , 4233/* .get_alloc_size = */ ggml_backend_webgpu_buffer_type_get_alloc_size , 4234/* .is_host = */ NULL , // defaults to false 4235}, 4236/* .device = */ 4237dev , 4238/* .context = */ NULL 4239}; 4240 4241return & ggml_backend_webgpu_buffer_type ; 4242} 4243 4244static bool ggml_backend_webgpu_device_supports_buft (ggml_backend_dev_t dev , ggml_backend_buffer_type_t buft ) { 4245GGML_UNUSED ( dev ); 4246return buft -> iface . get_name == ggml_backend_webgpu_buffer_type_get_name ; 4247} 4248 4249static bool ggml_webgpu_supported_qtype (ggml_type type ) { 4250switch ( type ) { 4251case GGML_TYPE_Q1_0 : 4252case GGML_TYPE_Q4_0 : 4253case GGML_TYPE_Q4_1 : 4254case GGML_TYPE_Q5_0 : 4255case GGML_TYPE_Q5_1 : 4256case GGML_TYPE_Q8_0 : 4257case GGML_TYPE_Q2_K : 4258case GGML_TYPE_Q3_K : 4259case GGML_TYPE_Q4_K : 4260case GGML_TYPE_Q5_K : 4261case GGML_TYPE_Q6_K : 4262case GGML_TYPE_IQ2_XXS : 4263case GGML_TYPE_IQ2_XS : 4264case GGML_TYPE_IQ2_S : 4265case GGML_TYPE_IQ3_XXS : 4266case GGML_TYPE_IQ3_S : 4267case GGML_TYPE_IQ1_S : 4268case GGML_TYPE_IQ1_M : 4269case GGML_TYPE_IQ4_NL : 4270case GGML_TYPE_IQ4_XS : 4271case GGML_TYPE_MXFP4 : 4272case GGML_TYPE_NVFP4 : 4273return true; 4274default : 4275return false; 4276} 4277} 4278 4279static bool ggml_backend_webgpu_device_supports_op (ggml_backend_dev_t dev , const ggml_tensor * op ) { 4280ggml_backend_webgpu_device_context * ctx = static_cast < ggml_backend_webgpu_device_context *> ( dev -> context ); 4281 4282ggml_tensor * src0 = op -> src [ 0 ]; 4283ggml_tensor * src1 = op -> src [ 1 ]; 4284ggml_tensor * src2 = op -> src [ 2 ]; 4285 4286// on smaller devices (or CI), tensors may be larger than the max storage buffer size 4287if ( ggml_nbytes ( op ) > ctx -> webgpu_global_ctx -> capabilities . limits . maxStorageBufferBindingSize || 4288( src0 != nullptr && 4289ggml_nbytes ( src0 ) > ctx -> webgpu_global_ctx -> capabilities . limits . maxStorageBufferBindingSize ) || 4290( src1 != nullptr && 4291ggml_nbytes ( src1 ) > ctx -> webgpu_global_ctx -> capabilities . limits . maxStorageBufferBindingSize )) { 4292return false; 4293} 4294 4295bool supports_op = false; 4296switch ( op -> op ) { 4297case GGML_OP_NONE : 4298case GGML_OP_VIEW : 4299case GGML_OP_PERMUTE : 4300case GGML_OP_TRANSPOSE : 4301case GGML_OP_RESHAPE : 4302supports_op = true; 4303break ; 4304case GGML_OP_ADD : 4305case GGML_OP_SUB : 4306case GGML_OP_MUL : 4307case GGML_OP_DIV : 4308supports_op = ( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && ( src0 -> type == op -> type ) && 4309( src1 -> type == op -> type ); 4310break ; 4311case GGML_OP_ADD_ID : 4312supports_op = src0 -> type == GGML_TYPE_F32 ; 4313break ; 4314case GGML_OP_CONCAT : 4315supports_op = ( src0 -> type == GGML_TYPE_F32 || src0 -> type == GGML_TYPE_I32 ); 4316break ; 4317case GGML_OP_REPEAT : 4318supports_op = ( src0 -> type == GGML_TYPE_F32 || src0 -> type == GGML_TYPE_F16 || src0 -> type == GGML_TYPE_I32 || 4319src0 -> type == GGML_TYPE_I16 ); 4320break ; 4321case GGML_OP_CPY : 4322case GGML_OP_CONT : 4323supports_op = ( op -> type == GGML_TYPE_F16 || op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_I32 ) && 4324( src0 -> type == GGML_TYPE_F16 || src0 -> type == GGML_TYPE_F32 || src0 -> type == GGML_TYPE_I32 ); 4325break ; 4326case GGML_OP_SET : 4327supports_op = src0 -> type == src1 -> type && src0 -> type == op -> type && 4328( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_I32 ); 4329break ; 4330case GGML_OP_SET_ROWS : 4331supports_op = (( op -> type == GGML_TYPE_F16 || op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_Q8_0 || 4332op -> type == GGML_TYPE_Q4_0 ) && 4333src0 -> type == GGML_TYPE_F32 && ( src1 -> type == GGML_TYPE_I64 || src1 -> type == GGML_TYPE_I32 )); 4334break ; 4335case GGML_OP_GET_ROWS : 4336{ 4337const size_t storage_alignment = 4338ctx -> webgpu_global_ctx -> capabilities . limits . minStorageBufferOffsetAlignment ; 4339const size_t src_address_unit = 4340src0 -> type == GGML_TYPE_F32 && op -> ne [ 0 ] % 4 == 0 ? 4 * sizeof (float) : ggml_type_size ( src0 -> type ); 4341if ( ggml_webgpu_tensor_misalignment ( src0 , storage_alignment ) % src_address_unit != 0 ) { 4342break ; 4343} 4344if ( src0 -> type == GGML_TYPE_F32 || src0 -> type == GGML_TYPE_F16 || 4345ggml_webgpu_supported_qtype ( src0 -> type )) { 4346supports_op = ( op -> type == GGML_TYPE_F32 ); 4347} else if ( src0 -> type == GGML_TYPE_I32 ) { 4348supports_op = op -> type == GGML_TYPE_I32 ; 4349} 4350break ; 4351} 4352case GGML_OP_MUL_MAT : 4353{ 4354switch ( src1 -> type ) { 4355case GGML_TYPE_F16 : 4356supports_op |= ( src0 -> type == GGML_TYPE_F16 ); 4357break ; 4358case GGML_TYPE_F32 : 4359switch ( src0 -> type ) { 4360case GGML_TYPE_F32 : 4361case GGML_TYPE_F16 : 4362case GGML_TYPE_Q1_0 : 4363case GGML_TYPE_Q4_0 : 4364case GGML_TYPE_Q4_1 : 4365case GGML_TYPE_Q5_0 : 4366case GGML_TYPE_Q5_1 : 4367case GGML_TYPE_Q8_0 : 4368case GGML_TYPE_Q2_K : 4369case GGML_TYPE_Q3_K : 4370case GGML_TYPE_Q4_K : 4371case GGML_TYPE_Q5_K : 4372case GGML_TYPE_Q6_K : 4373case GGML_TYPE_IQ2_XXS : 4374case GGML_TYPE_IQ2_XS : 4375case GGML_TYPE_IQ2_S : 4376case GGML_TYPE_IQ3_XXS : 4377case GGML_TYPE_IQ3_S : 4378case GGML_TYPE_IQ1_S : 4379case GGML_TYPE_IQ1_M : 4380case GGML_TYPE_IQ4_NL : 4381case GGML_TYPE_IQ4_XS : 4382case GGML_TYPE_MXFP4 : 4383case GGML_TYPE_NVFP4 : 4384supports_op = true; 4385break ; 4386default : 4387break ; 4388} 4389default : 4390break ; 4391} 4392break ; 4393} 4394case GGML_OP_MUL_MAT_ID : 4395switch ( src1 -> type ) { 4396case GGML_TYPE_F16 : 4397supports_op |= ( src0 -> type == GGML_TYPE_F16 ); 4398break ; 4399case GGML_TYPE_F32 : 4400switch ( src0 -> type ) { 4401case GGML_TYPE_F32 : 4402case GGML_TYPE_F16 : 4403case GGML_TYPE_Q1_0 : 4404case GGML_TYPE_Q4_0 : 4405case GGML_TYPE_Q4_1 : 4406case GGML_TYPE_Q5_0 : 4407case GGML_TYPE_Q5_1 : 4408case GGML_TYPE_Q8_0 : 4409case GGML_TYPE_Q2_K : 4410case GGML_TYPE_Q3_K : 4411case GGML_TYPE_Q4_K : 4412case GGML_TYPE_Q5_K : 4413case GGML_TYPE_Q6_K : 4414case GGML_TYPE_IQ1_S : 4415case GGML_TYPE_IQ1_M : 4416case GGML_TYPE_IQ2_XXS : 4417case GGML_TYPE_IQ2_XS : 4418case GGML_TYPE_IQ2_S : 4419case GGML_TYPE_IQ3_XXS : 4420case GGML_TYPE_IQ3_S : 4421case GGML_TYPE_IQ4_NL : 4422case GGML_TYPE_IQ4_XS : 4423case GGML_TYPE_MXFP4 : 4424case GGML_TYPE_NVFP4 : 4425supports_op = true; 4426break ; 4427default : 4428break ; 4429} 4430break ; 4431default : 4432break ; 4433} 4434break ; 4435case GGML_OP_FLASH_ATTN_EXT : 4436{ 4437// conservative support checks for whether the more resource-intensive shader paths 4438// can be used, to avoid cases where flash_attn is assigned to the CPU later on 4439supports_op = src0 -> type == GGML_TYPE_F32 && 4440( src1 -> type == GGML_TYPE_F32 || src1 -> type == GGML_TYPE_F16 || 4441src1 -> type == GGML_TYPE_Q4_0 || src1 -> type == GGML_TYPE_Q8_0 ) && 4442( src2 -> type == GGML_TYPE_F32 || src2 -> type == GGML_TYPE_F16 || 4443src2 -> type == GGML_TYPE_Q4_0 || src2 -> type == GGML_TYPE_Q8_0 ) && 4444op -> type == GGML_TYPE_F32 ; 4445if (! supports_op ) { 4446break ; 4447} 4448if ( ggml_webgpu_tensor_binding_overlap ( ctx -> webgpu_global_ctx , src1 , src2 ) && 4449src1 -> type != src2 -> type && ! ggml_is_quantized ( src1 -> type ) && ! ggml_is_quantized ( src2 -> type )) { 4450supports_op = false; 4451break ; 4452} 4453const auto & capabilities = ctx -> webgpu_global_ctx -> capabilities ; 4454const size_t storage_offset_alignment = capabilities . limits . minStorageBufferOffsetAlignment ; 4455 4456// subgroup matrix path requirements 4457const bool use_subgroup_matrix = ggml_webgpu_flash_attn_can_use_subgroup_matrix_path ( 4458capabilities . supports_subgroup_matrix , capabilities . sg_mat_k , capabilities . sg_mat_n , src0 , src2 ); 4459 4460// tile path requirements 4461const bool float_vec4_aligned = 4462(( src1 -> type != GGML_TYPE_F16 && src1 -> type != GGML_TYPE_F32 ) || 4463ggml_webgpu_flash_attn_float_vec4_aligned ( src1 , storage_offset_alignment )) && 4464(( src2 -> type != GGML_TYPE_F16 && src2 -> type != GGML_TYPE_F32 ) || 4465ggml_webgpu_flash_attn_float_vec4_aligned ( src2 , storage_offset_alignment )); 4466const uint32_t k_tile_head_align = ( src1 -> type == GGML_TYPE_F32 || src1 -> type == GGML_TYPE_F16 ) ? 4467GGML_WEBGPU_FLASH_ATTN_TILE_KV_VEC_WIDTH : 4468(uint32_t) ggml_blck_size ( src1 -> type ); 4469const uint32_t v_tile_head_align = ( src2 -> type == GGML_TYPE_F32 || src2 -> type == GGML_TYPE_F16 ) ? 4470GGML_WEBGPU_FLASH_ATTN_TILE_KV_VEC_WIDTH : 4471(uint32_t) ggml_blck_size ( src2 -> type ); 4472const bool tile_kv_head_dims_aligned = 4473src0 -> ne [ 0 ] % k_tile_head_align == 0 && src2 -> ne [ 0 ] % v_tile_head_align == 0 ; 4474const bool tile_can_dispatch_all_q_rows = 4475capabilities . limits . maxComputeInvocationsPerWorkgroup >= 4476GGML_WEBGPU_FLASH_ATTN_TILE_Q_TILE * capabilities . max_subgroup_size ; 4477const bool use_tile = ! use_subgroup_matrix && capabilities . supports_subgroups && float_vec4_aligned && 4478tile_kv_head_dims_aligned && tile_can_dispatch_all_q_rows ; 4479 4480if (! use_subgroup_matrix && ! use_tile ) { 4481supports_op = false; 4482break ; 4483} 4484const uint32_t q_tile = 4485use_subgroup_matrix ? capabilities . sg_mat_m : GGML_WEBGPU_FLASH_ATTN_TILE_Q_TILE ; 4486const uint32_t kv_granularity = use_subgroup_matrix ? capabilities . sg_mat_n : 1u ; 4487const bool kv_direct = use_subgroup_matrix ? 4488ggml_webgpu_flash_attn_k_direct ( src0 , src1 , capabilities . sg_mat_k ) || 4489ggml_webgpu_flash_attn_v_direct ( src0 , src2 , capabilities . sg_mat_k ) : 4490false; 4491const uint32_t max_kv_tile = ggml_webgpu_flash_attn_max_kv_tile ( 4492capabilities . limits . maxComputeWorkgroupStorageSize , q_tile , kv_granularity , (uint32_t) src0 -> ne [ 0 ], 4493(uint32_t) src2 -> ne [ 0 ], op -> src [ 3 ] != nullptr , kv_direct ); 4494supports_op = max_kv_tile > 0 ; 4495break ; 4496} 4497case GGML_OP_RMS_NORM : 4498case GGML_OP_NORM : 4499case GGML_OP_L2_NORM : 4500supports_op = ( op -> type == GGML_TYPE_F32 && src0 -> type == GGML_TYPE_F32 ) && ggml_is_contiguous_rows ( src0 ); 4501break ; 4502case GGML_OP_ROPE : 4503supports_op = op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ; 4504break ; 4505case GGML_OP_GLU : 4506switch ( ggml_get_glu_op ( op )) { 4507case GGML_GLU_OP_REGLU : 4508case GGML_GLU_OP_GEGLU : 4509case GGML_GLU_OP_SWIGLU : 4510case GGML_GLU_OP_GEGLU_ERF : 4511case GGML_GLU_OP_GEGLU_QUICK : 4512case GGML_GLU_OP_SWIGLU_CLAMP : 4513supports_op = op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ; 4514break ; 4515case GGML_GLU_OP_SWIGLU_OAI : 4516supports_op = op -> type == GGML_TYPE_F32 ; 4517break ; 4518default : 4519break ; 4520} 4521break ; 4522case GGML_OP_SCALE : 4523supports_op = op -> type == GGML_TYPE_F32 ; 4524break ; 4525case GGML_OP_SOFT_MAX : 4526supports_op = op -> type == GGML_TYPE_F32 ; 4527break ; 4528case GGML_OP_UNARY : 4529{ 4530const ggml_unary_op UNARY_OP = ggml_get_unary_op ( op ); 4531 4532switch ( UNARY_OP ) { 4533case GGML_UNARY_OP_ABS : 4534case GGML_UNARY_OP_SGN : 4535case GGML_UNARY_OP_NEG : 4536case GGML_UNARY_OP_STEP : 4537case GGML_UNARY_OP_TANH : 4538case GGML_UNARY_OP_ELU : 4539case GGML_UNARY_OP_RELU : 4540case GGML_UNARY_OP_SIGMOID : 4541case GGML_UNARY_OP_GELU : 4542case GGML_UNARY_OP_GELU_QUICK : 4543case GGML_UNARY_OP_SILU : 4544case GGML_UNARY_OP_HARDSWISH : 4545case GGML_UNARY_OP_HARDSIGMOID : 4546case GGML_UNARY_OP_EXP : 4547case GGML_UNARY_OP_GELU_ERF : 4548case GGML_UNARY_OP_SOFTPLUS : 4549case GGML_UNARY_OP_EXPM1 : 4550case GGML_UNARY_OP_FLOOR : 4551case GGML_UNARY_OP_CEIL : 4552case GGML_UNARY_OP_ROUND : 4553case GGML_UNARY_OP_TRUNC : 4554case GGML_UNARY_OP_XIELU : 4555supports_op = 4556( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && ( src0 -> type == op -> type ); 4557break ; 4558default : 4559break ; 4560} 4561} 4562break ; 4563case GGML_OP_TRI : 4564supports_op = op -> type == GGML_TYPE_F32 && src0 -> type == GGML_TYPE_F32 ; 4565break ; 4566case GGML_OP_DIAG : 4567supports_op = op -> type == GGML_TYPE_F32 && src0 -> type == GGML_TYPE_F32 ; 4568break ; 4569case GGML_OP_SOLVE_TRI : 4570supports_op = op -> type == GGML_TYPE_F32 && src0 -> type == GGML_TYPE_F32 && src1 -> type == GGML_TYPE_F32 ; 4571break ; 4572case GGML_OP_CONV_2D : 4573supports_op = ( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && 4574( src0 -> type == GGML_TYPE_F32 || src0 -> type == GGML_TYPE_F16 ) && 4575( src1 -> type == GGML_TYPE_F32 || src1 -> type == GGML_TYPE_F16 ); 4576break ; 4577case GGML_OP_CONV_2D_DW : 4578supports_op = ( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && 4579( src0 -> type == GGML_TYPE_F32 || src0 -> type == GGML_TYPE_F16 ) && 4580( src1 -> type == GGML_TYPE_F32 || src1 -> type == GGML_TYPE_F16 ) && 4581( ggml_is_contiguous ( src1 ) || ggml_is_contiguous_channels ( src1 )); 4582break ; 4583case GGML_OP_IM2COL : 4584supports_op = ( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && 4585( src0 -> type == GGML_TYPE_F32 || src0 -> type == GGML_TYPE_F16 ); 4586break ; 4587case GGML_OP_SSM_CONV : 4588supports_op = op -> type == GGML_TYPE_F32 ; 4589break ; 4590case GGML_OP_SSM_SCAN : 4591supports_op = op -> type == GGML_TYPE_F32 && 4592src0 -> ne [ 0 ] <= ctx -> webgpu_global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 4593break ; 4594case GGML_OP_GATED_DELTA_NET : 4595{ 4596const uint32_t s_v = (uint32_t) src2 -> ne [ 0 ]; 4597supports_op = op -> type == GGML_TYPE_F32 && src0 -> type == GGML_TYPE_F32 && src1 -> type == GGML_TYPE_F32 && 4598src2 -> type == GGML_TYPE_F32 && op -> src [ 3 ] -> type == GGML_TYPE_F32 && 4599op -> src [ 4 ] -> type == GGML_TYPE_F32 && op -> src [ 5 ] -> type == GGML_TYPE_F32 && 4600s_v <= ctx -> webgpu_global_ctx -> capabilities . limits . maxComputeInvocationsPerWorkgroup ; 4601} 4602break ; 4603case GGML_OP_CLAMP : 4604supports_op = ( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && ( src0 -> type == op -> type ); 4605break ; 4606case GGML_OP_FILL : 4607supports_op = op -> type == GGML_TYPE_F32 && src0 -> type == GGML_TYPE_F32 ; 4608break ; 4609case GGML_OP_LOG : 4610supports_op = ( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && ( src0 -> type == op -> type ); 4611break ; 4612case GGML_OP_SQR : 4613supports_op = ( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && ( src0 -> type == op -> type ); 4614break ; 4615case GGML_OP_SQRT : 4616supports_op = ( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && ( src0 -> type == op -> type ); 4617break ; 4618case GGML_OP_SIN : 4619supports_op = ( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && ( src0 -> type == op -> type ); 4620break ; 4621case GGML_OP_COS : 4622supports_op = ( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && ( src0 -> type == op -> type ); 4623break ; 4624case GGML_OP_PAD : 4625supports_op = op -> type == GGML_TYPE_F32 && src0 -> type == GGML_TYPE_F32 ; 4626break ; 4627case GGML_OP_ARGMAX : 4628supports_op = op -> type == GGML_TYPE_I32 && src0 -> type == GGML_TYPE_F32 ; 4629break ; 4630case GGML_OP_ARGSORT : 4631supports_op = op -> type == GGML_TYPE_I32 && src0 -> type == GGML_TYPE_F32 && ggml_is_contiguous_rows ( src0 ); 4632break ; 4633case GGML_OP_TOP_K : 4634supports_op = op -> type == GGML_TYPE_I32 && src0 -> type == GGML_TYPE_F32 && ggml_is_contiguous_rows ( src0 ); 4635break ; 4636case GGML_OP_CUMSUM : 4637supports_op = op -> type == GGML_TYPE_F32 && src0 -> type == op -> type ; 4638break ; 4639case GGML_OP_SUM : 4640case GGML_OP_SUM_ROWS : 4641supports_op = op -> type == GGML_TYPE_F32 && src0 -> type == op -> type && ggml_is_contiguous_rows ( src0 ); 4642break ; 4643case GGML_OP_UPSCALE : 4644supports_op = ( op -> type == GGML_TYPE_F32 || op -> type == GGML_TYPE_F16 ) && 4645( src0 -> type == GGML_TYPE_F32 || src0 -> type == GGML_TYPE_F16 ); 4646break ; 4647default : 4648break ; 4649} 4650if ( ggml_nbytes ( op ) > ctx -> webgpu_global_ctx -> capabilities . limits . maxStorageBufferBindingSize || 4651( src0 != nullptr && 4652ggml_nbytes ( src0 ) > ctx -> webgpu_global_ctx -> capabilities . limits . maxStorageBufferBindingSize ) || 4653( src1 != nullptr && 4654ggml_nbytes ( src1 ) > ctx -> webgpu_global_ctx -> capabilities . limits . maxStorageBufferBindingSize ) || 4655( src2 != nullptr && 4656ggml_nbytes ( src2 ) > ctx -> webgpu_global_ctx -> capabilities . limits . maxStorageBufferBindingSize )) { 4657supports_op = false; 4658WEBGPU_LOG_DEBUG ( "ggml_webgpu op not supported due to size: " ); 4659} 4660 4661if (! supports_op ) { 4662WEBGPU_LOG_DEBUG ( "ggml_webgpu op not supported: " 4663<< ggml_op_name ( op -> op ) << " with types dst: " << ggml_type_name ( op -> type ) 4664<< ", src0: " << ( op -> src [ 0 ] ? ggml_type_name ( op -> src [ 0 ] -> type ) : "null" ) 4665<< ", src1: " << ( op -> src [ 1 ] ? ggml_type_name ( op -> src [ 1 ] -> type ) : "null" )); 4666} else { 4667WEBGPU_LOG_DEBUG ( "ggml_webgpu op supported: " 4668<< ggml_op_name ( op -> op ) << " with types dst: " << ggml_type_name ( op -> type ) 4669<< ", src0: " << ( op -> src [ 0 ] ? ggml_type_name ( op -> src [ 0 ] -> type ) : "null" ) 4670<< ", src1: " << ( op -> src [ 1 ] ? ggml_type_name ( op -> src [ 1 ] -> type ) : "null" )); 4671} 4672return supports_op ; 4673} 4674 4675static struct ggml_backend_device_i ggml_backend_webgpu_device_i = { 4676/* .get_name = */ ggml_backend_webgpu_device_get_name , 4677/* .get_description = */ ggml_backend_webgpu_device_get_description , 4678/* .get_memory = */ ggml_backend_webgpu_device_get_memory , 4679/* .get_type = */ ggml_backend_webgpu_device_get_type , 4680/* .get_props = */ ggml_backend_webgpu_device_get_props , 4681/* .init_backend = */ ggml_backend_webgpu_backend_init , 4682/* .get_buffer_type = */ ggml_backend_webgpu_device_get_buffer_type , 4683/* .get_host_buffer_type = */ NULL , 4684/* .buffer_from_host_ptr = */ NULL , 4685/* .supports_op = */ ggml_backend_webgpu_device_supports_op , 4686/* .supports_buft = */ ggml_backend_webgpu_device_supports_buft , 4687/* .offload_op = */ NULL , 4688/* .event_new = */ ggml_backend_webgpu_device_event_new , 4689/* .event_free = */ ggml_backend_webgpu_device_event_free , 4690/* .event_synchronize = */ ggml_backend_webgpu_device_event_synchronize , 4691}; 4692 4693/* End GGML Backend Device Interface */ 4694 4695/* GGML Backend Registration Interface */ 4696 4697static const char * ggml_backend_webgpu_reg_get_name (ggml_backend_reg_t reg ) { 4698ggml_backend_webgpu_reg_context * ctx = static_cast < ggml_backend_webgpu_reg_context *> ( reg -> context ); 4699return ctx -> name ; 4700} 4701 4702static size_t ggml_backend_webgpu_reg_get_device_count (ggml_backend_reg_t reg ) { 4703ggml_backend_webgpu_reg_context * ctx = static_cast < ggml_backend_webgpu_reg_context *> ( reg -> context ); 4704return ctx -> device_count ; 4705} 4706 4707// Only one device is supported for now 4708static ggml_backend_dev_t ggml_backend_webgpu_reg_get_device (ggml_backend_reg_t reg , size_t index ) { 4709GGML_ASSERT ( index == 0 ); 4710WEBGPU_LOG_DEBUG ( "ggml_backend_reg_get_device()" ); 4711 4712WEBGPU_CPU_PROFILE_TOTAL_START ( reg_get_device ); 4713 4714ggml_backend_webgpu_reg_context * reg_ctx = static_cast < ggml_backend_webgpu_reg_context *> ( reg -> context ); 4715 4716create_webgpu_device ( reg_ctx ); 4717 4718static ggml_backend_webgpu_device_context device_ctx ; 4719device_ctx . device_name = GGML_WEBGPU_NAME ; 4720device_ctx . device_desc = GGML_WEBGPU_NAME ; 4721device_ctx . webgpu_global_ctx = reg_ctx -> webgpu_global_ctx ; 4722// See GGML Backend Device Interface section 4723static ggml_backend_device device = { 4724/* .iface = */ ggml_backend_webgpu_device_i , 4725/* .reg = */ reg , 4726/* .context = */ & device_ctx , 4727}; 4728 4729WEBGPU_CPU_PROFILE_TOTAL_END ( reg_get_device , reg_ctx -> webgpu_global_ctx ); 4730return & device ; 4731} 4732 4733static const struct ggml_backend_reg_i ggml_backend_webgpu_reg_i = { 4734/* .get_name = */ ggml_backend_webgpu_reg_get_name , 4735/* .get_device_count = */ ggml_backend_webgpu_reg_get_device_count , 4736/* .get_device = */ ggml_backend_webgpu_reg_get_device , 4737/* .get_proc_address = */ NULL , 4738}; 4739 4740/* End GGML Backend Registration Interface */ 4741 4742ggml_backend_reg_t ggml_backend_webgpu_reg () { 4743WEBGPU_LOG_DEBUG ( "ggml_backend_webgpu_reg()" ); 4744 4745// Intentionally leak the global registry context to avoid crashing inside 4746// Dawn/Vulkan static teardown during process exit. 4747static ggml_backend_webgpu_reg_context * ctx = new ggml_backend_webgpu_reg_context(); 4748 4749static ggml_backend_reg reg = { 4750/* .api_version = */ GGML_BACKEND_API_VERSION , 4751/* .iface = */ ggml_backend_webgpu_reg_i , 4752/* .context = */ ctx , 4753}; 4754 4755ctx -> name = GGML_WEBGPU_NAME ; 4756ctx -> device_count = 0 ; 4757 4758// Keep one Dawn/WebGPU instance alive for the lifetime of the static backend 4759// registry. Recreating it on repeated registry lookups can invalidate 4760// adapter/device references that are still held by the backend/device layer. 4761if ( ctx -> webgpu_global_ctx != nullptr && ctx -> webgpu_global_ctx -> instance != nullptr ) { 4762return & reg ; 4763} 4764 4765wgpu::InstanceDescriptor instance_descriptor {}; 4766std::vector < wgpu::InstanceFeatureName > instance_features = { wgpu::InstanceFeatureName:: TimedWaitAny }; 4767instance_descriptor . requiredFeatures = instance_features . data (); 4768instance_descriptor . requiredFeatureCount = instance_features . size (); 4769 4770#ifndef __EMSCRIPTEN__ 4771const char * const instanceEnabledToggles [] = { "allow_unsafe_apis" }; 4772wgpu::DawnTogglesDescriptor instanceTogglesDesc ; 4773instanceTogglesDesc . enabledToggles = instanceEnabledToggles ; 4774instanceTogglesDesc . enabledToggleCount = 1 ; 4775instance_descriptor . nextInChain = & instanceTogglesDesc ; 4776#endif 4777 4778wgpu::Instance inst = wgpu:: CreateInstance ( & instance_descriptor ); 4779ctx -> webgpu_global_ctx = webgpu_global_context ( new webgpu_global_context_struct()); 4780ctx -> webgpu_global_ctx -> instance = std:: move ( inst ); 4781 4782// Probe for adapter support 4783wgpu::Adapter adapter ; 4784if ( ctx -> webgpu_global_ctx -> instance != nullptr ) { 4785ggml_backend_webgpu_request_adapter ( ctx -> webgpu_global_ctx -> instance , adapter ); 4786} 4787 4788// WebGPU backend requires f16 support and, on native, implicit device synchronization. 4789if ( adapter != nullptr && adapter . HasFeature (wgpu::FeatureName:: ShaderF16 ) 4790#ifndef __EMSCRIPTEN__ 4791&& adapter . HasFeature (wgpu::FeatureName:: ImplicitDeviceSynchronization ) 4792#endif 4793) { 4794ctx -> device_count = 1 ; 4795} 4796 4797return & reg ; 4798} 4799 4800ggml_backend_t ggml_backend_webgpu_init (void) { 4801ggml_backend_reg_t reg = ggml_backend_webgpu_reg (); 4802if ( ggml_backend_reg_dev_count ( reg ) == 0 ) { 4803return nullptr ; 4804} 4805ggml_backend_dev_t dev = ggml_backend_reg_dev_get ( reg , 0 ); 4806return ggml_backend_webgpu_backend_init ( dev , nullptr ); 4807} 4808 4809GGML_BACKEND_DL_IMPL ( ggml_backend_webgpu_reg )