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.c1819202122 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.h215216217218219220221222223224225226227228229230 231232233234235236237238239240241 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.c271272273 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.h374375 /* The upstream ImGui panel is replaced by the Dolly controls in app.c. */376377378379380381382383384385386387388389390391FILE /usr/src/dolly/fluid/cglm/cglm.h392393394FILE /usr/src/dolly/fluid/webgpu/wgpu_common.h395396397FILE /usr/src/dolly/fluid/webgpu/imgui_overlay.h398399400FILE /usr/src/dolly/fluid/cimgui.h401402403FILE /usr/src/dolly/fluid/sokol_time.h404405406SOURCE 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 /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