PageSourceSearch

https://wgmath.rs/assets/js/da57175b.6d23d0f9.js

js wgmath.rs collected 2026-10-06 19:20:16 UTC 15,217 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkrapier=self.webpackChunkrapier||[]).push([[711],{2791:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>u,default:()=>p,frontMatter:()=>l,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"user_guides/wgcore/hot_reloading","title":"Hot-reloading","description":"The code described in this section can be run from the","source":"@site/docs/user_guides/wgcore/hot_reloading.mdx","sourceDirName":"user_guides/wgcore","slug":"/user_guides/wgcore/hot_reloading","permalink":"/docs/user_guides/wgcore/hot_reloading","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"id":"hot_reloading","title":"Hot-reloading","sidebar_label":"Hot-reloading"},"sidebar":"docs","previous":{"title":"Buffers readback","permalink":"/docs/user_guides/wgcore/buffers_readback"},"next":{"title":"Timestamp queries","permalink":"/docs/user_guides/wgcore/timestamp_queries"}}');var a=t(4848),o=t(8453),i=t(1470),s=t(9365);const l={id:"hot_reloading",title:"Hot-reloading",sidebar_label:"Hot-reloading"},u=void 0,d={},c=[{value:"How does this work?",id:"how-does-this-work",level:2},{value:"Complete example",id:"complete-example",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,o.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsxs)(n.p,{children:["The code described in this section can be run from the\n",(0,a.jsx)(n.a,{href:"https://github.com/dimforge/wgmath/blob/main/crates/wgcore/examples/hot_reloading.rs",children:(0,a.jsx)(n.strong,{children:"hot_reloading.rs"})})," example with\n",(0,a.jsx)(n.code,{children:"cargo run --features derive --example hot_reloading"}),". Complete source code is also provided at the\n",(0,a.jsx)(n.a,{href:"#complete-example",children:"end of this page"}),"."]})}),"\n",(0,a.jsxs)(n.p,{children:["Hot-reloading lets you edit and apply changes to your WGSL shaders while the application using them is running. This\nenables quick iterations and debugging on existing shaders without needing to rebuild and restart the application.\nWhen using ",(0,a.jsx)(n.code,{children:"wgcore"})," (and its ",(0,a.jsx)(n.a,{href:"/docs/user_guides/wgcore/shaders_composition",children:(0,a.jsx)(n.code,{children:"derive(Shader)"})}),") to associate a ",(0,a.jsx)(n.code,{children:".wgsl"})," file to a rust struct,\nit is possible to benefit from hot-reloading with very little boilerplate:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",children:"// Initialize the gpu device and queue.\nlet gpu = GpuInstance::new().await?;\n// Detects file changes.\nlet mut hot_reload = HotReloadState::new()?;\n// Register the shader for hot-reloading.\nMyShader::watch_sources(&mut hot_reload)?;\n// Instantiate the compute pipeline.\nlet mut compute_pipeline = MyShader::from_device(gpu.device())?;\n\n// This can be, for example, your game loop or window event loop.\nloop {\n    // Detects file changes since the last call to `update_changes`.\n    hot_reload.update_changes();\n    // Reload the compute pipeline if it changed.\n    compute_pipeline.reload_if_changed(gpu.device(), &hot_reload);\n}\n"})}),"\n",(0,a.jsx)(n.h2,{id:"how-does-this-work",children:"How does this work?"}),"\n",(0,a.jsxs)(n.p,{children:["Say you are working on an application\u2019s code (like, for example, one of the examples of\n",(0,a.jsx)(n.a,{href:"https://github.com/dimforge/wgsparkl",children:"wgsparkl"}),"), and depends on local version of its dependencies (like\n",(0,a.jsx)(n.a,{href:"https://github.com/dimforge/wgmath",children:"wgebra"}),") using a ",(0,a.jsx)(n.a,{href:"https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html",children:"cargo patch"}),".\nUsing the code snippet from the previous section, you can leverage hot-reloading on ",(0,a.jsx)(n.strong,{children:"any"})," shader, even the ones\nfrom the local dependencies. The ",(0,a.jsx)(n.code,{children:"derive(Shader)"})," macros will automatically figure out the absolute path of all the\nshaders at compile-time so they can be watched with ",(0,a.jsx)(n.code,{children:"Shader::watch_sources"}),"."]}),"\n",(0,a.jsx)(n.admonition,{type:"danger",children:(0,a.jsx)(n.p,{children:"This automatic detection of shader paths might not work properly if you run your application from a directory that is\ndifferent from the root of the rust workspace it is built from. This is due to some limitations in the Rust libraries\nthat will hopefully be stabilized in future versions of the compiler."})}),"\n",(0,a.jsxs)(n.p,{children:["This won\u2019t work for shaders of a dependency that is not available locally on your machine, since there is no way that\nthe actual shader file could be modified (since they are embedded in the library directly). In order to make it work\nfor these shaders, you can ",(0,a.jsx)(n.a,{href:"/docs/user_guides/wgcore/overwriting_shaders",children:"overwrite them"})," with a local version of the shader by specifying\ntheir path with ",(0,a.jsx)(n.code,{children:"Shader::set_wgsl_path"}),". After their path is overwritten, ",(0,a.jsx)(n.code,{children:"Shader::watch_sources"})," needs to be called\nand hot-reloading will work."]}),"\n",(0,a.jsx)(n.admonition,{type:"tip",children:(0,a.jsxs)(n.p,{children:["If your shader depends on other shaders with ",(0,a.jsx)(n.code,{children:"shader(derive(...))"}),", then calling ",(0,a.jsx)(n.code,{children:"Shader::watch_sources"})," on the\ntop-level shader will automatically register all its dependencies as well for hot-reloading ","\ud83d\udd25"]})}),"\n",(0,a.jsx)(n.h2,{id:"complete-example",children:"Complete example"}),"\n",(0,a.jsxs)(i.A,{groupId:"wgcore",defaultValue:"rust",values:[{label:"main.rs",value:"rust"},{label:"kernel.wgsl",value:"wgsl"}],children:[(0,a.jsx)(s.A,{value:"rust",children:(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",children:'#[cfg(not(feature = "derive"))]\nstd::compile_error!(\n    r#"\n    ###############################################################\n    ## The `derive` feature must be enabled to run this example. ##\n    ###############################################################\n"#\n);\n\nuse nalgebra::{DVector, Vector4};\nuse wgcore::composer::ComposerExt;\nuse wgcore::gpu::GpuInstance;\nuse wgcore::hot_reloading::HotReloadState;\nuse wgcore::kernel::{KernelInvocationBuilder, KernelInvocationQueue};\nuse wgcore::tensor::{GpuScalar, GpuVector};\nuse wgcore::Shader;\nuse wgpu::{BufferUsages, ComputePipeline};\n\n#[derive(Shader)]\n#[shader(src = "hot_reloading.wgsl", composable = false)]\nstruct ShaderHotReloading {\n    main: ComputePipeline,\n}\n\n#[async_std::main]\nasync fn main() -> anyhow::Result<()> {\n    // Initialize the gpu device and its queue.\n    //\n    // Note that `GpuInstance` is just a simple helper struct for initializing the gpu resources.\n    // You are free to initialize them independently if more control is needed, or reuse the ones\n    // that were already created/owned by e.g., a game engine.\n    let gpu = GpuInstance::new().await?;\n\n    // Load and compile our kernel. The `from_device` function was generated by the `Shader` derive.\n    // Note that its dependency to `Composable` is automatically resolved by the `Shader` derive\n    // too.\n    let mut kernel = ShaderHotReloading::from_device(gpu.device()
1)?;\n\n    // Create the buffers.\n    let buffer = GpuScalar::init(\n        gpu.device(),\n        0u32,\n        BufferUsages::STORAGE | BufferUsages::COPY_SRC,\n    );\n    let staging = GpuScalar::init(\n        gpu.device(),\n        0u32,\n        BufferUsages::COPY_DST | BufferUsages::MAP_READ,\n    );\n\n    // Init hot-reloading.\n    let mut hot_reload = HotReloadState::new()?;\n    ShaderHotReloading::watch_sources(&mut hot_reload)?;\n\n    // Queue the operation.\n    println!("#############################");\n    println!("Edit the file `hot_reloading.wgsl`.\\nThe updated result will be printed below whenever a change is detected.");\n    println!("#############################");\n\n    for loop_id in 0.. {\n        // Detect & apply changes.\n        hot_reload.update_changes();\n        match kernel.reload_if_changed(gpu.device(), &hot_reload) {\n            Ok(changed) => {\n                if changed || loop_id == 0 {\n                    // We detected a change (or this is the first loop).\n                    // Read the result.\n                    let mut queue = KernelInvocationQueue::new(gpu.device());\n                    KernelInvocationBuilder::new(&mut queue, &kernel.main)\n                        .bind0([buffer.buffer()])\n                        .queue(1);\n\n                    // Encode & submit the operation to the gpu.\n                    let mut encoder = gpu.device().create_command_encoder(&Default::default());\n                    // Run our kernel.\n                    queue.encode(&mut encoder, None);\n                    // Copy the result to the staging buffer.\n                    staging.copy_from(&mut encoder, &buffer);\n                    gpu.queue().submit(Some(encoder.finish()));\n\n                    let result_read = staging.read(gpu.device()).await.unwrap();\n                    println!("Current result value: {}", result_read[0]);\n                }\n            }\n            Err(e) => {\n                // Hot-reloading failed, likely due to a syntax error in the shader.\n                println!("Hot reloading error: {:?}", e);\n            }\n        }\n    }\n\n    Ok(())\n}\n'})})}),(0,a.jsx)(s.A,{value:"wgsl",children:(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",children:"@group(0) @binding(0)\nvar<storage, read_write> a: u32;\n\n@compute @workgroup_size(1, 1, 1)\nfn main(@builtin(global_invocation_id) invocation_id: vec3<u32>) {\n    a = 1u; // Change this value and save the file while running the `hot_reloading` example.\n}\n"})})})]})]})}function p(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(h,{...e})}):h(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>i});t(6540);var r=t(8215);const a={tabItem:"tabItem_Ymn6"};var o=t(4848);function i(e){let{children:n,hidden:t,className:i}=e;return(0,o.jsx)("div",{role:"tabpanel",className:(0,r.A)(a.tabItem,i),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>y});var r=t(6540),a=t(8215),o=t(3104),i=t(6347),s=t(205),l=t(7485),u=t(1682),d=t(679);function c(e){return r.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,r.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad <Tabs> child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:t}=e;return(0,r.useMemo)((()=>{const e=n??function(e){return c(e).map((e=>{let{props:{value:n,label:t,attributes:r,default:a}}=e;return{value:n,label:t,attributes:r,default:a}}))}(t);return function(e){const n=(0,u.XI)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e}),[n,t])}function p(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function g(e){let{queryString:n=!1,groupId:t}=e;const a=(0,i.W6)(),o=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,l.aZ)(o),(0,r.useCallback)((e=>{if(!o)return;const n=new URLSearchParams(a.location.search);n.set(o,e),a.replace({...a.location,search:n.toString()})}),[o,a])]}function f(e){const{defaultValue:n,queryString:t=!1,groupId:a}=e,o=h(e),[i,l]=(0,r.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(n){if(!p({value:n,tabValues:t}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const r=t.find((e=>e.default))??t[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:n,tabValues:o}))),[u,c]=g({queryString:t,groupId:a}),[f,m]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[a,o]=(0,d.Dv)(t);return[a,(0,r.useCallback)((e=>{t&&o.set(e)}
1),[t,o])]}({groupId:a}),b=(()=>{const e=u??f;return p({value:e,tabValues:o})?e:null})();(0,s.A)((()=>{b&&l(b)}),[b]);return{selectedValue:i,selectValue:(0,r.useCallback)((e=>{if(!p({value:e,tabValues:o}))throw new Error(`Can't select invalid tab value=${e}`);l(e),c(e),m(e)}),[c,m,o]),tabValues:o}}var m=t(2303);const b={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var w=t(4848);function v(e){let{className:n,block:t,selectedValue:r,selectValue:i,tabValues:s}=e;const l=[],{blockElementScrollPositionUntilNextRender:u}=(0,o.a_)(),d=e=>{const n=e.currentTarget,t=l.indexOf(n),a=s[t].value;a!==r&&(u(n),i(a))},c=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=l.indexOf(e.currentTarget)+1;n=l[t]??l[0];break}case"ArrowLeft":{const t=l.indexOf(e.currentTarget)-1;n=l[t]??l[l.length-1];break}}n?.focus()};return(0,w.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,a.A)("tabs",{"tabs--block":t},n),children:s.map((e=>{let{value:n,label:t,attributes:o}=e;return(0,w.jsx)("li",{role:"tab",tabIndex:r===n?0:-1,"aria-selected":r===n,ref:e=>l.push(e),onKeyDown:c,onClick:d,...o,className:(0,a.A)("tabs__item",b.tabItem,o?.className,{"tabs__item--active":r===n}),children:t??n},n)}))})}function _(e){let{lazy:n,children:t,selectedValue:o}=e;const i=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===o));return e?(0,r.cloneElement)(e,{className:(0,a.A)("margin-top--md",e.props.className)}):null}return(0,w.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,r.cloneElement)(e,{key:n,hidden:e.props.value!==o})))})}function x(e){const n=f(e);return(0,w.jsxs)("div",{className:(0,a.A)("tabs-container",b.tabList),children:[(0,w.jsx)(v,{...n,...e}),(0,w.jsx)(_,{...n,...e})]})}function y(e){const n=(0,m.A)();return(0,w.jsx)(x,{...e,children:c(e.children)},String(n))}},8453:(e,n,t)=>{t.d(n,{R:()=>i,x:()=>s});var r=t(6540);const a={},o=r.createContext(a);function i(e){const n=r.useContext(o);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),r.createElement(o.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.