WebGPU-4

第一个最简单的sample,绘制一个三角形。
在这里插入图片描述
hello_triangle的效果图。

我们一点一点来拆解这部分的内容。
首先是关于引入的一个第三方库 dist/utils.js
这是由Kai Ninomiya编译的将shader编译成SPIR-V的工具。具体可以参考:https://github.com/kainino0x/shaderc
看git是个狠人啊 -_-!!!
web版在https://github.com/kainino0x/-webgpu-shaderc,另一个repo。
至于SPIR-V的表述:https://blog.youkuaiyun.com/cloudqiu/article/details/60334783?utm_source=blogxgwz9
简单说就是编译出来一堆32bit的数据流,而不再是之前的明文shader了。

然后定义了一个600*600的canvas,定死了canvas的尺寸,缩放并不会改变窗口的大小。

<canvas height=600 width=600></canvas>

接下来我们看shader的部分:

const vertexShaderGLSL = `#version 450
      const vec2 pos[3] = vec2[3](vec2(0.0f, 0.5f), vec2(-0.5f, -0.5f), vec2(0.5f, -0.5f));

      void main() {
          gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
      }
    `;

    const fragmentShaderGLSL = `#version 450
      layout(location = 0) out vec4 outColor;

      void main() {
          outColor = vec4(1.0, 0.0, 0.0, 1.0);
      }
    `;

这个简单的demo只定义了2个shader,vertexfragment两个,然后问题就出现了:
webgpu的shader版本是450,参考之前的WebGPU的spec:

interface GPUShaderStageBit {
    const u32 NONE = 0;
    const u32 VERTEX = 1;
    const u32 FRAGMENT = 2;
    const u32 COMPUTE = 4;
};

说明WebGPU就支持3种,vertex shaderfragment shadercompute shader,但是glsl 4.5的版本其实还支持 tessellation shader,具体包括 evaluationcontrol 两部分,此外还有geometry shader,也就是说,WebGPU阉割掉了3个shader模块。

再看shader本身的内容,就比较简单了,先定义了一个const的2维数组,充作三角形的3个顶点,然后通过gl_VertexIndex送入gl_Position,通过pipeline三个点被组合成了一个三角形,然后赋值红色。差不多就这样了。

再来看init函数中的实现,

	  const adapter = await navigator.gpu.requestAdapter();  //获取GPU适配器
      const device = await adapter.requestDevice({});  //获取设备
      const canvas = document.querySelector('canvas'); //拿到canvas
      const context = canvas.getContext('gpupresent'); // 拿到上下文
      const swapChainFormat = "bgra8unorm"; // 定义swapbuffer的格式为RGBA8位的无符号归一化格式
      // 定义交换的内容
      const swapChain = context.configureSwapChain({
        device,
        format: swapChainFormat,
      });

可以看到整个过程其实和WebGL的设置似乎没有什么本质的区别,如果换作webgl,过程可能更简单些

    //通过getElementById()方法获取canvas画布
    var canvas=document.getElementById('webgl');
    //通过方法getContext()获取WebGL上下文
    var gl=canvas.getContext('webgl');

大体初始化的过程换汤不换药,还是比较好理解的。
然后通过API自带的函数,设置device和buffer类型。
设置方式和Spec中给出的接口描述也是一毛一样:

dictionary GPUSwapChainDescriptor {
    required GPUDevice device;
    required GPUTextureFormat format;
    GPUTextureUsageFlags usage = GPUTextureUsage.OUTPUT_ATTACHMENT;
};

然后设置了渲染的流程,pipeline
pipilne需要实现几个变量的描述符,包括layoutvertexStagefragmentStageprimitiveTopologyvertexInputrasterizationStatecolorStates

	   const pipeline = device.createRenderPipeline({
	        // 没有layout,因为我们并没有送入什么特别的数据,点数据也是由shader自带的const数组而已,所以这里是空
	        layout: device.createPipelineLayout({ bindGroupLayouts: [] }), 
	        // vertex shader
	        vertexStage: {
	          module: device.createShaderModule({
	            code: Utils.compile("v", vertexShaderGLSL),
	          }),
	          entryPoint: "main",
	        },
	        // fragment shader
	        fragmentStage: {
	          module: device.createShaderModule({
	            code: Utils.compile("f", fragmentShaderGLSL),
	          }),
	          entryPoint: "main"
	        },
	
			// 绘制图元的类型
	        primitiveTopology: "triangle-list",
	        
	        // index的类型
	        vertexInput: {
	          indexFormat: "uint32",
	        },
	  		
	  		// 光栅化的设置
	        rasterizationState: {
	          frontFace: 'ccw',
	          cullMode: 'none',
	        },
			// 颜色设置,没有alpha和blend
	        colorStates: [{
	          format: swapChainFormat,
	          alphaBlend: {},
	          colorBlend: {},
	        }],
      });

到这里大致可以看到,我们要绘制一个三角形,颜色为红色,vertext shader包办了point数据,我们只需要绘制一个三角形即可。
流程和之前webgl的流程本质上没有什么特别的不同,除了需要指出颜色的格式,绘制的顺序(顺时针还是逆时针)
等等一些很细碎的设置之外。

最后我们来看一下frame函数:

      function frame() {
        // 新建一个commandEncoder,
        const commandEncoder = device.createCommandEncoder({});
        // 获取当前buffer,textureView
        const textureView = swapChain.getCurrentTexture().createDefaultView();

		// 创建render pass的描述符,指定操作的buffer,clear颜色,load和store时的操作。
        const renderPassDescriptor = {
          colorAttachments: [{
            loadOp: "clear",
            storeOp: "store",
            clearColor: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
            attachment: textureView,
          }],
        };
        
        // 开始绘制
        const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
        // 设定绘制流程
        passEncoder.setPipeline(pipeline);
        // 指定要画什么
        passEncoder.draw(3, 1, 0, 0);
        // 结束绘制
        passEncoder.endPass();

        // 送入命令队列
        device.getQueue().submit([commandEncoder.finish()]);

        requestAnimationFrame(frame);
      }
      
      requestAnimationFrame(frame);
    }

从GPU的角度来看这个问题,我们送如GPU一个命令队列(queue),也就是一堆绘制渲染的命令,这些命令包含几个步骤,
首先我们设置好要绘制的buffer,将它摆到当前(current)的位置,
然后clear一下颜色,清理成黑色,
然后设定好绘制的流程setPipeline,这个流程告诉我改怎么处理送进来的数据。
接着我们启动了draw命令,参考spec里的解释:

void draw(u32 vertexCount, u32 instanceCount, u32 firstVertex, u32 firstInstance);

我们要绘制3个vertex,1个instance,都从0开始,即vertex的index是0-1-2,正好和shader中的3个const vertex对应。
由于我们设置过了绘制的是triangle-list,每三个点被组合成一个三角形,送入fragment shager,加上之前设置过的光栅化设置,最终屏幕上出现了一个红色的三角形。
绘制任务结束。

总结:在WebGL的基础上再看这个流程,基本没有出什么大圈子,但是也能看到设置的过程比之前繁琐了很多,加上shader的版本升级,可以用的shader多了一个compute shader,应该是有非常多的新的特性可以扩展的。继续深入探索,一定可以挖掘GPU更强大的潜力。 下一章继续分析第二个sample-rotating_cube

注:整个samples使用的功能并不能涵盖WebGPU和GLSL4.5所有的内容,解释完所有的samples之后会继续自己写新的例子做些有趣的功能

统一结尾:以上均为个人理解和一家之言,有任何错漏之处欢迎留言讨论,共同进步,一经发现错漏,必立刻更新,且会在修改处指明reporter。谢谢

<think>我们正在处理一个TypeError,错误信息是“Cannotreadpropertiesofnull(reading'limits')”,发生在webgpu-device.ts文件中。这通常表示在尝试访问一个null对象的'limits'属性时发生了错误。根据错误信息,我们可以推断:1.代码中有一个对象,我们期望它是一个有效的WebGPU设备对象(非null),但实际上它是null。2.当我们尝试访问这个null对象的'limits'属性时,就抛出了这个错误。可能的原因:-在请求WebGPU设备(`adapter.requestDevice()`)时失败,返回的设备为null,但代码没有检查设备是否为null就直接使用。-设备请求是异步的,可能在设备还未成功返回时就尝试使用它(即没有正确处理异步流程)。-浏览器或运行环境不支持WebGPU,导致无法获取设备。解决方案步骤:1.确保运行环境支持WebGPU:检查浏览器或运行环境是否支持WebGPU。可以通过检查`navigator.gpu`是否存在来判断。2.在请求设备后,检查设备是否为null:在调用`requestDevice()`后,应该检查返回的设备对象是否为null,如果是,则处理错误(例如,回退到其他渲染方式或提示用户)。3.正确处理异步:确保在设备请求完成后再使用设备(使用`async/await`或`.then()`)。示例代码:```javascriptasyncfunctioninitWebGPU(){//检查浏览器是否支持WebGPUif(!navigator.gpu){console.error('WebGPUisnotsupportedinthisbrowser.');return;}//请求适配器constadapter=awaitnavigator.gpu.requestAdapter();if(!adapter){console.error('FailedtogetGPUadapter.');return;}//请求设备constdevice=awaitadapter.requestDevice();if(!device){console.error('FailedtogetGPUdevice.');return;}//现在可以安全地使用device.limitsconsole.log(device.limits);}initWebGPU();```如果按照上述步骤检查,就可以避免这个错误。另外,注意`requestDevice()`方法可能会因为各种原因失败(例如权限问题、资源不足等),所以一定要检查返回的设备对象。如果问题仍然存在,请检查以下几点:-浏览器版本是否支持WebGPU(如Chrome113+,并确保在chrome://flags中启用了WebGPU)。-如果是扩展环境(如Electron),确保启用了WebGPU支持。相关问题:1.WebGPU的适配器(adapter)和设备(device)有什么区别?2.如何检测浏览器是否支持WebGPU?3.在WebGPU中,设备(device)的limits属性包含哪些信息?4.如何处理WebGPU中异步操作可能出现的错误?</think>### TypeError: Cannot read properties of null (reading 'limits') in WebGPUDevice.ts 分析 这个错误发生在尝试访问 `null` 对象的 `limits` 属性时,具体涉及 WebGPU 设备初始化流程。以下是根本原因和解决方案: #### 错误原因 1. **设备请求失败** `adapter.requestDevice()` 返回了 `null`,但代码未检查返回值直接访问其属性: ```javascript const device = await adapter.requestDevice(); // 可能返回 null console.log(device.limits); // 访问 null.limits 导致错误 ``` 2. **浏览器兼容性问题** - 浏览器未启用 WebGPU 支持(Chrome 需在 `chrome://flags` 启用 **"Unsafe WebGPU"**) - 浏览器版本过低(WebGPU 要求 Chrome 113+ 或 Edge 113+) - 硬件/驱动不支持(如旧 GPU 或移动设备) 3. **异步初始化问题** 在设备初始化完成前提前访问 `device` 对象: ```javascript initWebGPU(); // 异步操作未完成 render(); // 立即访问未初始化的 device ``` #### 解决方案 ##### 1. 添加空值检查 在访问 `device` 前验证其有效性: ```javascript const device = await adapter.requestDevice(); if (!device) { throw new Error("WebGPU device request failed"); } console.log(device.limits); // 安全访问 ``` ##### 2. 启用浏览器支持 - **Chrome/Edge**:访问 `chrome://flags` → 搜索 **"Unsafe WebGPU"** → 设置为 **Enabled** → 重启浏览器 - **Firefox**:在 `about:config` 中启用 `dom.webgpu.enabled` ##### 3. 验证环境支持 添加运行时检测: ```javascript if (!navigator.gpu) { alert("WebGPU not supported in this browser!"); return; } const adapter = await navigator.gpu.requestAdapter(); if (!adapter) { alert("No compatible GPU adapter found!"); return; } ``` ##### 4. 调试设备请求失败 捕获设备请求错误: ```javascript try { const device = await adapter.requestDevice({ requiredLimits: { maxStorageBufferBindingSize: 1024 } // 示例需求 }); } catch (error) { console.error("Device request failed:", error); } ``` #### 关键检查点 1. 浏览器是否启用 WebGPU 实验性功能 2. 设备请求是否包含不支持的 `requiredLimits` 配置 3. 是否存在安全限制(如非 HTTPS 环境或跨域资源) 4. 硬件是否满足 WebGPU 最低要求 > **扩展参考**:WebGPU 设备初始化流程遵循 [W3C WebGPU 规范](https://www.w3.org/TR/webgpu/#gpudevice),设备创建失败时规范要求返回 `null` 而非抛出异常[^1]。 --- ### 相关问题 1. 如何在 Chrome 中启用 WebGPU 支持? 2. WebGPU 的 `requiredLimits` 参数如何配置? 3. 哪些浏览器版本完全支持 WebGPU4. 如何检测用户设备的 WebGPU 兼容性? 5. WebGPUWebGL 在错误处理机制上有何区别? [^1]: W3C WebGPU Device Interface Specification, Section 3.3 Device Creation.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值