You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebGPU(JavaScript):CPU读取GPU缓冲区问题及排障

WebGPU鼠标拾取:缓冲区读取错误与黑屏排查

初始错误:TypeError映射失败

错误信息:

Uncaught (in promise) TypeError: Failed to execute 'mapAsync' on 'GPUBuffer': Value is not of type 'unsigned long'.

直接原因

WebGPU的GPUMapMode枚举值是大写常量,你误用了小写的GPUMapMode.read,必须替换为GPUMapMode.READ,这是导致类型错误的核心原因。


修正后黑屏问题排查

改完枚举值后无报错但黑屏,大概率是以下环节出问题:

1. 命令未提交到GPU队列

你的代码只调用了encoder.copyBufferToBuffer,但没有将编码器的命令提交给GPU执行。必须在复制操作后添加:

device.queue.submit([encoder.finish()]);

不提交命令的话,GPU不会执行缓冲区复制,后续读取的是无效数据,同时渲染流程中断会导致黑屏。

2. 缓冲区用法配置错误

  • 暂存缓冲区:必须同时设置GPUBufferUsage.MAP_READ和GPUBufferUsage.COPY_DST,不能添加STORAGE用法:
    const entityRenderTextureStagingBuffer = device.createBuffer({
      size: entitiesRenderArray.byteLength,
      usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
    });
    
  • 存储对象ID的GPU缓冲区:需要添加GPUBufferUsage.COPY_SRC,否则无法被复制到暂存缓冲区:
    const entityRenderTextureBuffer = device.createBuffer({
      size: entitiesRenderArray.byteLength,
      usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
      mappedAtCreation: false,
    });
    

3. 片段着色器逻辑问题

  • 检查是否正确计算数组索引(基于屏幕坐标:x + y * canvas宽度),确保对象ID写入到正确位置。
  • 片段着色器必须返回正常的渲染颜色,只写ID不返回颜色会直接导致黑屏,示例:
    @fragment
    fn fs_main(@builtin(position) fragCoord: vec4<f32>) -> @location(0) vec4<f32> {
      let index = u32(fragCoord.x) + u32(fragCoord.y) * u32(canvasWidth);
      entityID[index] = f32(currentEntityId);
      // 返回正常颜色,比如白色
      return vec4<f32>(1.0, 1.0, 1.0, 1.0);
    }
    

4. 映射时机错误

缓冲区复制是异步操作,必须等GPU完成复制后再映射。修改后的完整函数:

async function ReadStagingBuffer(encoder){
  encoder.copyBufferToBuffer(
    entityRenderTextureBuffer[0],
    0,
    entityRenderTextureStagingBuffer,
    0,
    entitiesRenderArray.byteLength,
  );
  // 提交命令到GPU
  device.queue.submit([encoder.finish()]);
  // 等待GPU完成所有提交的工作
  await device.queue.onSubmittedWorkDone();
  // 映射缓冲区
  await entityRenderTextureStagingBuffer.mapAsync(GPUMapMode.READ, 0, entitiesRenderArray.byteLength);
  
  const copyArrayBuffer = entityRenderTextureStagingBuffer.getMappedRange(0, entitiesRenderArray.byteLength);
  const data = copyArrayBuffer.slice(0);
  entityRenderTextureStagingBuffer.unmap();
  console.log(new Float32Array(data));
}

内容的提问来源于stack exchange,提问作者Rob Uchiha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 21:58:37