From e4f45841ccdd3ede4cabfbbf0145d7f5953b56ea Mon Sep 17 00:00:00 2001 From: sotrh Date: Wed, 29 Dec 2021 18:30:10 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20sotrh/le?= =?UTF-8?q?arn-wgpu@75bc7c4ef7317f467601fc1cb929021ee256674e=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/33.4daba1cc.js | 1 + assets/js/33.caeaef96.js | 1 - assets/js/{app.aa00988d.js => app.07ca68b9.js} | 4 ++-- beginner/tutorial1-window/index.html | 8 ++++---- beginner/tutorial2-surface/index.html | 8 ++++---- beginner/tutorial3-pipeline/index.html | 8 ++++---- beginner/tutorial4-buffer/index.html | 8 ++++---- beginner/tutorial5-textures/index.html | 8 ++++---- beginner/tutorial6-uniforms/index.html | 8 ++++---- beginner/tutorial7-instancing/index.html | 8 ++++---- beginner/tutorial8-depth/index.html | 8 ++++---- beginner/tutorial9-models/index.html | 8 ++++---- index.html | 8 ++++---- intermediate/pbr-notes.html | 8 ++++---- intermediate/tutorial10-lighting/index.html | 8 ++++---- intermediate/tutorial11-normals/index.html | 8 ++++---- intermediate/tutorial12-camera/index.html | 8 ++++---- intermediate/tutorial13-threading/index.html | 8 ++++---- news/0.12/index.html | 8 ++++---- news/pre-0.12/index.html | 8 ++++---- showcase/alignment/index.html | 10 +++++----- showcase/compute/index.html | 8 ++++---- showcase/gifs/index.html | 8 ++++---- showcase/imgui-demo/index.html | 8 ++++---- showcase/index.html | 8 ++++---- showcase/pong/index.html | 8 ++++---- showcase/windowless/index.html | 8 ++++---- todo.html | 8 ++++---- 28 files changed, 104 insertions(+), 104 deletions(-) create mode 100644 assets/js/33.4daba1cc.js delete mode 100644 assets/js/33.caeaef96.js rename assets/js/{app.aa00988d.js => app.07ca68b9.js} (98%) diff --git a/assets/js/33.4daba1cc.js b/assets/js/33.4daba1cc.js new file mode 100644 index 00000000..0eaf9bb9 --- /dev/null +++ b/assets/js/33.4daba1cc.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[33],{332:function(t,e,n){"use strict";n.r(e);var a=n(10),s=Object(a.a)({},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"memory-layout-in-wgsl"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#memory-layout-in-wgsl"}},[t._v("#")]),t._v(" Memory Layout in WGSL")]),t._v(" "),n("div",{staticClass:"warn"},[n("p",[t._v("This page is currently being reworked. I want to understand the topics a bit better, but\nas 0.12 is out I want to release what I have for now.")])]),t._v(" "),n("h2",{attrs:{id:"alignment-of-vertex-and-index-buffers"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#alignment-of-vertex-and-index-buffers"}},[t._v("#")]),t._v(" Alignment of vertex and index buffers")]),t._v(" "),n("p",[t._v("Vertex buffers require defining a "),n("code",[t._v("VertexBufferLayout")]),t._v(", so the memory alignment is whatever\nyou tell WebGPU it should be. This can be really convenient for keeping down memory usage\non the GPU.")]),t._v(" "),n("p",[t._v("The Index Buffer use the alignment of whatever primitive type you specify via the "),n("code",[t._v("IndexFormat")]),t._v("\nyou pass into "),n("code",[t._v("RenderEncoder::set_index_buffer()")]),t._v(".")]),t._v(" "),n("h2",{attrs:{id:"alignment-of-uniform-and-storage-buffers"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#alignment-of-uniform-and-storage-buffers"}},[t._v("#")]),t._v(" Alignment of Uniform and Storage buffers")]),t._v(" "),n("p",[t._v("GPUs are designed to process thousands of pixels in parallel. In order to achieve this,\nsome sacrifices had to be made. Graphics hardware likes to have all the bytes you intend\non processing aligned by powers of 2. The exact specifics of why this is are beyond\nmy level of knowledge, but it's important to know so that you can trouble shoot why your\nshaders aren't working.")]),t._v(" "),n("p",[t._v("Let's take a look at the following table:")]),t._v(" "),n("hr"),t._v(" "),n("table",[n("thead",[n("tr",[n("th",[t._v("Type")]),t._v(" "),n("th",[t._v("Alignment in Bytes")]),t._v(" "),n("th",[t._v("Size in Bytes")])])]),t._v(" "),n("tbody",[n("tr",[n("td",[t._v("scalar (i32, u32, f32)")]),t._v(" "),n("td",[t._v("4")]),t._v(" "),n("td",[t._v("4")])]),t._v(" "),n("tr",[n("td",[t._v("vec2")]),t._v(" "),n("td",[t._v("8")]),t._v(" "),n("td",[t._v("8")])]),t._v(" "),n("tr",[n("td",[t._v("vec3")]),t._v(" "),n("td",[n("strong",[t._v("16")])]),t._v(" "),n("td",[t._v("12")])]),t._v(" "),n("tr",[n("td",[t._v("vec4")]),t._v(" "),n("td",[t._v("16")]),t._v(" "),n("td",[t._v("16")])])])]),t._v(" "),n("p",[t._v("You can see for "),n("code",[t._v("vec3")]),t._v(" the alignment is the next power of 2 from the size, 16. This can\ncatch beginners (and even veterans) off guard as it's not the most intuitive. This becomes especially\nimportant when we start laying out structs. Take the light struct from the "),n("RouterLink",{attrs:{to:"/intermediate/tutorial10-lighting/#seeing-the-light"}},[t._v("lighting tutorial")]),t._v(":")],1),t._v(" "),n("p",[t._v("You can see the full table of the alignments in section "),n("a",{attrs:{href:"https://www.w3.org/TR/WGSL/#alignment-and-size",target:"_blank",rel:"noopener noreferrer"}},[t._v("4.3.7.1 of the WGSL spec"),n("OutboundLink")],1)]),t._v(" "),n("div",{staticClass:"language-wgsl extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("struct Light {\n position: vec3;\n color: vec3;\n};\n")])])]),n("p",[t._v("So what's the alignment of this scruct? Your first guess would be that it's the sum of\nthe alignments of the individual fields. That might make sense if we were in Rust-land,\nbut in shader-land, it's a little more involved. The alignment for a given struct is given\nby the following equation:")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("// S is the struct in question\n// M is a member of the struct\nAlignOf(S) = max(AlignOfMember(S, M1), ... , AlignOfMember(S, Mn))\n")])])]),n("p",[t._v("Basically the alignment of the struct is the maximum of the alignments of the members of\nthe struct. This means that:")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("AlignOf(Light) \n = max(AlignOfMember(Light, position), AlignOfMember(Light, color))\n = max(16, 16)\n = 16\n")])])]),n("p",[t._v("This is why the "),n("code",[t._v("LightUniform")]),t._v(" has those padding fields. WGPU won't accept it if the data\nis not aligned correctly.")]),t._v(" "),n("h2",{attrs:{id:"how-to-deal-with-alignment-issues"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#how-to-deal-with-alignment-issues"}},[t._v("#")]),t._v(" How to deal with alignment issues")]),t._v(" "),n("p",[t._v("In general 16, is the max alignment you'll see. In that case you might think that we should\nbe able to do something like the following:")]),t._v(" "),n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{pre:!0,attrs:{class:"token attribute attr-name"}},[t._v("#[repr(C, align(16))]")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token type-definition class-name"}},[t._v("LightUniform")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n position"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("f32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n color"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("f32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("But this won't compile. The "),n("a",{attrs:{href:"https://docs.rs/bytemuck/",target:"_blank",rel:"noopener noreferrer"}},[t._v("bytemuck crate"),n("OutboundLink")],1),t._v(" doesn't work with\nstructs with implicit padding bytes. Rust can't guarantee that the memory between the fields\nhas been initialized properly. This gave be an error when I tried it:")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("error[E0512]: cannot transmute between types of different sizes, or dependently-sized types\n --\x3e code/intermediate/tutorial10-lighting/src/main.rs:246:8\n |\n246 | struct LightUniform {\n | ^^^^^^^^^^^^\n |\n = note: source type: `LightUniform` (256 bits)\n = note: target type: `_::{closure#0}::TypeWithoutPadding` (192 bits)\n")])])])])}),[],!1,null,null,null);e.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/33.caeaef96.js b/assets/js/33.caeaef96.js deleted file mode 100644 index de01997b..00000000 --- a/assets/js/33.caeaef96.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[33],{332:function(t,e,n){"use strict";n.r(e);var a=n(10),s=Object(a.a)({},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"memory-layout-in-wgsl"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#memory-layout-in-wgsl"}},[t._v("#")]),t._v(" Memory Layout in WGSL")]),t._v(" "),n("div",{staticClass:"warn"},[n("p",[t._v("This page is currently being reworked. I want to understand the topics a bit better, but\nas 0.12 is out I want to release what I have for now.")])]),t._v(" "),n("h2",{attrs:{id:"alignment-of-vertex-and-index-buffers"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#alignment-of-vertex-and-index-buffers"}},[t._v("#")]),t._v(" Alignment of vertex and index buffers")]),t._v(" "),n("p",[t._v("Vertex buffers require defining a "),n("code",[t._v("VertexBufferLayout")]),t._v(", so the memory alignment is whatever\nyou tell WebGPU it should be. This can be really convenient for keeping down memory usage\non the GPU.")]),t._v(" "),n("p",[t._v("The Index Buffer use the alignment of whatever primitive type you specify via the "),n("code",[t._v("IndexFormat")]),t._v("\nyou pass into "),n("code",[t._v("RenderEncoder::set_index_buffer()")]),t._v(".")]),t._v(" "),n("h2",{attrs:{id:"alignment-of-uniform-and-storage-buffers"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#alignment-of-uniform-and-storage-buffers"}},[t._v("#")]),t._v(" Alignment of Uniform and Storage buffers")]),t._v(" "),n("p",[t._v("GPUs are designed to process thousands of pixels in parallel. In order to achieve this,\nsome sacrifices had to be made. Graphics hardware likes to have all the bytes you intend\non processing aligned by powers of 2. The exact specifics of why this is are beyond\nmy level of knowledge, but it's important to know so that you can trouble shoot why your\nshaders aren't working.")]),t._v(" "),n("p",[t._v("Let's take a look at the following table:")]),t._v(" "),n("hr"),t._v(" "),n("table",[n("thead",[n("tr",[n("th",[t._v("Type")]),t._v(" "),n("th",[t._v("Alignment in Bytes")]),t._v(" "),n("th",[t._v("Size in Bytes")])])]),t._v(" "),n("tbody",[n("tr",[n("td",[t._v("scalar (i32, u32, f32)")]),t._v(" "),n("td",[t._v("4")]),t._v(" "),n("td",[t._v("4")])]),t._v(" "),n("tr",[n("td",[t._v("vec2")]),t._v(" "),n("td",[t._v("8")]),t._v(" "),n("td",[t._v("8")])]),t._v(" "),n("tr",[n("td",[t._v("vec3")]),t._v(" "),n("td",[n("strong",[t._v("16")])]),t._v(" "),n("td",[t._v("12")])]),t._v(" "),n("tr",[n("td",[t._v("vec4")]),t._v(" "),n("td",[t._v("16")]),t._v(" "),n("td",[t._v("16")])])])]),t._v(" "),n("p",[t._v("You can see for "),n("code",[t._v("vec3")]),t._v(" the alignment is the next power of 2 from the size, 16. This can\ncatche beginners (and even veterans) as it's not the most intuitive. This becomes especially\nimportant when we start laying out structs. Take the light struct from the "),n("RouterLink",{attrs:{to:"/intermediate/tutorial10-lighting/#seeing-the-light"}},[t._v("lighting tutorial")]),t._v(":")],1),t._v(" "),n("p",[t._v("You can see the full table of the alignments in section "),n("a",{attrs:{href:"https://www.w3.org/TR/WGSL/#alignment-and-size",target:"_blank",rel:"noopener noreferrer"}},[t._v("4.3.7.1 of the WGSL spec"),n("OutboundLink")],1)]),t._v(" "),n("div",{staticClass:"language-wgsl extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("struct Light {\n position: vec3;\n color: vec3;\n};\n")])])]),n("p",[t._v("So what's the alignment of this scruct? Your first guess would be that it's the sum of\nthe alignments of the individual fields. That might make sense if we were in Rust-land,\nbut in shader-land, it's a little more involved. The alignment for a given struct is given\nby the following equation:")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("// S is the struct in question\n// M is a member of the struct\nAlignOf(S) = max(AlignOfMember(S, M1), ... , AlignOfMember(S, Mn))\n")])])]),n("p",[t._v("Basically the alignment of the struct is the maximum of the alignments of the members of\nthe struct. This means that:")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("AlignOf(Light) \n = max(AlignOfMember(Light, position), AlignOfMember(Light, color))\n = max(16, 16)\n = 16\n")])])]),n("p",[t._v("This is why the "),n("code",[t._v("LightUniform")]),t._v(" has those padding fields. WGPU won't accept it if the data\nis not aligned correctly.")]),t._v(" "),n("h2",{attrs:{id:"how-to-deal-with-alignment-issues"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#how-to-deal-with-alignment-issues"}},[t._v("#")]),t._v(" How to deal with alignment issues")]),t._v(" "),n("p",[t._v("In general 16, is the max alignment you'll see. In that case you might think that we should\nbe able to do something like the following:")]),t._v(" "),n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{pre:!0,attrs:{class:"token attribute attr-name"}},[t._v("#[repr(C, align(16))]")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token type-definition class-name"}},[t._v("LightUniform")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n position"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("f32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n color"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("f32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("But this won't compile. The "),n("a",{attrs:{href:"https://docs.rs/bytemuck/",target:"_blank",rel:"noopener noreferrer"}},[t._v("bytemuck crate"),n("OutboundLink")],1),t._v(" doesn't work with\nstructs with implicit padding bytes. Rust can't guarantee that the memory between the fields\nhas been initialized properly. This gave be an error when I tried it:")]),t._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("error[E0512]: cannot transmute between types of different sizes, or dependently-sized types\n --\x3e code/intermediate/tutorial10-lighting/src/main.rs:246:8\n |\n246 | struct LightUniform {\n | ^^^^^^^^^^^^\n |\n = note: source type: `LightUniform` (256 bits)\n = note: target type: `_::{closure#0}::TypeWithoutPadding` (192 bits)\n")])])])])}),[],!1,null,null,null);e.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/app.aa00988d.js b/assets/js/app.07ca68b9.js similarity index 98% rename from assets/js/app.aa00988d.js rename to assets/js/app.07ca68b9.js index a7e2d47d..5dfd1757 100644 --- a/assets/js/app.aa00988d.js +++ b/assets/js/app.07ca68b9.js @@ -1,8 +1,8 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[0],[]]);!function(e){function t(t){for(var r,a,c=t[0],u=t[1],s=t[2],f=0,l=[];f0?o(r(e),9007199254740991):0}},function(e,t,n){var r=n(16);e.exports=function(e){return Object(r(e))}},function(e,t,n){var r=n(3),o=n(9),i=n(5),a=n(60),c=n(86),u=n(24),s=u.get,f=u.enforce,l=String(String).split("String");(e.exports=function(e,t,n,c){var u=!!c&&!!c.unsafe,s=!!c&&!!c.enumerable,p=!!c&&!!c.noTargetGet;"function"==typeof n&&("string"!=typeof t||i(n,"name")||o(n,"name",t),f(n).source=l.join("string"==typeof t?t:"")),e!==r?(u?!p&&e[t]&&(s=!0):delete e[t],s?e[t]=n:o(e,t,n)):s?e[t]=n:a(t,n)})(Function.prototype,"toString",(function(){return"function"==typeof this&&s(this).source||c(this)}))},function(e,t,n){var r=n(52),o=n(16);e.exports=function(e){return r(o(e))}},function(e,t,n){var r=n(7),o=n(2),i=n(5),a=Object.defineProperty,c={},u=function(e){throw e};e.exports=function(e,t){if(i(c,e))return c[e];t||(t={});var n=[][e],s=!!i(t,"ACCESSORS")&&t.ACCESSORS,f=i(t,0)?t[0]:u,l=i(t,1)?t[1]:void 0;return c[e]=!!n&&!o((function(){if(s&&!r)return!0;var e={length:-1};s?a(e,1,{enumerable:!0,get:u}):e[1]=1,n.call(e,f,l)}))}},function(e,t){e.exports=function(e){if(null==e)throw TypeError("Can't call method on "+e);return e}},function(e,t,n){var r=n(64),o=n(13),i=n(129);r||o(Object.prototype,"toString",i,{unsafe:!0})},function(e,t,n){"use strict";var r=n(0),o=n(22).filter,i=n(39),a=n(15),c=i("filter"),u=a("filter");r({target:"Array",proto:!0,forced:!c||!u},{filter:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}})},function(e,t,n){var r=n(7),o=n(83),i=n(25),a=n(14),c=n(26),u=n(5),s=n(84),f=Object.getOwnPropertyDescriptor;t.f=r?f:function(e,t){if(e=a(e),t=c(t,!0),s)try{return f(e,t)}catch(e){}if(u(e,t))return i(!o.f.call(e,t),e[t])}},function(e,t,n){var r,o=n(8),i=n(131),a=n(62),c=n(28),u=n(132),s=n(85),f=n(38),l=f("IE_PROTO"),p=function(){},d=function(e){return" + diff --git a/beginner/tutorial2-surface/index.html b/beginner/tutorial2-surface/index.html index 95899852..aee8b8e4 100644 --- a/beginner/tutorial2-surface/index.html +++ b/beginner/tutorial2-surface/index.html @@ -7,8 +7,8 @@ - - + + @@ -240,7 +240,7 @@ event_loop.r store: true, }, } -

The RenderPassColorAttachment has the view field which informs wgpu what texture to save the colors to. In this case we specify frame.view that we created using surface.get_current_texture(). This means that any colors we draw to this attachment will get drawn to the screen.

The resolve_target is the texture that will receive the resolved output. This will be the same as view unless multisampling is enabled. We don't need to specify this, so we leave it as None.

The ops field takes a wpgu::Operations object. This tells wgpu what to do with the colors on the screen (specified by frame.view). The load field tells wgpu how to handle colors stored from the previous frame. Currently, we are clearing the screen with a bluish color. The store field tells wgpu whether we want to store the rendered results to the Texture behind our TextureView (in this case it's the SurfaceTexture). We use true as we do want to store our render results. There are cases when you wouldn't want to but those

It's not uncommon to not clear the screen if the screen is going to be completely covered up with objects. If your scene doesn't cover the entire screen however you can end up with something like this.

./no-clear.png

# Validation Errors?

If wgpu is using Vulkan on your machine, you may run into validation errors if you are running an older version of the Vulkan SDK. You should be using at least version 1.2.182 as older versions can give out some false positives. If errors persist, you may have encountered a bug in wgpu. You can post an issue at https://github.com/gfx-rs/wgpu

# Challenge

Modify the input() method to capture mouse events, and update the clear color using that. Hint: you'll probably need to use WindowEvent::CursorMoved.

Last Updated: 12/29/2021, 6:22:45 PM

The RenderPassColorAttachment has the view field which informs wgpu what texture to save the colors to. In this case we specify frame.view that we created using surface.get_current_texture(). This means that any colors we draw to this attachment will get drawn to the screen.

The resolve_target is the texture that will receive the resolved output. This will be the same as view unless multisampling is enabled. We don't need to specify this, so we leave it as None.

The ops field takes a wpgu::Operations object. This tells wgpu what to do with the colors on the screen (specified by frame.view). The load field tells wgpu how to handle colors stored from the previous frame. Currently, we are clearing the screen with a bluish color. The store field tells wgpu whether we want to store the rendered results to the Texture behind our TextureView (in this case it's the SurfaceTexture). We use true as we do want to store our render results. There are cases when you wouldn't want to but those

It's not uncommon to not clear the screen if the screen is going to be completely covered up with objects. If your scene doesn't cover the entire screen however you can end up with something like this.

./no-clear.png

# Validation Errors?

If wgpu is using Vulkan on your machine, you may run into validation errors if you are running an older version of the Vulkan SDK. You should be using at least version 1.2.182 as older versions can give out some false positives. If errors persist, you may have encountered a bug in wgpu. You can post an issue at https://github.com/gfx-rs/wgpu

# Challenge

Modify the input() method to capture mouse events, and update the clear color using that. Hint: you'll probably need to use WindowEvent::CursorMoved.

Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/beginner/tutorial3-pipeline/index.html b/beginner/tutorial3-pipeline/index.html index f799068a..84a2516e 100644 --- a/beginner/tutorial3-pipeline/index.html +++ b/beginner/tutorial3-pipeline/index.html @@ -7,8 +7,8 @@ - - + + @@ -141,7 +141,7 @@ fn fs_main(in: VertexOutput) -> [[location(0)]] vec4<f32> { render_pass.draw(0..3, 0..1); // 3. } // ... -

We didn't change much, but let's talk about what we did change.

  1. We renamed _render_pass to render_pass and made it mutable.
  2. We set the pipeline on the render_pass using the one we just created.
  3. We tell wgpu to draw something with 3 vertices, and 1 instance. This is where [[builtin(vertex_index)]] comes from.

With all that you should be seeing a lovely brown triangle.

Said lovely brown triangle

# Challenge

Create a second pipeline that uses the triangle's position data to create a color that it then sends to the fragment shader. Have the app swap between these when you press the spacebar. Hint: you'll need to modify VertexOutput

Last Updated: 12/29/2021, 6:22:45 PM

We didn't change much, but let's talk about what we did change.

  1. We renamed _render_pass to render_pass and made it mutable.
  2. We set the pipeline on the render_pass using the one we just created.
  3. We tell wgpu to draw something with 3 vertices, and 1 instance. This is where [[builtin(vertex_index)]] comes from.

With all that you should be seeing a lovely brown triangle.

Said lovely brown triangle

# Challenge

Create a second pipeline that uses the triangle's position data to create a color that it then sends to the fragment shader. Have the app swap between these when you press the spacebar. Hint: you'll need to modify VertexOutput

Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/beginner/tutorial4-buffer/index.html b/beginner/tutorial4-buffer/index.html index 5207d943..89327ec5 100644 --- a/beginner/tutorial4-buffer/index.html +++ b/beginner/tutorial4-buffer/index.html @@ -7,8 +7,8 @@ - - + + @@ -247,7 +247,7 @@ render_pass. render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); // 1. render_pass.draw_indexed(0..self.num_indices, 0, 0..1); // 2. -

A couple things to note:

  1. The method name is set_index_buffer not set_index_buffers. You can only have one index buffer set at a time.
  2. When using an index buffer, you need to use draw_indexed. The draw method ignores the index buffer. Also make sure you use the number of indices (num_indices), not vertices as your model will either draw wrong, or the method will panic because there are not enough indices.

With all that you should have a garishly magenta pentagon in your window.

Magenta pentagon in window

# Color Correction

If you use a color picker on the magenta pentagon, you'll get a hex value of #BC00BC. If you convert this to RGB values you'll get (188, 0, 188). Dividing these values by 255 to get them into the [0, 1] range we get roughly (0.737254902, 0, 0.737254902). This is not the same as we are using for our vertex colors which is (0.5, 0.0, 0.5). The reason for this has to do with color spaces.

Most monitors use a color space know as sRGB. Our surface is (most likely depending on what is returned from surface.get_preferred_format()) using an sRGB texture format. The sRGB format stores colors according to their relative brightness instead of their actual brightness. The reason for this is that our eyes don't perceive light linearly. We notice more differences in darker colors than we do lighter colors.

You get an approximation of the correct color using the following formula: srgb_color = (rgb_color / 255) ^ 2.2. Doing this with an RGB value of (188, 0, 188) will give us (0.511397819, 0.0, 0.511397819). A little off from our (0.5, 0.0, 0.5). While you could tweak the formula to get the desired values, you'll likely save a lot of time by using textures instead as they are stored as sRGB by default, so they don't suffer from the same color inaccuracies that vertex colors do. We'll cover textures in the next lesson.

# Challenge

Create a more complex shape than the one we made (aka. more than three triangles) using a vertex buffer and an index buffer. Toggle between the two with the space key.

Last Updated: 12/29/2021, 6:22:45 PM

A couple things to note:

  1. The method name is set_index_buffer not set_index_buffers. You can only have one index buffer set at a time.
  2. When using an index buffer, you need to use draw_indexed. The draw method ignores the index buffer. Also make sure you use the number of indices (num_indices), not vertices as your model will either draw wrong, or the method will panic because there are not enough indices.

With all that you should have a garishly magenta pentagon in your window.

Magenta pentagon in window

# Color Correction

If you use a color picker on the magenta pentagon, you'll get a hex value of #BC00BC. If you convert this to RGB values you'll get (188, 0, 188). Dividing these values by 255 to get them into the [0, 1] range we get roughly (0.737254902, 0, 0.737254902). This is not the same as we are using for our vertex colors which is (0.5, 0.0, 0.5). The reason for this has to do with color spaces.

Most monitors use a color space know as sRGB. Our surface is (most likely depending on what is returned from surface.get_preferred_format()) using an sRGB texture format. The sRGB format stores colors according to their relative brightness instead of their actual brightness. The reason for this is that our eyes don't perceive light linearly. We notice more differences in darker colors than we do lighter colors.

You get an approximation of the correct color using the following formula: srgb_color = (rgb_color / 255) ^ 2.2. Doing this with an RGB value of (188, 0, 188) will give us (0.511397819, 0.0, 0.511397819). A little off from our (0.5, 0.0, 0.5). While you could tweak the formula to get the desired values, you'll likely save a lot of time by using textures instead as they are stored as sRGB by default, so they don't suffer from the same color inaccuracies that vertex colors do. We'll cover textures in the next lesson.

# Challenge

Create a more complex shape than the one we made (aka. more than three triangles) using a vertex buffer and an index buffer. Toggle between the two with the space key.

Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/beginner/tutorial5-textures/index.html b/beginner/tutorial5-textures/index.html index eb5fa0dd..8dcb1a15 100644 --- a/beginner/tutorial5-textures/index.html +++ b/beginner/tutorial5-textures/index.html @@ -7,8 +7,8 @@ - - + + @@ -397,7 +397,7 @@ fn fs_main(in: VertexOutput) -> [[location(0)]] vec4<f32> { } } } -

Phew!

With these changes in place, the code should be working the same as it was before, but we now have a much easier way to create textures.

# Challenge

Create another texture and swap it out when you press the space key.

Last Updated: 12/29/2021, 6:22:45 PM

Phew!

With these changes in place, the code should be working the same as it was before, but we now have a much easier way to create textures.

# Challenge

Create another texture and swap it out when you press the space key.

Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/beginner/tutorial6-uniforms/index.html b/beginner/tutorial6-uniforms/index.html index 1ac8dd76..e5d965e6 100644 --- a/beginner/tutorial6-uniforms/index.html +++ b/beginner/tutorial6-uniforms/index.html @@ -7,8 +7,8 @@ - - + + @@ -318,7 +318,7 @@ fn vs_main( self.camera_uniform.update_view_proj(&self.camera); self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[self.camera_uniform])); } -

That's all we need to do. If you run the code now you should see a pentagon with our tree texture that you can rotate around and zoom into with the wasd/arrow keys.

# Challenge

Have our model rotate on it's own independently of the the camera. Hint: you'll need another matrix for this.

Last Updated: 12/29/2021, 6:22:45 PM

That's all we need to do. If you run the code now you should see a pentagon with our tree texture that you can rotate around and zoom into with the wasd/arrow keys.

# Challenge

Have our model rotate on it's own independently of the the camera. Hint: you'll need another matrix for this.

Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/beginner/tutorial7-instancing/index.html b/beginner/tutorial7-instancing/index.html index cf5c6291..a2343e0d 100644 --- a/beginner/tutorial7-instancing/index.html +++ b/beginner/tutorial7-instancing/index.html @@ -7,8 +7,8 @@ - - + + @@ -173,7 +173,7 @@ fn vs_main( out.clip_position = camera.view_proj * model_matrix * vec4<f32>(model.position, 1.0); return out; } -

With all that done, we should have a forest of trees!

./forest.png

# Challenge

Modify the position and/or rotation of the instances every frame.

Last Updated: 12/29/2021, 6:22:45 PM

With all that done, we should have a forest of trees!

./forest.png

# Challenge

Modify the position and/or rotation of the instances every frame.

Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/beginner/tutorial8-depth/index.html b/beginner/tutorial8-depth/index.html index 167ff219..8cfd1463 100644 --- a/beginner/tutorial8-depth/index.html +++ b/beginner/tutorial8-depth/index.html @@ -7,8 +7,8 @@ - - + + @@ -100,7 +100,7 @@ stencil_ops: None, }), }); -

And that's all we have to do! No shader code needed! If you run the application, the depth issues will be fixed.

forest_fixed.png

# Challenge

Since the depth buffer is a texture, we can sample it in the shader. Because it's a depth texture, we'll have to use the samplerShadow uniform type and the sampler2DShadow function instead of sampler, and sampler2D respectively. Create a bind group for the depth texture (or reuse an existing one), and render it to the screen.

Last Updated: 12/29/2021, 6:22:45 PM

And that's all we have to do! No shader code needed! If you run the application, the depth issues will be fixed.

forest_fixed.png

# Challenge

Since the depth buffer is a texture, we can sample it in the shader. Because it's a depth texture, we'll have to use the samplerShadow uniform type and the sampler2DShadow function instead of sampler, and sampler2D respectively. Create a bind group for the depth texture (or reuse an existing one), and render it to the screen.

Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/beginner/tutorial9-models/index.html b/beginner/tutorial9-models/index.html index 4a94bede..ba5c2362 100644 --- a/beginner/tutorial9-models/index.html +++ b/beginner/tutorial9-models/index.html @@ -7,8 +7,8 @@ - - + + @@ -355,7 +355,7 @@ render_pass.

The code in main.rs will change accordingly.

render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..));
 render_pass.set_pipeline(&self.render_pipeline);
 render_pass.draw_model_instanced(&self.obj_model, 0..self.instances.len() as u32, &self.camera_bind_group);
-
Last Updated: 12/29/2021, 6:22:45 PM
Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/index.html b/index.html index 83660653..418698d6 100644 --- a/index.html +++ b/index.html @@ -7,16 +7,16 @@ - - + + -

# Introduction

# What is wgpu?

Wgpu is a Rust implementation of the WebGPU API spec. WebGPU is a specification published by the GPU for the Web Community Group. It aims to allow web code access to GPU functions in a safe and reliable manner. It does this by mimicking the Vulkan API, and translating that down to whatever API the host hardware is using (ie. DirectX, Metal, Vulkan).

Wgpu is still in development, so some of this doc is subject to change.

# Why Rust?

Wgpu actually has C bindings to allow you to write C/C++ code with it, as well as use other languages that interface with C. That being said, wgpu is written in Rust, and it has some convenient Rust bindings that don't have to jump through any hoops. On top of that, I've been enjoying writing in Rust.

You should be fairly familiar with Rust before using this tutorial as I won't go into much detail on Rust syntax. If you're not super comfortable with Rust you can review the Rust tutorial. You should also be familiar with Cargo.

I'm using this project to learn wgpu myself, so I might miss some important details, or explain things badly. I'm always open to constructive feedback.

# Contribution and Support

  • I accept pull requests (GitHub repo) for fixing issues with this tutorial such as typos, incorrect information, and other inconsistencies.
  • Due to wgpu's rapidly changing api, I'm not accepting any new pull requests for showcase demos.
  • If you want to support me directly, check out my patreon!

# Special thanks to these patrons!

In no particular order

  • Jan Šipr
  • Danny McGee
  • Bernard Llanos
  • Ken Kochis
  • Aron Granberg
  • Ben Anderson
  • Ian Gowen
  • Paul E Hansen
  • Lennart
  • Gunstein Vatnar
  • David Laban
Last Updated: 12/29/2021, 6:22:45 PM

It's hard to tell the difference, but here's the results.

./half_dir.png

Last Updated: 12/29/2021, 6:22:45 PM

It's hard to tell the difference, but here's the results.

./half_dir.png

Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/intermediate/tutorial11-normals/index.html b/intermediate/tutorial11-normals/index.html index acabf83c..c772a336 100644 --- a/intermediate/tutorial11-normals/index.html +++ b/intermediate/tutorial11-normals/index.html @@ -7,8 +7,8 @@ - - + + @@ -477,7 +477,7 @@ render_pass. &self.camera_bind_group, &self.light_bind_group, ); -

That gives us something like this.

You can find the textures I use in the Github Repository.

Last Updated: 12/29/2021, 6:22:45 PM

That gives us something like this.

You can find the textures I use in the Github Repository.

Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/intermediate/tutorial12-camera/index.html b/intermediate/tutorial12-camera/index.html index e84c093f..166a800c 100644 --- a/intermediate/tutorial12-camera/index.html +++ b/intermediate/tutorial12-camera/index.html @@ -7,8 +7,8 @@ - - + + @@ -368,7 +368,7 @@ } }); } -

With that we should be able to move our camera wherever we want.

./screenshot.png

Last Updated: 12/29/2021, 6:22:45 PM

With that we should be able to move our camera wherever we want.

./screenshot.png

Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/intermediate/tutorial13-threading/index.html b/intermediate/tutorial13-threading/index.html index be174b5f..9ffd7602 100644 --- a/intermediate/tutorial13-threading/index.html +++ b/intermediate/tutorial13-threading/index.html @@ -7,8 +7,8 @@ - - + + @@ -99,7 +99,7 @@ }

We've parallelized loading the meshes, and making the vertex array for them. Propably a bit overkill, but rayon should prevent us from using too many threads.

You'll notice that we didn't use rayon for calculating the tangent, and bitangent. I tried to get it to work, but I was having trouble finding a way to do it without multiple mutable references to vertices. I don't feel like introducing a std::sync::Mutex, so I'll leave it for now.

This is honestly a better job for a compute shader, as the model data is going to get loaded into a buffer anyway.

# It's that easy!

Most of the wgpu types are Send + Sync, so we can use them in threads without much trouble. It was so easy, that I feel like this tutorial is too short! I'll just leave off with a speed comparison between the previous model loading code and the current code.

Elapsed (Original): 309.596382ms
 Elapsed (Threaded): 199.645027ms
-

We're not loading that many resources, so the speed up is minimal. We'll be doing more stuff with threading, but this is a good introduction.

Last Updated: 12/29/2021, 6:22:45 PM

We're not loading that many resources, so the speed up is minimal. We'll be doing more stuff with threading, but this is a good introduction.

Last Updated: 12/29/2021, 6:24:37 PM
- + diff --git a/news/0.12/index.html b/news/0.12/index.html index 01cb988e..fd3340c3 100644 --- a/news/0.12/index.html +++ b/news/0.12/index.html @@ -7,8 +7,8 @@ - - + + @@ -49,7 +49,7 @@ this change.

# Misc< breaks glob imports (aka. use anyhow::*), I had to switch qualified imports and uses (ie. anyhow::Result). This was mostly an issue on my build scripts for some of showcase examples.

The main tutorial examples weren't affected, and the changes are minor, so -if your curious feel free to look at the repo.

Last Updated: 12/29/2021, 6:22:45 PM
Last Updated: 12/29/2021, 6:24:37 PM