{"id":237,"date":"2021-09-19T21:03:38","date_gmt":"2021-09-19T13:03:38","guid":{"rendered":"https:\/\/blog.indeex.club\/?p=237"},"modified":"2021-10-13T10:43:13","modified_gmt":"2021-10-13T02:43:13","slug":"webgpu%e5%9f%ba%e7%a1%80","status":"publish","type":"post","link":"https:\/\/blog.indeex.club\/index.php\/2021\/09\/19\/webgpu%e5%9f%ba%e7%a1%80\/","title":{"rendered":"Webgpu\u57fa\u7840"},"content":{"rendered":"<hr \/>\n<p>\u524d\u4e24\u5929\u770b\u4e86\u4e0bApi\uff0c\u53d8\u5316\u6709\u70b9\u5927\uff0c\u91cd\u65b0\u6574\u7406\u4e0b\u3002<\/p>\n<h2>\u521d\u59cb\u5316<\/h2>\n<pre><code class=\"language-javascript line-numbers\">const entry: GPU = navigator.gpu;\nif (!entry) {\n    throw new Error('WebGPU is not supported on this browser.\u4f60\u7c7b\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u3002')\n}\n<\/code><\/pre>\n<h3>Adapter\u9002\u914d\u5668<\/h3>\n<p>Adapter\u5c31\u662f\u663e\u5361\uff0c\u9664\u4e86N\u5361\u8fd8\u6709A\u5361\u3001\u9ad8\u901a\u9a81\u9f99\u7684SoC\u7b49\u7b49\u3002<\/p>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/webgpubase1.png\" alt=\"Webgpu\u57fa\u7840\" \/><\/p>\n<pre><code class=\"language-javascript line-numbers\">let adapter: GPUAdapter = null;\n\nadapter = await entry.requestAdapter();\n<\/code><\/pre>\n<h3>Device\u8bbe\u5907<\/h3>\n<p>Device\u5c31\u662f\u903b\u8f91\u5bf9\u8c61\u5316\u7684\u663e\u5361\uff0c\u53ef\u4ee5\u6784\u5efa\u6570\u636e\u7ed3\u6784\u3002<\/p>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/webgpubase2.png\" alt=\"Webgpu\u57fa\u7840\" \/><\/p>\n<pre><code class=\"language-javascript line-numbers\">let device: GPUDevice = null;\n\ndevice = await adapter.requestDevice();\n<\/code><\/pre>\n<p>requestDevice\u65b9\u6cd5\uff0c\u5141\u8bb8\u8bf7\u6c42\u4e00\u4e9b\u663e\u5361\u7684\u6269\u5c55\u7279\u6027\uff0c\u4e0e WebGL \u4f1a\u901a\u8fc7\u8bf7\u6c42\u6269\u5c55\u6765\u5f15\u5165\u989d\u5916\u7684\u529f\u80fd\u7c7b\u4f3c\u3002<\/p>\n<h3>Queue\u961f\u5217<\/h3>\n<p>Queue\u6570\u636e\u7ed3\u6784\u7684\u961f\u5217\uff0c\u5f02\u6b65\u4f20\u7ed9GPU\u3002<\/p>\n<pre><code class=\"language-javascript line-numbers\">let queue: GPUQueue = null;\n\nqueue = device.queue;\n<\/code><\/pre>\n<h3>Swapchain\u7f13\u51b2\u7ed3\u6784<\/h3>\n<p>Swapchain\u4e00\u4e2a\u7f13\u51b2\u7ed3\u6784\u3002<\/p>\n<pre><code class=\"language-javascript line-numbers\">const context: GPUCanvasContext = null;\n\ncontext = canvas.getContext('webgpu');\n\nconst swapChainDesc: GPUSwapChainDescriptor = {\n    device: device,\n    format: 'bgra8unorm',\n    usage: GPUTextureUsage.OUTPUT_ATTACHMENT | GPUTextureUsage.COPY_SRC\n};\n\ncontext.configure(swapChainDesc);\n<\/code><\/pre>\n<h3>Frame Buffer Attachments<\/h3>\n<p>Frame Buffer Attachments\u7eb9\u7406\u5f15\u7528\u3002<\/p>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/webgpubase3.png\" alt=\"Webgpu\u57fa\u7840\" \/><\/p>\n<pre><code class=\"language-javascript line-numbers\">let depthTexture: GPUTexture = null;\nlet depthTextureView: GPUTextureView = null;\n\nconst depthTextureDesc: GPUTextureDescriptor = {\n    size: [canvas.width, canvas.height, 1],\n    dimension: '2d',\n    format: 'depth24plus-stencil8',\n    usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC\n};\ndepthTexture = device.createTexture(depthTextureDesc);\ndepthTextureView = depthTexture.createView();\n\nlet colorTexture: GPUTexture = null;\nlet colorTextureView: GPUTextureView = null;\ncolorTexture = swapchain.getCurrentTexture();\ncolorTextureView = colorTexture.createView();\n<\/code><\/pre>\n<h2>\u8d44\u6e90<\/h2>\n<h3>Buffers\u7f13\u51b2\u5668<\/h3>\n<p>Buffers\u53ef\u4ee5\u4e00\u6b21\u6027\u5411\u7740\u8272\u5668\u4f20\u5165\u591a\u4e2a\u9876\u70b9\u7684\u6570\u636e\u3002<\/p>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/webgpubase4.png\" alt=\"Webgpu\u57fa\u7840\" \/><\/p>\n<pre><code class=\"language-javascript line-numbers\">const positions = new Float32Array([\n    1.0, -1.0, 0.0,\n   -1.0, -1.0, 0.0,\n    0.0,  1.0, 0.0\n]);\n\nconst colors = new Float32Array([\n    1.0, 0.0, 0.0, \/\/ \ud83d\udd34\n    0.0, 1.0, 0.0, \/\/ \ud83d\udfe2\n    0.0, 0.0, 1.0  \/\/ \ud83d\udd35\n]);\n\nconst indices = new Uint16Array([ 0, 1, 2 ]);\n\nlet positionBuffer: GPUBuffer = null;\nlet colorBuffer: GPUBuffer = null;\nlet indexBuffer: GPUBuffer = null;\n\nlet createBuffer = (arr: Float32Array | Uint16Array, usage: number) =&gt; {\n\n    let desc = { size: ((arr.byteLength + 3) &amp; ~3), usage, mappedAtCreation: true };\n    let buffer = device.createBuffer(desc);\n    const writeArray =\n        arr instanceof Uint16Array ? new Uint16Array(buffer.getMappedRange()) : new Float32Array(buffer.getMappedRange());\n    writeArray.set(arr);\n    buffer.unmap();\n    return buffer;\n};\npositionBuffer = createBuffer(positions, GPUBufferUsage.VERTEX);\ncolorBuffer = createBuffer(colors, GPUBufferUsage.VERTEX);\nindexBuffer = createBuffer(indices, GPUBufferUsage.INDEX);\n<\/code><\/pre>\n<h3>Shaders\u7740\u8272\u5668<\/h3>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/webgpubase5.png\" alt=\"Webgpu\u57fa\u7840\" \/><\/p>\n<h4>vertex shader\u9876\u70b9\u7740\u8272\u5668\u6e90\u7801<\/h4>\n<pre><code class=\"language-javascript line-numbers\">struct VSOut {\n    [[builtin(position)]] Position: vec4&lt;f32&gt;;\n    [[location(0)]] color: vec3&lt;f32&gt;;\n};\n[[stage(vertex)]]\nfn main([[location(0)]] inPos: vec3&lt;f32&gt;,\n        [[location(1)]] inColor: vec3&lt;f32&gt;) -&gt; VSOut {\n    var vsOut: VSOut;\n    vsOut.Position = vec4&lt;f32&gt;(inPos, 1.0);\n    vsOut.color = inColor;\n    return vsOut;\n}\n<\/code><\/pre>\n<h4>fragment shader\u7247\u6bb5\u7740\u8272\u5668\u6e90\u7801<\/h4>\n<pre><code class=\"language-javascript line-numbers\">[[stage(fragment)]]\nfn main([[location(0)]] inColor: vec3&lt;f32&gt;) -&gt; [[location(0)]] vec4&lt;f32&gt; {\n    return vec4&lt;f32&gt;(inColor, 1.0);\n}\n<\/code><\/pre>\n<h3>Shader Modules\u7740\u8272\u5668\u6a21\u5757<\/h3>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/webgpubase6.png\" alt=\"Webgpu\u57fa\u7840\" \/><\/p>\n<pre><code class=\"language-javascript line-numbers\">import vertShaderCode from '.\/shaders\/triangle.vert.wgsl';\nimport fragShaderCode from '.\/shaders\/triangle.frag.wgsl';\n\nlet vertModule: GPUShaderModule = null;\nlet fragModule: GPUShaderModule = null;\n\nconst vsmDesc = { code: vertShaderCode };\nvertModule = device.createShaderModule(vsmDesc);\n\nconst fsmDesc = { code: fragShaderCode };\nfragModule = device.createShaderModule(fsmDesc);\n<\/code><\/pre>\n<h3>Uniform Buffer\u8054\u5408\u7f13\u51b2\u533a<\/h3>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/webgpubase7.png\" alt=\"Webgpu\u57fa\u7840\" \/><\/p>\n<p>\u9700\u8981\u5728\u51fd\u6570\u524d\u58f0\u660e\uff1a<\/p>\n<pre><code class=\"language-javascript line-numbers\">[[block]] struct UBO {\n  modelViewProj: mat4x4&lt;f32&gt;;\n};\n[[binding(0), group(0)]] var&lt;uniform&gt; uniforms: UBO;\n\nvsOut.Position = modelViewProj * vec4&lt;f32&gt;(inPos, 1.0);\n<\/code><\/pre>\n<p>\u7136\u540e\u5728JS\u4e2d\u4f7f\u7528\uff1a<\/p>\n<pre><code class=\"language-javascript line-numbers\">const uniformData = new Float32Array([\n    1.0, 0.0, 0.0, 0.0\n    0.0, 1.0, 0.0, 0.0\n    0.0, 0.0, 1.0, 0.0\n    0.0, 0.0, 0.0, 1.0\n    \/\/ \ud83d\udd34 \n    0.9, 0.1, 0.3, 1.0\n    \/\/ \ud83d\udfe3 \n    0.8, 0.2, 0.8, 1.0\n]);\n\nlet uniformBuffer: GPUBuffer = null;\nuniformBuffer = createBuffer(uniformData, GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST);\n<\/code><\/pre>\n<h3>Pipeline Layout\u7ba1\u9053\u5e03\u5c40<\/h3>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/webgpubase8.jpeg\" alt=\"Webgpu\u57fa\u7840\" \/><\/p>\n<pre><code class=\"language-javascript line-numbers\">let bindGroupLayout: GPUBindGroupLayout = pipeline.getBindGroupLayout(0);\n<\/code><\/pre>\n<p>\u4e5f\u53ef\u4ee5\u81ea\u5b9a\uff1a<\/p>\n<pre><code class=\"language-javascript line-numbers\">let uniformBindGroupLayout: GPUBindGroupLayout = null;\nlet uniformBindGroup: GPUBindGroup = null;\nlet layout: GPUPipelineLayout = null;\n\nuniformBindGroupLayout = device.createBindGroupLayout({\n    bindings: [{\n        binding: 0,\n        visibility: GPUShaderStage.VERTEX,\n        type: \"uniform-buffer\"\n    }]\n});\n\nuniformBindGroup = device.createBindGroup({\n    layout: uniformBindGroupLayout,\n    bindings: [{\n        binding: 0,\n        resource: {\n            buffer: uniformBuffer\n        }\n    }]\n});\n\nlayout = device.createPipelineLayout({bindGroupLayouts: [uniformBindGroupLayout]});\n<\/code><\/pre>\n<h3>Graphics Pipeline\u56fe\u5f62\u7ba1\u9053<\/h3>\n<p>Graphics Pipeline\u5c06\u4e00\u4e2aobject\u8f6c\u53d8\u6210\u4e00\u5f20image\u5c55\u793a\u5728\u5c4f\u5e55\u4e0a\u7684\u8fc7\u7a0b\u3002<\/p>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/webgpubase9.png\" alt=\"Webgpu\u57fa\u7840\" \/><\/p>\n<pre><code class=\"language-javascript line-numbers\">let pipeline: GPURenderPipeline = null;\n\nconst positionAttribDesc: GPUVertexAttribute = {\n    shaderLocation: 0, \/\/ [[attribute(0)]]\n    offset: 0,\n    format: 'float32x3'\n};\nconst colorAttribDesc: GPUVertexAttribute = {\n    shaderLocation: 1, \/\/ [[attribute(1)]]\n    offset: 0,\n    format: 'float32x3'\n};\nconst positionBufferDesc: GPUVertexBufferLayout = {\n    attributes: [positionAttribDesc],\n    arrayStride: 4 * 3, \/\/ sizeof(float) * 3\n    stepMode: 'vertex'\n};\nconst colorBufferDesc: GPUVertexBufferLayout = {\n    attributes: [colorAttribDesc],\n    arrayStride: 4 * 3, \/\/ sizeof(float) * 3\n    stepMode: 'vertex'\n};\n\nconst depthStencil: GPUDepthStencilState = {\n    depthWriteEnabled: true,\n    depthCompare: 'less',\n    format: 'depth24plus-stencil8'\n};\n\nconst pipelineLayoutDesc = { bindGroupLayouts: [] };\nconst layout = device.createPipelineLayout(pipelineLayoutDesc);\n\nconst vertex: GPUVertexState = {\n    module: vertModule,\n    entryPoint: 'main',\n    buffers: [positionBufferDesc, colorBufferDesc]\n};\n\nconst colorState: GPUColorTargetState = {\n    format: 'bgra8unorm'\n};\nconst fragment: GPUFragmentState = {\n    module: fragModule,\n    entryPoint: 'main',\n    targets: [colorState],\n};\n\nconst primitive: GPUPrimitiveState = {\n    frontFace: 'cw',\n    cullMode: 'none', topology: 'triangle-list'\n};\nconst pipelineDesc: GPURenderPipelineDescriptor = {\n    layout,\n    vertex,\n    fragment,\n    primitive,\n    depthStencil,\n};\npipeline = device.createRenderPipeline(pipelineDesc);\n<\/code><\/pre>\n<h3>Command Encoder<\/h3>\n<p>Command Encoder\u5bf9\u51c6\u5907\u5728\u56fe\u5f62\u7ba1\u9053\u7f16\u7801\u5668\u7ec4\u4e2d\u6267\u884c\u7684\u6240\u6709\u7ed8\u56fe\u547d\u4ee4\u8fdb\u884c\u7f16\u7801\u3002\u5b8c\u6210\u7f16\u7801\u540e\uff0c\u5f97\u5230\u4e00\u4e2a\u53ef\u4ee5\u63d0\u4ea4\u5230\u961f\u5217\u7684\u547d\u4ee4\u7f13\u51b2\u533a\u3002<\/p>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/webgpubase10.png\" alt=\"Webgpu\u57fa\u7840\" \/><\/p>\n<p>\u547d\u4ee4\u7f13\u51b2\u533a\u4e00\u65e6\u63d0\u4ea4\u5230\u961f\u5217\uff0c\u5c31\u4f1a\u5728 GPU \u4e0a\u6267\u884c\u6e32\u67d3\uff1a<\/p>\n<pre><code class=\"language-javascript line-numbers\">let commandEncoder: GPUCommandEncoder = null;\nlet passEncoder: GPURenderPassEncoder = null;\n\nfunction encodeCommands() {\n    let colorAttachment: GPURenderPassColorAttachment = {\n        view: colorTextureView,\n        loadValue: { r: 0, g: 0, b: 0, a: 1 },\n        storeOp: 'store'\n    };\n    const depthAttachment: GPURenderPassDepthStencilAttachment = {\n        view: depthTextureView,\n        depthLoadValue: 1,\n        depthStoreOp: 'store',\n        stencilLoadValue: 'load',\n        stencilStoreOp: 'store'\n    };\n    const renderPassDesc: GPURenderPassDescriptor = {\n        colorAttachments: [colorAttachment],\n        depthStencilAttachment: depthAttachment\n    };\n    commandEncoder = device.createCommandEncoder();\n\n    passEncoder = commandEncoder.beginRenderPass(renderPassDesc);\n    passEncoder.setPipeline(pipeline);\n    passEncoder.setViewport(0, 0, canvas.width, canvas.height, 0, 1);\n    passEncoder.setScissorRect(0, 0, canvas.width, canvas.height);\n    passEncoder.setVertexBuffer(0, positionBuffer);\n    passEncoder.setVertexBuffer(1, colorBuffer);\n    passEncoder.setIndexBuffer(indexBuffer, 'uint16');\n    passEncoder.drawIndexed(3);\n    passEncoder.endPass();\n    queue.submit([commandEncoder.finish()]);\n    }\n<\/code><\/pre>\n<h3>Render\u6e32\u67d3<\/h3>\n<p>\u6e32\u67d3\u7684\u65f6\u5019\u53ef\u4ee5\u7528requestAnimationFrame\u505a\u4e2a\u52a8\u753b\ud83d\ude04\u3002<\/p>\n<p><img src=\"https:\/\/hungking.cc\/assets\/imgs\/indeex.cc\/webgpubase11.gif\" alt=\"Webgpu\u57fa\u7840\" \/><\/p>\n<pre><code class=\"language-javascript line-numbers\">const render = () =&gt; {\n\ncolorTexture = context.getCurrentTexture();\ncolorTextureView = colorTexture.createView();\n\nencodeCommands();\n\nrequestAnimationFrame(render);\n};\n<\/code><\/pre>\n<p>\u54c8\u54c8\uff0c\u5c31\u8fd9\u4e48\u7b80\u5355\u3002<\/p>\n<p>code enjoy! \ud83d\udc3e\ud83d\udc3e\ud83d\udc3e\ud83d\udc3e\ud83d\udc3e\ud83d\udc3e\ud83d\udc3e\ud83d\udc3e\ud83d\udc0c\ud83d\udc1d<\/p>\n<p>\u4f5c\u8005\uff1aindeex<\/p>\n<p>\u94fe\u63a5\uff1a<a class=\"wp-editor-md-post-content-link\" href=\"https:\/\/indeex.club\/\">https:\/\/indeex.club<\/a><\/p>\n<p>\u8457\u4f5c\u6743\u5f52\u4f5c\u8005\u6240\u6709\u3002\u5546\u4e1a\u8f6c\u8f7d\u8bf7\u8054\u7cfb\u4f5c\u8005\u83b7\u5f97\u6388\u6743\uff0c\u975e\u5546\u4e1a\u8f6c\u8f7d\u8bf7\u6ce8\u660e\u51fa\u5904\u3002<\/p>\n<hr \/>\n","protected":false},"excerpt":{"rendered":"<p>\u524d\u4e24\u5929\u770b\u4e86\u4e0bApi\uff0c\u53d8\u5316\u6709\u70b9\u5927\uff0c\u91cd\u65b0\u6574\u7406\u4e0b\u3002 \u521d\u59cb\u5316 const entry: GPU = navi<a href=\"https:\/\/blog.indeex.club\/index.php\/2021\/09\/19\/webgpu%e5%9f%ba%e7%a1%80\/\" class=\"read-more\">Read More<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[10],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/posts\/237"}],"collection":[{"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/comments?post=237"}],"version-history":[{"count":3,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/posts\/237\/revisions"}],"predecessor-version":[{"id":241,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/posts\/237\/revisions\/241"}],"wp:attachment":[{"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/media?parent=237"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/categories?post=237"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.indeex.club\/index.php\/wp-json\/wp\/v2\/tags?post=237"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}