16. WebGPU 数据内存布局

news2024/11/18 18:36:10

在 WebGPU 中,提供给它的几乎所有数据都需要在内存中设定布局,以匹配在着色器中定义的内容。这与 JavaScript 和 TypeScript 形成鲜明对比,后者很少出现内存布局问题。

在 WGSL 中,当编写着色器时,通常定义 struct 。结构有点像 JavaScript 对象,声明结构的成员,类似于 JavaScript 对象的属性。但是,除了给每个属性一个名称之外,还必须给它一个类型。并且,在提供数据时,需要计算该结构的特定成员将出现在缓冲区中的哪个位置。

在 WGSL v1 中,有 4 种基本类型

  1. f32 (a 32bit floating point number)
  2. i32 (a 32bit integer)
  3. u32 (a 32bit unsigned integer)
  4. f16 (a 16bit floating point number) [注释1]

一个字节是 8 bit,所以一个 32 bit 的值需要 4 个字节,一个 16 bit 的值需要 2 个字节。

如下声明一个结构

struct OurStruct {
  velocity: f32,
  acceleration: f32,
  frameCount: u32,
};

该结构的可视化表示可能看起来像这样

在这里插入图片描述

上图中每个方块是一个字节。上面数据占用了 12 个字节。 velocity 占用前 4 个字节。 acceleration 取下 4 个, frameCount 取最后 4 个。

To pass data to the shader we need to prepare data to match the memory layout OurStruct. To do that we need to make an ArrayBuffer of 12 bytes, then setup TypedArray views of the correct type so we can fill it out.

要将数据传递给着色器,需要准备数据以匹配OurStruct内存布局 。为此,需要定义一个 12 字节的 ArrayBuffer ,然后设置正确类型的 TypedArray 视图,以便可以方便填充它。

const kOurStructSizeBytes =
  4 + // velocity
  4 + // acceleration
  4 ; // frameCount
const ourStructData = new ArrayBuffer(kOurStructSizeBytes);
const ourStructValuesAsF32 = new Float32Array(ourStructData);
const ourStructValuesAsU32 = new Uint32Array(ourStructData);

上面, ourStructData 是一个 ArrayBuffer ,它是一块内存。为了方便查看此内存的内容,创建了它的视图。 ourStructValuesAsF32 是将内存视为 32 位浮点值的视图。 ourStructValuesAsU32 是与 32 位无符号整数值相同的内存视图。

现在有了一个缓冲区和 2 个视图,下边可以在结构中设置数据。

const kVelocityOffset = 0;
const kAccelerationOffset = 1;
const kFrameCountOffset = 2;
 
ourStructValuesAsF32[kVelocityOffset] = 1.2;
ourStructValuesAsF32[kAccelerationOffset] = 3.4;
ourStructValuesAsU32[kFrameCountOffset] = 56;    // an integer value

请注意,就像编程中的许多事情一样,我们可以通过多种方式来做到这一点。 TypeArray 有一个采用各种形式的构造函数。例如

  • new Float32Array(12)

    这个版本创建了一个新的 ArrayBuffer ,在本例中为 12 * 4 字节。然后它创建 Float32Array 来查看它。

  • new Float32Array([4, 5, 6])

    这个版本创建了一个新的 ArrayBuffer ,在本例中为 3 * 4 字节。然后它创建
    Float32Array 来查看它。并将初始值设置为 4、5、6。

    注意,您还可以传递另一个 TypedArray 。例如

    new Float32Array(someUint8ArrayOf6Values) 将新建一个大小为 6 * 4 的
    ArrayBuffer,然后创建一个 Float32Array 来查看它,然后将现有视图中的值复制到新的 Float32Array
    中。这些值是按数字而不是二进制复制的。换句话说,它们是这样复制的

    srcArray.forEach((v, i) => dstArray[i] = v); 
    
  • new Float32Array(someArrayBuffer)

    这是我们之前使用的案例。在现有缓冲区上创建一个新的 Float32Array 视图。

  • new Float32Array(someArrayBuffer, byteOffset)

    这会在现有缓冲区上创建一个新的 Float32Array ,但会在 byteOffset 处开始查看视图

  • new Float32Array(someArrayBuffer, byteOffset, length)

    这会在现有缓冲区上创建一个新的 Float32Array 。视图从 byteOffset 开始,长度为 length
    个单位。因此,如果我们传递 3 或长度,视图将是 someArrayBuffer 的 3 个 float32 值长(12 字节)

使用最后一种形式,可以将上面的代码更改为

const kOurStructSizeBytes =
  4 + // velocity
  4 + // acceleration
  4 ; // frameCount
const ourStructData = new ArrayBuffer(kOurStructSizeBytes);
const velocityView = new Float32Array(ourStructData, 0, 1); //从1开始
const accelerationView = new Float32Array(ourStructData, 4, 1);//从4开始
const frameCountView = new Uint32Array(ourStructData, 8, 1);//从8开始
 
velocityView[0] = 1.2;
accelerationView[0] = 3.4;
frameCountView[0] = 56;

此外,每个 TypedArray 都具有以下属性

  • length: number of units
  • byteLength: size in bytes
  • byteOffset: offset in the TypeArray’s ArrayBuffer
  • buffer: the ArrayBuffer this TypeArray is viewing

TypeArray 有各种方法,很多都和 Array 类似,但 subarray 方法不同于Array 。subarray 创建了一个相同类型的新 TypedArray 视图。它的参数是 subarray(begin, end) ,不包括 end,即区间 [begin, end) 。所以 someTypedArray.subarray(5, 10) 使 someTypedArray 的元素 5 到 9 的相同 ArrayBuffer 成为新的 TypedArray 。

所以可以把上面的代码改成这样

const kOurStructSizeFloat32Units =
  1 + // velocity
  1 + // acceleration
  1 ; // frameCount
const ourStructData = new Float32Array(kOurStructSizeFloat32Units)
const velocityView = ourStructData.subarray(0, 1)
const accelerationView = new Float32Array(ourStructData, 1, 2);
const frameCountView = new Uint32Array(ourStructData, 2, 3);
 
velocityView[0] = 1.2;
accelerationView[0] = 3.4;
frameCountView[0] = 56;

WGSL 具有由 4 种基本类型构成的类型。它们是:

typedescriptionshort name
vec2<f32>a type with 2 f32svec2f
vec2<u32>a type with 2 u32svec2u
vec2<i32>a type with 2 i32svec2i
vec2<f16>a type with 2 f16svec2h
vec3<f32>a type with 3 f32svec3f
vec3<u32>a type with 3 u32svec3u
vec3<i32>a type with 3 i32svec3i
vec3<f16>a type with 3 f16svec3h
vec4<f32>a type with 4 f32svec4f
vec4<u32>a type with 4 u32svec4u
vec4<i32>a type with 4 i32svec4i
vec4<f16>a type with 4 f16svec4h
mat2x2<f32>a matrix of 2 vec2<f32>smat2x2f
mat2x2<u32>a matrix of 2 vec2<u32>smat2x2u
mat2x2<i32>a matrix of 2 vec2<i32>smat2x2i
mat2x2<f16>a matrix of 2 vec2<f16>smat2x2h
mat2x3<f32>a matrix of 2 vec3<f32>smat2x3f
mat2x3<u32>a matrix of 2 vec3<u32>smat2x3u
mat2x3<i32>a matrix of 2 vec3<i32>smat2x3i
mat2x3<f16>a matrix of 2 vec3<f16>smat2x3h
mat2x4<f32>a matrix of 2 vec4<f32>smat2x4f
mat2x4<u32>a matrix of 2 vec4<u32>smat2x4u
mat2x4<i32>a matrix of 2 vec4<i32>smat2x4i
mat2x4<f16>a matrix of 2 vec4<f16>smat2x4h
mat3x2<f32>a matrix of 3 vec2<f32>smat3x2f
mat3x2<u32>a matrix of 3 vec2<u32>smat3x2u
mat3x2<i32>a matrix of 3 vec2<i32>smat3x2i
mat3x2<f16>a matrix of 3 vec2<f16>smat3x2h
mat3x3<f32>a matrix of 3 vec3<f32>smat3x3f
mat3x3<u32>a matrix of 3 vec3<u32>smat3x3u
mat3x3<i32>a matrix of 3 vec3<i32>smat3x3i
mat3x3<f16>a matrix of 3 vec3<f16>smat3x3h
mat3x4<f32>a matrix of 3 vec4<f32>smat3x4f
mat3x4<u32>a matrix of 3 vec4<u32>smat3x4u
mat3x4<i32>a matrix of 3 vec4<i32>smat3x4i
mat3x4<f16>a matrix of 3 vec4<f16>smat3x4h
mat4x2<f32>a matrix of 4 vec2<f32>smat4x2f
mat4x2<u32>a matrix of 4 vec2<u32>smat4x2u
mat4x2<i32>a matrix of 4 vec2<i32>smat4x2i
mat4x2<f16>a matrix of 4 vec2<f16>smat4x2h
mat4x3<f32>a matrix of 4 vec3<f32>smat4x3f
mat4x3<u32>a matrix of 4 vec3<u32>smat4x3u
mat4x3<i32>a matrix of 4 vec3<i32>smat4x3i
mat4x3<f16>a matrix of 4 vec3<f16>smat4x3h
mat4x4<f32>a matrix of 4 vec4<f32>smat4x4f
mat4x4<u32>a matrix of 4 vec4<u32>smat4x4u
mat4x4<i32>a matrix of 4 vec4<i32>smat4x4i
mat4x4<f16>a matrix of 4 vec4<f16>smat4x4h

鉴于 vec3f 是一个有 3 个 f32 的类型,而 mat4x4f 是一个 4x4 的 f32 矩阵,所以它是 16 个 f32 ,你想想下面的结构在内存中看起来像什么?

struct Ex2 {
  scale: f32,
  offset: vec3f, //align 16
  projection: mat4x4f,
};

准备好了吗?答案揭晓!

在这里插入图片描述
这是怎么回事?事实证明每种类型都有对齐要求。对于给定的类型,它必须对齐到特定字节数的倍数。

以下是各种类型的尺寸和对齐方式。

typesizealign
i3244
u3244
f3244
f1622
vec288
vec288
vec288
vec244
vec31216
vec31216
vec31216
vec368
vec41616
vec41616
vec41616
vec488
mat2x2168
mat2x284
mat3x2248
mat3x2124
mat4x2328
mat4x2164
mat2x33216
mat2x3168
mat3x34816
mat3x3248
mat4x36416
mat4x3328
mat2x43216
mat2x4168
mat3x44816
mat3x4248
mat4x46416
mat4x4328

但是等等,还有!

你认为这个结构的布局是什么?

struct Ex3 {
  transform: mat3x3f,
  directions: array<vec3f, 4>,
};

array<type, count> 语法定义了一个包含 count 个元素的 type 数组。

答案揭晓…

在这里插入图片描述

如果查看对齐表,您会看到 vec3 有 16 个字节的对齐。这意味着每个 vec3 ,无论它是在矩阵还是数组中,最终都会有一个额外的对齐空间。

还有另外一个例子:

struct Ex4a {
  velocity: vec3f,
};
 
struct Ex4 {
  orientation: vec3f,
  size: f32, // align 4
  direction: array<vec3f, 1>, //align  16    
  scale: f32,
  info: Ex4a, //align  16
  friction: f32,
};

在这里插入图片描述

Why did size end up at byte offset 12, just after orientation but scale and friction got bumped offsets 32 and 64
为什么 size 在offset=12 处,而 scalefriction 的offset却为 32 和 64?

That’s because arrays and structs have their own own special alignment rules so even though the array is a single vec3f and the Ex4a struct is also a single vec3f they get aligned according to different rules.

那是因为数组和结构有它们自己的特殊对齐规则,所以即使数组是单个 vec3f 并且 Ex4a 结构也是单个 vec3f ,它们也会根据不同的规则对齐。

计算偏移量和大小是一个 PITA!(Computing Offset and Sizes is a PITA!)

在 WGSL 中计算数据的大小和偏移量可能是 WebGPU 最大的痛点。您需要自己计算这些偏移量并保持最新。如果您在着色器中某个结构的中间某处添加一个成员,您需要返回到您的 JavaScript 并更新所有偏移量。如果单个字节或长度错误,则传递给着色器的数据将是错误的。你不会得到错误,但你的着色器可能会做错事,因为它正在查看错误的数据。你的模型不会绘制或者你的计算会产生不好的结果。

幸运的是,有现成的库可以帮助解决这个问题。

webgpu-utils 就是其中一个

你给它 WGSL 代码,它给你一个 API 来为你做这一切。这样你就可以改变你的结构,而且通常情况下,一切都会正常进行。

例如,使用最后一个示例,我们可以像这样将它传递给 webgpu-utils

import {
  makeShaderDataDefinitions,
  makeStructuredView,
} from 'https://greggman.github.io/webgpu-utils/dist/0.x/webgpu-utils.module.js';
 
const code = `
struct Ex4a {
  velocity: vec3f,
};
 
struct Ex4 {
  orientation: vec3f,
  size: f32,
  direction: array<vec3f, 1>,
  scale: f32,
  info: Ex4a,
  friction: f32,
};
@group(0) @binding(0) var<uniform> myUniforms: Ex4;
 
...
`;
 
const defs = makeShaderDataDefinitions(code);
const myUniformValues = makeStructuredView(defs.uniforms.myUniforms);
 
// Set some values via set
myUniformValues.set({
  orientation: [1, 0, -1],
  size: 2,
  direction: [0, 1, 0],
  scale: 1.5,
  info: {
    velocity: [2, 3, 4],
  },
  friction: 0.1,
});
 
// now pass myUniformValues.arrayBuffer to WebGPU when needed.

是否使用这个特定的库或使用其他库或根本不使用,取决于您。对我来说,我经常会花 20-30-60 分钟试图弄清楚为什么某些东西不起作用只是为了发现我手动计算的偏移量或大小错误所以对于我自己的工作我宁愿使用一个库来避免这种痛苦.

如果您确实想手动执行此操作,这里有一个页面可以为您计算偏移量

注释

注释1

f16 支持是可选功能

原文地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/687176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis集群详解

目录 一、前言二、Redis 集群方案应该怎么做&#xff1f;都有哪些方案&#xff1f;三、twemproxy四、分而治之-codis1、codis简介2、Codis 分片原理3、不同的 Codis 实例之间槽位关系如何同步&#xff1f;4、假如Redis扩容&#xff0c;如何调整槽位的&#xff1f;5、codis优缺点…

巧用 overflow-scroll 实现丝滑轮播图

前言: 近期我在项目中就接到了一个完成轮播图组件的需求。最开始我也像大家一样&#xff0c;直接选择使用了知名的开源项目 “Swiper”&#xff0c;但是后来发现它在移动端项目中某些测试环境下会白屏一段时间。无论如何调试都不能修复这个问题&#xff0c;于是就自己上手写了个…

Java智慧工厂UWB高精度人员定位管理系统源码

一、系统概述&#xff1a; 智慧工厂高精度定位管理系统源码&#xff0c;采用UWB定位技术&#xff0c;通过部署UWB定位设备实现人、车、物精确定位&#xff0c;打造可寻、可视、可防、可控的一体化管控平台。UWB定位系统具有容量大、稳定性强、精度高、安装便捷、易维护、操作简…

pebblely无需代码连接集简云数据表的方法

1 使用场景 当电商UI接到一个设计产品图的需求时&#xff0c;按照常规的工作安排&#xff0c;从对接需求到最后完成效果图最短时间都要在5天左右&#xff0c;如果遇到高要求的客户&#xff0c;后期还需要在电脑上进一步调整细节&#xff0c;一张成片起码要花上数小时时间去完成…

LabVIEW开发压电陶瓷特性测试系统

LabVIEW开发压电陶瓷特性测试系统 目前&#xff0c;随着科学技术的飞速发展&#xff0c;各个领域都需要纳米级的精密定位系统。而压电定位系统是迄今为止已经得到认可的微纳定位系统。该系统主要由压电驱动系统、位移测量传感器和控制系统组成。其中&#xff0c;整个压电驱动系…

oracle 使用sql语句实现交换一个表中两条记录的某个字段的内容

不要太忙了 好好珍惜眼下 的真爱 &#xff08;马晓静sorry~&#xff09; 表 test --------------- id | i_order --------------- 1 | 22 2 | 23 --------------- 转换后结果为&#xff1a; --------------- id | i_order --------------- 1 | 23 2 | …

【云原生丶Docker】Docker镜像常用命令大全

镜像是Docker中最为核心也是最具创造性的概念&#xff01;在理解Docker的核心概念文章中&#xff0c;我们理解了镜像的含义&#xff0c;下面让我们一起操作一些镜像&#xff0c;包括拉取、推送镜像等操作。 1、镜像支持哪些命令 通过 docker image help 查看 Docker 支持的镜像…

解决nacos频繁输出get changegroupkeys日志

# 1、根据心跳日志&#xff0c;定位日志输出的包名 c.a.n.client.config.impl.ClientWorker : get changedGroupKeys:[]# 2、在 IDEA 中搜索 ClientWorker 所在的包路径 package com.alibaba.nacos.client.config.impl;# 3、在任意格式的配置文件中将该包路径的日志设置为…

Unity 工具 之 Azure OpenAI 功能接入到Unity 中的简单整理

Unity 工具 之 Azure OpenAI 功能接入到Unity 中的简单整理 目录 Unity 工具 之 Azure OpenAI 功能接入到Unity 中的简单整理 一、简单介绍 二、实现原理 三、注意实现 四、简单实现步骤 五、关键代码 六、附加 创建新的 .NET Core &#xff0c;获取 Azure.AI.OpenAI d…

公司个人年终工作总结【10篇】

公司个人年终工作总结1 20__年即将过去&#xff0c;在公司领导的悉心关怀下和同事们的帮助指导下&#xff0c;结合我自身的努力&#xff0c;在工作、学习等各方面都取得了长足的进步&#xff0c;尤其是在保险理赔专业知识和技能培养方面的成熟&#xff0c;使我成为一名合格的车…

Linux_查看硬盘占用情况

一、查看是什么占用了硬盘空间 df -h 这个命令查看的是显示目前在 Linux 系统上的所有文件系统磁盘使用情况&#xff0c;并根据大小适当显示&#xff08;-h 参数代表以可读的方式展示文件的大小&#xff09;。 一下为加 -h 和不加 -h 的结果 不加 -h 加 -h Filesystem&…

探秘安卓广播:揭秘Android广播机制的神奇之处

壹 广播机制 Android中的广播(Broadcast)机制用于进程/线程间通信&#xff0c;该机制使用了观察者模式。观察者模式是一种软件设计模式&#xff0c;该模式是基于消息的发布/订阅事件模型&#xff0c;该模型中的消息发布者是广播机制中的广播发送者&#xff0c;消息订阅者是广播…

ajax原理是什么?如何实现?

一、是什么 AJAX全称(Async Javascript and XML) 即异步的JavaScript 和XML&#xff0c;是一种创建交互式网页应用的网页开发技术&#xff0c;可以在不重新加载整个网页的情况下&#xff0c;与服务器交换数据&#xff0c;并且更新部分网页 Ajax的原理简单来说通过XmlHttpReq…

算法——查找表

查找&#xff0c;根据一个值查找另一个值&#xff0c;value值可以是容器&#xff0c;结构&#xff0c;这样可查找的元素就更多&#xff1b; 哈希冲突&#xff1a; 主关键字&#xff1a;可以唯一的标识一个记录的关键字&#xff0c;如准考证号&#xff1b; 此关键词&#xff…

RFID微图柜的操作流程以及功能介绍

微型图书馆智能书柜是一种基于 RFID 技术的图书自助借阅延伸服务的终端设备&#xff0c;可实现 24 小时无人看守的共享的智能化设备&#xff0c;可自助借还书、自动数据记录分析、自助提醒等一体化管理服务功能。 智能书柜的操作流程 借书&#xff1a;在操作页面上选择“借书…

【好书精读】网络是怎样连接的 —— 信号在网线和集线器中传输

&#xff08; 该图由我使用 AI 绘制 &#xff09; 目录 每个包都是独立传输的 防止网线中的信号衰减很重要 “双绞”是为了抑制噪声 集线器将信号发往所有线路 每个包都是独立传输的 从计算机发送出来的网络包会通过集线器 、 路由器等设备被转发 &#xff0c; 最 终到达…

解决 CentOS/Alma 安装 libpcap-devel 报错:No match for argument: libpcap-devel

环境&#xff1a;Alma 8.5、Centos 7.x 解决方案 Linux 安装软件的时候&#xff0c;需要 libpcap-devel 这个组件&#xff0c;执行命令&#xff1a;yum install libpcap-devel &#xff0c;然后报错如下&#xff1a; Last metadata expiration check: 0:05:24 ago on Mon 12…

5.3 Linux目录配置

5.3.1 Linux目录配置的依据--FHS 根据FHS的标准文件指出&#xff0c;他们的主要目的是希望让使用者可以了解到已安装软件通常放置于那个目录下&#xff0c; 所以他们希望独立的软件开发商、操作系统制作者、以及想要维护系统的使用者&#xff0c;都能够遵循FHS的标准。 也就是…

ECS 简略版说明五:Baking and entity scenes

目录 Baking and entity scenes Creating and editing sub scenes Accessing data in a baker Loading and unloading entity scenes Baking and entity scenes Baking 是一个把 sub scenes 转变成 entity scenes 的过程&#xff0c;使用 bakers和 baking systems: A sub …

数据中心供配电监控系统解决方案介绍 安科瑞 许敏

摘 要&#xff1a;供配电系统始终是数据中心比较重要的内容&#xff0c;在供配电系统能够得到平稳安全的运行的时候&#xff0c;才能够促使数据中心的相关设备具有比较可靠的动力源泉。在新型数据中心不断发展的过程中&#xff0c;其功率密度也相对比较大&#xff0c;对供电的要…