1DOLLY 72APPLICATION gpu-fluid3REQUIRES HOST runtime@04REQUIRES HOST display@05REQUIRES HOST input@06REQUIRES HOST download@07REQUIRES HOST gpu@08REQUIRES HOST http@09REQUIRES HOST snapshot@010REQUIRES HOST upload@01112FROM /v0.1.2/Dollyfile-gpu-sdk 1c89c5226ff3d5c118ac86e0df869ee68add2c419db6f33723e7f3984810679b13REQUIRES TOOL cc14REQUIRES HEADER gpu15REQUIRES LIB dolly-gpu1617FILE /usr/src/dolly/fluid/app.c18    #include "platform.h"19    #define main fluid_upstream_main20    #include "fluid_simulation.c"21    #undef main22    23    static wgpu_context_t context;24    static dolly_display_surface surface;25    static uint64_t input;26    static bool show_panel=true,paused=false,automatic=true,rebuild=false,stopping=false,benchmark_mode=false;27    static unsigned selected_height=720,selected_grid=256,overlay_count;28    static const char *benchmark_style="ink";29    static double fps,gpu_ms=-1,last_mouse=-10;30    static uint64_t overlay_buffer,overlay_uniform,overlay_pipeline,overlay_group;31    static struct {float rect[4];uint32_t character,color,a,b;} overlay[512];32    static const uint32_t text_color=0xffe4e7e4,muted=0xffa6afa7,accent=0xff8cddac;33    static void add_item(float x,float y,float w,float h,unsigned c,uint32_t color) {34      if(overlay_count==ARRAY_SIZE(overlay))return;35      unsigned i=overlay_count++;overlay[i].rect[0]=x;overlay[i].rect[1]=y;overlay[i].rect[2]=w;overlay[i].rect[3]=h;overlay[i].character=c;overlay[i].color=color;36    }37    static void label(float x,float y,const char *s,uint32_t color) {for(;*s;s++,x+=12)if(*s!=' ')add_item(x,y,10,14,(unsigned char)*s,color);}38    static void button(int x,int y,int w,const char *s,bool active) {add_item(x,y,w,28,0,active?0xff415b40:0xff282c28);label(x+8,y+7,s,active?accent:text_color);}39    void fluid_draw_overlay(void) {40      if(benchmark_mode)return;41      overlay_count=0;42      if(show_panel) {43        add_item(12,12,404,258,0,0xec111611);44        label(28,28,"FLUID LAB",accent);button(286,20,114,"H HIDE",false);45        char text[80];if(gpu_ms>=0)snprintf(text,sizeof(text),"GPU %.2F MS   %.0F FPS",gpu_ms,fps);else snprintf(text,sizeof(text),"GPU N/A   %.0F FPS",fps);46        label(28,58,text,muted);47        label(28,94,"IMAGE",muted);button(138,86,102,"720P",selected_height==720);button(250,86,102,"1080P",selected_height==1080);48        label(28,130,"GRID",muted);button(138,122,76,"128",selected_grid==128);button(226,122,76,"256",selected_grid==256);button(314,122,76,"512",selected_grid==512);49        label(28,166,"PRESSURE",muted);button(150,158,36,"-",false);snprintf(text,sizeof(text),"%d",settings.pressure_iterations);label(202,166,text,text_color);button(250,158,36,"+",false);50        button(28,194,76,"INK",settings.render_mode==0);button(114,194,100,"SMOKE",settings.render_mode==2&&!settings.enable_shadows);button(224,194,112,"SHADED",settings.render_mode==2&&settings.enable_shadows);51        button(28,230,100,paused?"RESUME":"PAUSE",paused);button(138,230,88,"RESET",false);button(236,230,164,automatic?"AUTO ON":"AUTO OFF",automatic);52      } else {add_item(12,12,176,30,0,0xb8111611);label(24,20,"H CONTROLS",muted);}53      dolly_gpu_write(&fluid_gpu,overlay_buffer,overlay,overlay_count*sizeof(overlay[0]));54      dolly_gpu_draw(&fluid_gpu,overlay_pipeline,overlay_group,6,overlay_count,context.width,context.height,0);55    }56    static void overlay_init(void) {57      char *source=malloc(16384);FILE*f=fopen("/usr/src/dolly/fluid/overlay.wgsl","r");if(!f){perror("overlay");exit(1);}size_t n=fread(source,1,16383,f);source[n]=0;fclose(f);58      overlay_buffer=gpu_id();overlay_uniform=gpu_id();uint64_t shader=gpu_id();overlay_pipeline=gpu_id();overlay_group=gpu_id();59      dolly_gpu_buffer(&fluid_gpu,overlay_buffer,sizeof(overlay),128|8);dolly_gpu_buffer(&fluid_gpu,overlay_uniform,16,64|8);60      float screen[]={context.width,context.height,0,0};dolly_gpu_write(&fluid_gpu,overlay_uniform,screen,sizeof(screen));dolly_gpu_shader(&fluid_gpu,shader,source);free(source);61      dolly_gpu_pipeline(&fluid_gpu,overlay_pipeline,shader,"ui_vertex","ui_fragment",1);62      uint64_t buffers[]={overlay_buffer,overlay_uniform},sizes[]={sizeof(overlay),16};dolly_gpu_group(&fluid_gpu,overlay_group,overlay_pipeline,2,buffers,sizes);gpu_flush();63    }64    static void initialize(void) {65      context.width=selected_height*16/9;context.height=selected_height;context.render_format=WGPUTextureFormat_BGRA8Unorm;66      gpu_check(dolly_display_set_size(surface.generation,context.width,context.height,&surface));67      int n=dolly_gpu_open(&fluid_gpu,context.width,context.height);gpu_check(n);printf("GPU adapter: %.*s\n",n-16,fluid_gpu.reply+16);68      gpu_reset_client();gpu_dimensions(context.width,context.height);settings.grid_size=selected_grid;69      settings.dye_add_intensity=4;settings.dye_diffusion=.995f;settings.render_intensity_multiplier=2;settings.input_symmetry=3;settings.sim_speed=2;70      mouse_pixel_pos.x=context.width*.5f;mouse_pixel_pos.y=context.height*.74f;mouse_infos.last[0]=.5f;mouse_infos.last[1]=.26f;simulation.last_frame=0;settings.time=0;simulation.initialized=false;71      gpu_check(init(&context));gpu_flush();if(!benchmark_mode)overlay_init();72      printf("Fluid: image=%ux%u grid=%ux%u pressure=%d\n",context.width,context.height,settings.grid_w,settings.grid_h,settings.pressure_iterations);73    }74    static void cleanup(void) {gpu_check(dolly_gpu_wait(&fluid_gpu));shutdown(&context);gpu_flush();gpu_check(dolly_gpu_close(&fluid_gpu));}75    static void reset(void) {76      simulation_reset();77      dynamic_buffer_copy_to(&dynamic_buffers.dye,&dynamic_buffers.rgb_buffer,(WGPUCommandEncoder)1);78      dolly_gpu_submit(&fluid_gpu);gpu_flush();79    }80    static void click(unsigned x,unsigned y) {81      if(!show_panel){if(x<188&&y<42)show_panel=true;return;}82      if(x>=286&&y>=20&&y<48){show_panel=false;return;}83      if(y>=86&&y<114&&x>=138&&x<352){selected_height=x<240?720:1080;rebuild=true;}84      if(y>=122&&y<150&&x>=138&&x<390){selected_grid=x<214?128:x<302?256:512;rebuild=true;}85      if(y>=158&&y<186){if(x>=150&&x<186)settings.pressure_iterations=MAX(0,settings.pressure_iterations-5);if(x>=250&&x<286)settings.pressure_iterations=MIN(50,settings.pressure_iterations+5);}86      if(y>=194&&y<222){if(x>=28&&x<104)settings.render_mode=0;else if(x>=114&&x<214){settings.render_mode=2;settings.enable_shadows=0;}else if(x>=224&&x<336){settings.render_mode=2;settings.enable_shadows=1;}}87      if(y>=230&&y<258){if(x>=28&&x<128)paused=!paused;else if(x>=138&&x<226){reset();}else if(x>=236&&x<400)automatic=!automatic;}88    }89    static void events(void) {90      dolly_input_event e;91      while(dolly_input_next_event(input,&e,0)>0) {92        if(e.type==DOLLY_INPUT_EVENT_KEY&&e.action==DOLLY_KEY_ACTION_PRESS) {93          if(e.key_length==1){switch(e.data[0]){case 'h':case 'H':show_panel=!show_panel;break;case ' ':paused=!paused;break;case 'r':case 'R':reset();break;case 'a':case 'A':automatic=!automatic;break;case 'q':case 'Q':stopping=true;break;}}94          if(e.key_length==6&&!memcmp(e.data,"Escape",6))stopping=true;95        } else if(e.type==DOLLY_INPUT_EVENT_POINTER) {96          unsigned x=e.x,y=e.y;97          bool controls=show_panel?(x>=12&&x<416&&y>=12&&y<270):(x>=12&&x<188&&y>=12&&y<42);98          if(controls){if(e.action==DOLLY_POINTER_ACTION_PRESS)click(x,y);}99          else {mouse_pixel_pos.x=x;mouse_pixel_pos.y=y;last_mouse=seconds();}100        }101      }102    }103    static void autopilot(double t) {104      mouse_pixel_pos.x=context.width*(.5+.28*sin(t*.93));105      mouse_pixel_pos.y=context.height*(.5+.24*cos(t*1.37));106    }107    static void render(void) {108      if(!paused)gpu_check(frame(&context));109      else {float smoke[]={settings.raymarch_steps,settings.smoke_density,settings.enable_shadows,settings.shadow_intensity,settings.smoke_height,settings.light_height,settings.light_intensity,settings.light_falloff};uniform_update(&uniforms.smoke_parameters,&context,smoke,8);for(unsigned i=0;i<UNIFORM_COUNT;i++)uniform_update(global_uniforms[i],&context,NULL,0);WGPUCommandEncoder e=wgpuDeviceCreateCommandEncoder(context.device,NULL);render_program_dispatch(&context,e);WGPUCommandBuffer b=wgpuCommandEncoderFinish(e,NULL);wgpuQueueSubmit(context.queue,1,&b);}110    }111    static void info(double *total,uint64_t *samples) {gpu_flush();gpu_check(dolly_gpu_info(&fluid_gpu));memcpy(&gpu_ms,fluid_gpu.reply+24,8);memcpy(total,fluid_gpu.reply+32,8);memcpy(samples,fluid_gpu.reply+40,8);uint32_t supported;memcpy(&supported,fluid_gpu.reply,4);if(!supported)gpu_ms=-1;}112    static void check_result(void) {113      uint64_t bytes=dynamic_buffers.dye.buffers[0].size;114      WGPUBuffer staging=wgpuDeviceCreateBuffer(context.device,&(WGPUBufferDescriptor){.size=bytes,.usage=WGPUBufferUsage_MapRead|WGPUBufferUsage_CopyDst});115      wgpuCommandEncoderCopyBufferToBuffer((WGPUCommandEncoder)1,dynamic_buffers.dye.buffers[0].buffer,0,staging,0,bytes);116      dolly_gpu_submit(&fluid_gpu);dolly_gpu_map(&fluid_gpu,(uintptr_t)staging,bytes);gpu_flush();117      double sum=0;uint64_t nonzero=0;118      for(uint64_t at=0;at<bytes;at+=DOLLY_GPU_REPLY_BYTES){unsigned n=MIN(bytes-at,DOLLY_GPU_REPLY_BYTES);gpu_check(dolly_gpu_read(&fluid_gpu,(uintptr_t)staging,at,n));for(unsigned i=0;i<n;i+=4){float f;memcpy(&f,fluid_gpu.reply+i,4);if(!isfinite(f)){fputs("Non-finite fluid result\n",stderr);exit(1);}sum+=f;if(fabsf(f)>1e-6)nonzero++;}}119      if(nonzero<100||sum<=0){fputs("Fluid dye did not evolve\n",stderr);exit(1);}120      FILE*f=fopen("/workspace/fluid-proof.txt","w");if(!f){perror("fluid-proof");exit(1);}fprintf(f,"Fluid compute PASS: nonzero=%llu sum=%.9f\n",(unsigned long long)nonzero,sum);fclose(f);printf("Fluid compute PASS: nonzero=%llu sum=%.9f\n",(unsigned long long)nonzero,sum);121    }122    int main(int argc,char **argv) {123      bool check=argc>1&&!strcmp(argv[1],"--check");benchmark_mode=check||(argc>1&&!strcmp(argv[1],"--bench"));124      if(argc>1&&!benchmark_mode){fputs("usage: fluid [--bench [grid [height [steps [ink|smoke|shaded]]]]|--check]\n",stderr);return 1;}125      unsigned steps=check?60:120;126      if(benchmark_mode){if(argc>2)selected_grid=atoi(argv[2]);if(argc>3)selected_height=atoi(argv[3]);if(argc>4)steps=atoi(argv[4]);if(argc>5)benchmark_style=argv[5];fluid_clock=1;}127      if((selected_grid!=128&&selected_grid!=256&&selected_grid!=512)||(selected_height!=720&&selected_height!=1080)||steps<1||steps>1000){fputs("grid: 128/256/512; height: 720/1080; steps: 1..1000\n",stderr);return 1;}128      if(!strcmp(benchmark_style,"smoke")){settings.render_mode=2;settings.enable_shadows=0;}129      else if(!strcmp(benchmark_style,"shaded")){settings.render_mode=2;settings.enable_shadows=1;}130      else if(strcmp(benchmark_style,"ink")){fputs("style: ink/smoke/shaded\n",stderr);return 1;}131      gpu_check(dolly_display_acquire(&surface));gpu_check(dolly_input_acquire(&input));initialize();132      if(benchmark_mode) {133        for(unsigned i=0;i<20;i++){fluid_clock+=16666667;autopilot(i/60.);render();}134        gpu_check(dolly_gpu_wait(&fluid_gpu));double before;uint64_t count_before;info(&before,&count_before);double start=seconds();135        for(unsigned i=0;i<steps;i++){fluid_clock+=16666667;autopilot((i+20)/60.);render();}136        gpu_check(dolly_gpu_wait(&fluid_gpu));double wall=seconds()-start,total;uint64_t samples;info(&total,&samples);137        printf("FLUID_BENCH {\"style\":\"%s\",\"grid\":%u,\"width\":%u,\"height\":%u,\"pressure\":%d,\"steps\":%u,\"wall_ms\":%.6f,\"gpu_ms\":%.6f,\"gpu_samples\":%llu}\n",benchmark_style,selected_grid,context.width,context.height,settings.pressure_iterations,steps,wall*1000/steps,samples>count_before?(total-before)/(samples-count_before):-1,(unsigned long long)(samples-count_before));138        if(check)check_result();139      } else {140        uint32_t sequence=0;unsigned frames=0;double start=seconds(),updated=start;141        while(!stopping){events();if(stopping)break;if(rebuild){cleanup();initialize();rebuild=false;}142          double now=seconds();if(automatic&&now-last_mouse>3)autopilot(now-start);143          render();frames++;if(now-updated>.5){fps=frames/(now-updated);frames=0;updated=now;double t;uint64_t n;info(&t,&n);}144          gpu_check(dolly_display_wait_frame(surface.generation,&sequence,-1));}145      }146      cleanup();gpu_check(dolly_input_release(input));gpu_check(dolly_display_release(surface.generation));return 0;147    }148149FILE /usr/src/dolly/fluid/overlay.wgsl150    struct Item {rect:vec4f, character:u32, color:u32, a:u32, b:u32}151    @group(0) @binding(0) var<storage,read> items:array<Item>;152    @group(0) @binding(1) var<uniform> screen:vec4f;153    struct V { @builtin(position) pos:vec4f, @location(0) uv:vec2f, @location(1) @interpolate(flat) item:u32 }154    @vertex fn ui_vertex(@builtin(vertex_index) v:u32,@builtin(instance_index) i:u32)->V {155     let corners=array<vec2f,6>(vec2f(0,0),vec2f(1,0),vec2f(0,1),vec2f(0,1),vec2f(1,0),vec2f(1,1));156     let q=corners[v];let r=items[i].rect;let p=r.xy+q*r.zw;var o:V;157     o.pos=vec4f(p.x/screen.x*2.-1.,1.-p.y/screen.y*2.,0,1);o.uv=q;o.item=i;return o;158    }159    fn glyph(c:u32,p:vec2u)->f32 {160      var lo=0u;var hi=0u;161      switch(c) {162        case 65u: {lo=1033774u;hi=17969u;}163        case 66u: {lo=509487u;hi=15921u;}164        case 67u: {lo=33854u;hi=30753u;}165        case 68u: {lo=575023u;hi=15921u;}166        case 69u: {lo=492607u;hi=31777u;}167        case 70u: {lo=492607u;hi=1057u;}168        case 71u: {lo=951358u;hi=31281u;}169        case 72u: {lo=1033777u;hi=17969u;}170        case 73u: {lo=135327u;hi=31876u;}171        case 74u: {lo=270620u;hi=6441u;}172        case 75u: {lo=103729u;hi=17701u;}173        case 76u: {lo=33825u;hi=31777u;}174        case 77u: {lo=710513u;hi=17969u;}175        case 78u: {lo=841329u;hi=17969u;}176        case 79u: {lo=575022u;hi=14897u;}177        case 80u: {lo=509487u;hi=1057u;}178        case 81u: {lo=575022u;hi=22837u;}179        case 82u: {lo=509487u;hi=17701u;}180        case 83u: {lo=459838u;hi=15888u;}181        case 84u: {lo=135327u;hi=4228u;}182        case 85u: {lo=575025u;hi=14897u;}183        case 86u: {lo=575025u;hi=4433u;}184        case 87u: {lo=706097u;hi=18293u;}185        case 88u: {lo=141873u;hi=17962u;}186        case 89u: {lo=141873u;hi=4228u;}187        case 90u: {lo=139807u;hi=31778u;}188        case 49u: {lo=135364u;hi=14468u;}189        case 50u: {lo=410158u;hi=31778u;}190        case 51u: {lo=475663u;hi=15888u;}191        case 48u: {lo=714286u;hi=14899u;}192        case 52u: {lo=1025321u;hi=8456u;}193        case 53u: {lo=492607u;hi=15888u;}194        case 54u: {lo=492590u;hi=14897u;}195        case 55u: {lo=139807u;hi=2114u;}196        case 56u: {lo=476718u;hi=14897u;}197        case 57u: {lo=1001006u;hi=14864u;}198        case 46u: {lo=0u;hi=4096u;}199        case 45u: {lo=1015808u;hi=0u;}200        case 43u: {lo=1020032u;hi=132u;}201        case 47u: {lo=139792u;hi=1058u;}202        default: {}203      }204      let i=p.y*5u+p.x;return f32(select((lo>>(i%20u))&1u,(hi>>((i-20u)%20u))&1u,i>=20u));205    }206    207    @fragment fn ui_fragment(v:V)->@location(0) vec4f {208     let item=items[v.item];let c=item.color;209     var alpha=f32((c>>24u)&255u)/255.;210     if(item.character!=0u) {alpha*=glyph(item.character,vec2u(min(v.uv*vec2f(5,7),vec2f(4,6))));}211     let rgb=vec3f(f32(c&255u),f32((c>>8u)&255u),f32((c>>16u)&255u))/255.;return vec4f(rgb*alpha,alpha);212    }213214FILE /usr/src/dolly/fluid/platform.h215    #pragma once216    #define _POSIX_C_SOURCE 200809L217    #include <webgpu/webgpu.h>218    #include <dolly/gpu.h>219    #include <dolly/display.h>220    #include <dolly/input.h>221    #include <cglm/vec2.h>222    #include <cglm/vec4.h>223    #include <assert.h>224    #include <math.h>225    #include <stdbool.h>226    #include <stdio.h>227    #include <stdlib.h>228    #include <string.h>229    #include <time.h>230    231    #define CODE(...) #__VA_ARGS__232    #define STRVIEW(s) ((WGPUStringView){s,sizeof(s)-1})233    #define ASSERT(x) assert(x)234    #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))235    #define MIN(a,b) ((a)<(b)?(a):(b))236    #define MAX(a,b) ((a)>(b)?(a):(b))237    #define UNUSED_VAR(x) (void)(x)238    #define WGPU_RELEASE_RESOURCE(T,x) do {if(x){wgpu##T##Release(x);x=0;}} while(0);239    #define WGPU_VERTATTR_DESC(l,f,o) ((WGPUVertexAttribute){.shaderLocation=l,.format=f,.offset=o})240    #define WGPU_VERTEX_BUFFER_LAYOUT(name,stride,...) \241     WGPUVertexAttribute name##_attrs[]={__VA_ARGS__}; \242     WGPUVertexBufferLayout name##_vertex_buffer_layout={.arrayStride=stride,.stepMode=WGPUVertexStepMode_Vertex,.attributeCount=ARRAY_SIZE(name##_attrs),.attributes=name##_attrs};243    244    typedef struct wgpu_context_t { WGPUDevice device; WGPUAdapter adapter; WGPUQueue queue; WGPUTextureView swapchain_view; WGPUTextureFormat render_format; unsigned width,height; } wgpu_context_t;245    typedef struct { WGPUBuffer buffer; WGPUBufferUsage usage; uint32_t size,count; } wgpu_buffer_t;246    typedef struct { const char *label; WGPUBufferUsage usage; uint32_t size,count; struct {const void *data;uint32_t size;} initial; WGPUBool mapped_at_creation; } wgpu_buffer_desc_t;247    typedef struct {int type; float mouse_x,mouse_y;} input_event_t;248    enum {INPUT_EVENT_TYPE_MOUSE_MOVE=1};249    typedef struct {const char *title; bool no_depth_buffer;int (*init_cb)(wgpu_context_t*);int (*frame_cb)(wgpu_context_t*);void (*shutdown_cb)(wgpu_context_t*);void (*input_event_cb)(wgpu_context_t*,const input_event_t*);} wgpu_desc_t;250    static void wgpu_start(const wgpu_desc_t *d) {(void)d;abort();} /* app.c owns the Dolly entry point. */251    wgpu_buffer_t wgpu_create_buffer(wgpu_context_t*,const wgpu_buffer_desc_t*);252    void wgpu_destroy_buffer(wgpu_buffer_t*);253    WGPUShaderModule wgpu_create_shader_module(WGPUDevice,const char*);254    WGPUBlendState wgpu_create_blend_state(bool);255    extern dolly_gpu fluid_gpu;256    extern uint64_t fluid_clock;257    uint64_t gpu_id(void);258    void fluid_draw_overlay(void);259    void gpu_flush(void);260    void gpu_reset_client(void);261    void gpu_dimensions(unsigned,unsigned);262    void gpu_check(int);263    static double seconds(void) {struct timespec t;clock_gettime(CLOCK_MONOTONIC,&t);return t.tv_sec+t.tv_nsec*1e-9;}264    static void stm_setup(void) {}265    static uint64_t stm_now(void) {return fluid_clock?fluid_clock:(uint64_t)(seconds()*1e9);}266    static double stm_ms(uint64_t t) {return t/1e6;}267    static double stm_sec(uint64_t t) {return t/1e9;}268    static uint64_t stm_diff(uint64_t a,uint64_t b) {return a-b;}269270FILE /usr/src/dolly/fluid/webgpu.c271    #include "platform.h"272    #include <errno.h>273    274    dolly_gpu fluid_gpu;275    uint64_t fluid_clock;276    static uint64_t next_id, render_pipeline, compute_pipeline, render_group, compute_group, vertex_buffer, vertex_offset, vertex_size;277    static unsigned width,height;278    uint64_t gpu_id(void) {return ++next_id;}279    static bool clear;280    static float clear_color[4];281    static struct {uint64_t id,size;} buffers[128];282    static uint64_t id(const void *p) {return (uintptr_t)p;}283    static void put32(void *p,size_t o,uint32_t n) {memcpy((char*)p+o,&n,4);}284    static void put64(void *p,size_t o,uint64_t n) {memcpy((char*)p+o,&n,8);}285    static void unsupported(bool allowed) {if(!allowed){fputs("Unsupported fluid WebGPU descriptor\n",stderr);exit(1);}}286    void gpu_check(int n) {if(n<0){perror("fluid GPU");exit(1);}}287    void gpu_flush(void) {if(fluid_gpu.count)gpu_check(dolly_gpu_batch(&fluid_gpu));dolly_gpu_begin(&fluid_gpu);}288    void gpu_reset_client(void) {next_id=0;memset(buffers,0,sizeof(buffers));dolly_gpu_begin(&fluid_gpu);}289    void gpu_dimensions(unsigned w,unsigned h) {width=w;height=h;}290    static void reserve(size_t n) {if(fluid_gpu.length+n>sizeof(fluid_gpu.packet)||fluid_gpu.count==256)gpu_flush();}291    static void *record(unsigned op,size_t bytes) {reserve(bytes+7);return dolly_gpu_record(&fluid_gpu,op,bytes);}292    static uint64_t buffer_size(WGPUBuffer b) {for(unsigned i=0;i<128;i++)if(buffers[i].id==id(b))return buffers[i].size;unsupported(false);return 0;}293    WGPUBuffer wgpuDeviceCreateBuffer(WGPUDevice device,const WGPUBufferDescriptor *d) {294      (void)device;unsupported(!d->nextInChain&&!d->mappedAtCreation&&d->usage<=1023);295      uint64_t n=++next_id;unsigned i;for(i=0;i<128&&buffers[i].id;i++){}unsupported(i<128);296      buffers[i].id=n;buffers[i].size=d->size;297      reserve(32);dolly_gpu_buffer(&fluid_gpu,n,d->size,d->usage);return (WGPUBuffer)(uintptr_t)n;298    }299    void wgpuQueueWriteBuffer(WGPUQueue q,WGPUBuffer b,uint64_t offset,const void *data,size_t size) {300      (void)q;unsupported(offset%4==0&&size%4==0);301      while(size){size_t n=MIN(size,128*1024);void*p=record(DOLLY_GPU_WRITE_BUFFER,32+n);302        put64(p,8,id(b));put64(p,16,offset);put32(p,24,32);put32(p,28,n);memcpy((char*)p+32,data,n);303        offset+=n;data=(const char*)data+n;size-=n;}304    }305    wgpu_buffer_t wgpu_create_buffer(wgpu_context_t *ctx,const wgpu_buffer_desc_t *d) {306      WGPUBuffer b=wgpuDeviceCreateBuffer(ctx->device,&(WGPUBufferDescriptor){.usage=d->usage,.size=d->size,.mappedAtCreation=d->mapped_at_creation});307      if(d->initial.data)wgpuQueueWriteBuffer(ctx->queue,b,0,d->initial.data,d->initial.size?d->initial.size:d->size);308      return (wgpu_buffer_t){.buffer=b,.usage=d->usage,.size=d->size,.count=d->count};309    }310    static void release(uint64_t n) {if(n){void*p=record(DOLLY_GPU_RELEASE,16);put64(p,8,n);}}311    void wgpuBufferRelease(WGPUBuffer b) {release(id(b));for(unsigned i=0;i<128;i++)if(buffers[i].id==id(b))buffers[i].id=0;}312    void wgpu_destroy_buffer(wgpu_buffer_t *b) {if(b->buffer)wgpuBufferRelease(b->buffer);memset(b,0,sizeof(*b));}313    void wgpuShaderModuleRelease(WGPUShaderModule s) {release(id(s));}314    void wgpuComputePipelineRelease(WGPUComputePipeline p) {release(id(p));}315    void wgpuRenderPipelineRelease(WGPURenderPipeline p) {release(id(p));}316    void wgpuBindGroupRelease(WGPUBindGroup g) {release(id(g));}317    WGPUShaderModule wgpu_create_shader_module(WGPUDevice device,const char *source) {318      (void)device;uint64_t n=++next_id;reserve(strlen(source)+32);dolly_gpu_shader(&fluid_gpu,n,source);return (WGPUShaderModule)(uintptr_t)n;319    }320    WGPUComputePipeline wgpuDeviceCreateComputePipeline(WGPUDevice device,const WGPUComputePipelineDescriptor *d) {321      (void)device;unsupported(!d->nextInChain&&!d->layout&&!d->compute.constantCount&&!d->compute.nextInChain);322      size_t length=d->compute.entryPoint.length;unsupported(length>0&&length<=64);char entry[65];memcpy(entry,d->compute.entryPoint.data,length);entry[length]=0;323      uint64_t n=++next_id;reserve(104);dolly_gpu_compute_pipeline(&fluid_gpu,n,id(d->compute.module),entry);return (WGPUComputePipeline)(uintptr_t)n;324    }325    WGPUBlendState wgpu_create_blend_state(bool enabled) {326      unsupported(!enabled);WGPUBlendComponent c={.operation=WGPUBlendOperation_Add,.srcFactor=WGPUBlendFactor_One,.dstFactor=WGPUBlendFactor_Zero};return (WGPUBlendState){.color=c,.alpha=c};327    }328    WGPURenderPipeline wgpuDeviceCreateRenderPipeline(WGPUDevice device,const WGPURenderPipelineDescriptor *d) {329      (void)device;unsupported(!d->nextInChain&&!d->layout&&!d->depthStencil&&d->vertex.bufferCount==1&&d->fragment&&d->fragment->module==d->vertex.module&&d->fragment->targetCount==1&&d->primitive.topology==WGPUPrimitiveTopology_TriangleList&&d->primitive.cullMode==WGPUCullMode_None&&d->multisample.count==1);330      const WGPUVertexBufferLayout *layout=d->vertex.buffers;331      unsupported(layout->stepMode==WGPUVertexStepMode_Vertex&&layout->attributeCount<=8&&layout->arrayStride<=2048);332      size_t a=d->vertex.entryPoint.length,b=d->fragment->entryPoint.length,count=layout->attributeCount;unsupported(a<=64&&b<=64);333      uint64_t n=++next_id;void*p=record(DOLLY_GPU_VERTEX_PIPELINE,48+count*16+a+b);334      put64(p,8,n);put64(p,16,id(d->vertex.module));put32(p,32,a);put32(p,36,b);put32(p,40,layout->arrayStride);put32(p,44,count);335      for(unsigned i=0;i<count;i++) {const WGPUVertexAttribute *attr=layout->attributes+i;unsigned components=attr->format==WGPUVertexFormat_Float32x2?2:attr->format==WGPUVertexFormat_Float32x3?3:attr->format==WGPUVertexFormat_Float32x4?4:0;unsupported(components&&attr->offset<=UINT32_MAX);336        put32(p,48+i*16,attr->shaderLocation);put32(p,52+i*16,components);put32(p,56+i*16,attr->offset);}337      memcpy((char*)p+48+count*16,d->vertex.entryPoint.data,a);memcpy((char*)p+48+count*16+a,d->fragment->entryPoint.data,b);338      return (WGPURenderPipeline)(uintptr_t)n;339    }340    WGPUBindGroupLayout wgpuComputePipelineGetBindGroupLayout(WGPUComputePipeline p,uint32_t index) {unsupported(index==0);return (WGPUBindGroupLayout)p;}341    WGPUBindGroupLayout wgpuRenderPipelineGetBindGroupLayout(WGPURenderPipeline p,uint32_t index) {unsupported(index==0);return (WGPUBindGroupLayout)p;}342    void wgpuBindGroupLayoutRelease(WGPUBindGroupLayout layout) {(void)layout;} /* Borrowed auto layout. */343    WGPUBindGroup wgpuDeviceCreateBindGroup(WGPUDevice device,const WGPUBindGroupDescriptor *d) {344      (void)device;unsupported(!d->nextInChain&&d->entryCount<=DOLLY_GPU_MAX_BINDINGS);345      uint64_t n=++next_id;void*p=record(DOLLY_GPU_BIND_GROUP,32+d->entryCount*24);put64(p,8,n);put64(p,16,id(d->layout));put32(p,24,d->entryCount);346      for(unsigned i=0;i<d->entryCount;i++){const WGPUBindGroupEntry*e=d->entries+i;unsupported(e->binding==i&&e->buffer&&!e->sampler&&!e->textureView&&!e->nextInChain);put64(p,32+i*24,id(e->buffer));put64(p,40+i*24,e->offset);put64(p,48+i*24,e->size==WGPU_WHOLE_SIZE?buffer_size(e->buffer)-e->offset:e->size);}347      return (WGPUBindGroup)(uintptr_t)n;348    }349    WGPUStatus wgpuAdapterGetLimits(WGPUAdapter a,WGPULimits *limits) {(void)a;gpu_flush();gpu_check(dolly_gpu_info(&fluid_gpu));memset(limits,0,sizeof(*limits));memcpy(&limits->maxStorageBufferBindingSize,fluid_gpu.reply+8,8);limits->maxTextureDimension2D=4096;return WGPUStatus_Success;}350    WGPUStatus wgpuDeviceGetLimits(WGPUDevice d,WGPULimits *l) {return wgpuAdapterGetLimits((WGPUAdapter)d,l);}351    WGPUCommandEncoder wgpuDeviceCreateCommandEncoder(WGPUDevice d,const WGPUCommandEncoderDescriptor *desc) {(void)d;unsupported(!desc);return (WGPUCommandEncoder)1;}352    WGPUComputePassEncoder wgpuCommandEncoderBeginComputePass(WGPUCommandEncoder e,const WGPUComputePassDescriptor *d) {unsupported(e==(WGPUCommandEncoder)1&&!d);return (WGPUComputePassEncoder)1;}353    void wgpuComputePassEncoderSetPipeline(WGPUComputePassEncoder p,WGPUComputePipeline pipeline) {(void)p;compute_pipeline=id(pipeline);}354    void wgpuComputePassEncoderSetBindGroup(WGPUComputePassEncoder p,uint32_t index,WGPUBindGroup g,size_t count,const uint32_t *offsets) {(void)p;(void)offsets;unsupported(index==0&&count==0);compute_group=id(g);}355    void wgpuComputePassEncoderDispatchWorkgroups(WGPUComputePassEncoder e,uint32_t x,uint32_t y,uint32_t z) {(void)e;void*p=record(DOLLY_GPU_COMPUTE,40);put64(p,8,compute_pipeline);put64(p,16,compute_group);put32(p,24,x);put32(p,28,y);put32(p,32,z);}356    void wgpuComputePassEncoderEnd(WGPUComputePassEncoder p) {(void)p;}357    void wgpuComputePassEncoderRelease(WGPUComputePassEncoder p) {(void)p;}358    void wgpuCommandEncoderCopyBufferToBuffer(WGPUCommandEncoder e,WGPUBuffer a,uint64_t ao,WGPUBuffer b,uint64_t bo,uint64_t size) {(void)e;void*p=record(DOLLY_GPU_COPY_BUFFER,48);put64(p,8,id(a));put64(p,16,id(b));put64(p,24,ao);put64(p,32,bo);put64(p,40,size);}359    WGPURenderPassEncoder wgpuCommandEncoderBeginRenderPass(WGPUCommandEncoder e,const WGPURenderPassDescriptor *d) {(void)e;unsupported(d->colorAttachmentCount==1&&!d->depthStencilAttachment);clear=d->colorAttachments[0].loadOp==WGPULoadOp_Clear;const WGPUColor*c=&d->colorAttachments[0].clearValue;clear_color[0]=c->r;clear_color[1]=c->g;clear_color[2]=c->b;clear_color[3]=c->a;vertex_buffer=0;return (WGPURenderPassEncoder)1;}360    void wgpuRenderPassEncoderSetPipeline(WGPURenderPassEncoder e,WGPURenderPipeline p) {(void)e;render_pipeline=id(p);}361    void wgpuRenderPassEncoderSetBindGroup(WGPURenderPassEncoder e,uint32_t index,WGPUBindGroup g,size_t count,const uint32_t *o) {(void)e;(void)o;unsupported(index==0&&count==0);render_group=id(g);}362    void wgpuRenderPassEncoderSetVertexBuffer(WGPURenderPassEncoder e,uint32_t slot,WGPUBuffer b,uint64_t offset,uint64_t size) {(void)e;unsupported(slot==0);vertex_buffer=id(b);vertex_offset=offset;vertex_size=size==WGPU_WHOLE_SIZE?buffer_size(b)-offset:size;}363    void wgpuRenderPassEncoderDraw(WGPURenderPassEncoder e,uint32_t vertices,uint32_t instances,uint32_t first,uint32_t first_instance) {364      (void)e;unsupported(first==0&&first_instance==0&&vertex_buffer);void*p=record(DOLLY_GPU_RENDER_VERTEX,88);put64(p,8,render_pipeline);put64(p,16,render_group);put32(p,24,vertices);put32(p,28,instances);put32(p,32,width);put32(p,36,height);memcpy((char*)p+40,clear_color,16);put32(p,56,clear);put64(p,64,vertex_buffer);put64(p,72,vertex_offset);put64(p,80,vertex_size);clear=false;365    }366    void wgpuRenderPassEncoderEnd(WGPURenderPassEncoder p) {(void)p;}367    void wgpuRenderPassEncoderRelease(WGPURenderPassEncoder p) {(void)p;}368    WGPUCommandBuffer wgpuCommandEncoderFinish(WGPUCommandEncoder e,const WGPUCommandBufferDescriptor *d) {unsupported(e==(WGPUCommandEncoder)1&&!d);return (WGPUCommandBuffer)1;}369    void wgpuQueueSubmit(WGPUQueue q,size_t count,const WGPUCommandBuffer *b) {(void)q;unsupported(count==1&&b[0]==(WGPUCommandBuffer)1);fluid_draw_overlay();reserve(8);dolly_gpu_submit(&fluid_gpu);gpu_flush();}370    void wgpuCommandBufferRelease(WGPUCommandBuffer b) {(void)b;}371    void wgpuCommandEncoderRelease(WGPUCommandEncoder e) {(void)e;}372373FILE /usr/src/dolly/fluid/without-imgui.h374    #pragma once375    /* The upstream ImGui panel is replaced by the Dolly controls in app.c. */376    #define imgui_overlay_init(...) ((void)0)377    #define imgui_overlay_shutdown(...) ((void)0)378    #define imgui_overlay_handle_input(...) ((void)0)379    #define imgui_overlay_new_frame(...) ((void)0)380    #define imgui_overlay_render(...) ((void)0)381    #define igSetNextWindowPos(...) ((void)0)382    #define igBegin(...) ((void)0)383    #define igEnd(...) ((void)0)384    #define igCollapsingHeader_TreeNodeFlags(...) false385    #define igSliderInt(...) false386    #define igSliderFloat(...) false387    #define igCombo_Str_arr(...) false388    #define igButton(...) false389    #define igCheckbox(...) false390391FILE /usr/src/dolly/fluid/cglm/cglm.h392    #include "../platform.h"393394FILE /usr/src/dolly/fluid/webgpu/wgpu_common.h395    #include "../platform.h"396397FILE /usr/src/dolly/fluid/webgpu/imgui_overlay.h398    #include "../without-imgui.h"399400FILE /usr/src/dolly/fluid/cimgui.h401    #include "without-imgui.h"402403FILE /usr/src/dolly/fluid/sokol_time.h404    #include "platform.h"405406SOURCE https://raw.githubusercontent.com/samdauwe/webgpu-native-examples/9a7c30753d6f44630564a8316eb9c44211ff0ecc/src/examples/fluid_simulation.c 74d7a9fa5b0c23988016589cf57554028c38a1cd49916d03d9aeead85a045c40 /usr/src/dolly/fluid/fluid_simulation.c407SOURCE https://raw.githubusercontent.com/samdauwe/webgpu-native-examples/9a7c30753d6f44630564a8316eb9c44211ff0ecc/LICENSE b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1 /usr/share/licenses/webgpu-native-examples/LICENSE408SOURCE https://raw.githubusercontent.com/webgpu-native/webgpu-headers/b5ff182caa90e53293f47939716281342c0812ba/webgpu.h 33f7b7f9a8e3cba0c397ca27894b1dd71ec9a7970016e163b2c3c0bba5223ef4 /usr/src/dolly/fluid/webgpu/webgpu.h409SOURCE https://raw.githubusercontent.com/webgpu-native/webgpu-headers/b5ff182caa90e53293f47939716281342c0812ba/LICENSE 17420d366df90c474bd70ad474694956cdb7fc64be70387a49a45458c4152d22 /usr/share/licenses/webgpu-headers/LICENSE410SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/LICENSE 431610a1607c117bc161f37d99a321da977deb27e4f1df367365a9ca422d9c1e /usr/share/licenses/cglm/LICENSE411SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/common.h 6dca9a56594272b7431bcb005b222796931e3ecf49a85526d8dfd7597c66f80e /usr/src/dolly/fluid/cglm/common.h412SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/simd/arm.h 54f7b7be2b95dd885e197cd0a8d3932c20afc415b3211b5439db9067db5ee468 /usr/src/dolly/fluid/cglm/simd/arm.h413SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/simd/intrin.h 6b81197052a63251719575ca7bb17997ec9a7d158f89b20cfede182cc93bb4dd /usr/src/dolly/fluid/cglm/simd/intrin.h414SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/simd/wasm.h bec0bedc8aa072a853cf4524f2e9f83800cb716316144f63b471ef7bed29da3d /usr/src/dolly/fluid/cglm/simd/wasm.h415SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/simd/x86.h a8eec2f4b00f625760a9abcd66c5b3aead61145d184d439eda5fde579e9eb15a /usr/src/dolly/fluid/cglm/simd/x86.h416SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/types.h 793d8a8cb0a425da50a2bfcc20dbd55e212ee86c3606991f84d5c9512a95b97f /usr/src/dolly/fluid/cglm/types.h417SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/util.h c325e6f2137bbe82a843844bee102cd41ae81ca902ce9b9107d5d0cf25db4c3f /usr/src/dolly/fluid/cglm/util.h418SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/vec2-ext.h 3224e072a872c8a1e5b9b17b2e7bb0deceb662d94e15d23c4382ed064bc2815f /usr/src/dolly/fluid/cglm/vec2-ext.h419SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/vec2.h 01116926ac02f1da2feb711ebff2ee44f1ce8bd76f7df89b5ee3bc5f57a0a51a /usr/src/dolly/fluid/cglm/vec2.h420SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/vec3-ext.h 6f7667d62e219c2eaf65725d0ed62d06798df84cfb2cee1340b07c2cc10deddb /usr/src/dolly/fluid/cglm/vec3-ext.h421SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/vec4-ext.h 09374f28cf188e9ef70874e5fdaad45fbf1107cd877df7ef640912ef2b58dd64 /usr/src/dolly/fluid/cglm/vec4-ext.h422SOURCE https://raw.githubusercontent.com/recp/cglm/144d1e7c29b3b0c6dede7917a0476cc95248559c/include/cglm/vec4.h 6ce2c9a52a7ed727a147693d9049a4f9febb549bbe9389e1a0d815cdca7a6584 /usr/src/dolly/fluid/cglm/vec4.h423SLOP cc -std=gnu11 -O2 -I/usr/src/dolly/fluid /usr/src/dolly/fluid/app.c /usr/src/dolly/fluid/webgpu.c -ldolly-gpu -lm -o /usr/bin/fluid424EXPORTS TOOL fluid425EXPORTS FOLDER fluid-sources /usr/src/dolly/fluid426EXPORTS FOLDER fluid-upstream-license /usr/share/licenses/webgpu-native-examples427EXPORTS FOLDER fluid-cglm-license /usr/share/licenses/cglm428EXPORTS FOLDER fluid-webgpu-headers-license /usr/share/licenses/webgpu-headers429430FILE /etc/dolly/gpu-fluid.slop431    /bin/foreground /usr/bin/fluid432    /bin/foreground -i /bin/slop433434ENTRY /bin/foreground -i /bin/slop /etc/dolly/gpu-fluid.slop435