)
deck.gl 二进制数据支持改进基于属性描述符的 GPU 直通数据管线设计v8.0 RFC 解读【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl本文基于 deck.gl 仓库中的 Binary Support Improvements RFCv8.0 草案撰写系统梳理了 deck.gl 在 v8.x 阶段如何把二进制数据升级为一等公民允许data.attributes直接以访问器accessor名称作为键、映射到 loaders.gl 兼容的属性描述符从而让二进制缓冲直达 GPU、跳过 CPU 打包环节。读完本文你将理解二进制数据在 deck.gl 中的演进脉络、三种逻辑属性Logical/Deck/Shader Attribute的分层模型、新版属性描述符的完整字段语义以及它如何为未来的 Arrow 数据帧、GLSL 访问器与 GPU 数据帧支持铺路。背景layer 更新路径上的三大性能瓶颈在一次图层更新layer update过程中deck.gl 会依次执行以下三类开销最大的操作下载并解析源数据downloading and parsing the source data在 CPU 上打包属性packing the attributes on CPU将属性上传到 GPUuploading the attributes to GPU。对于数据量大、更新频率高的性能敏感型应用在服务器与客户端之间传输二进制格式binary format是常见做法。然而在 v7 之前即使数据以二进制形式下载完成应用仍必须在客户端把二进制数据重新构造成 JavaScript 数组白白增加内存与 CPU 开销。v7.x 的 Binary Data RFC 引入了两种彻底绕开这一步的用法从而消除了第 1 步的性能损失。方式一基于索引的访问器index-based accessor// binaryData 在服务器端打包格式为 [x, y, r, g, b, a] const DATA {src: binaryData, length: binaryData.length / 6}; new ScatterplotLayer({ data: DATA, getPosition: (object, {index, data, target}) { target[0] data.src[index * 6]; target[1] data.src[index * 6 1]; target[2] 0; return target; }, getColor: (object, {index, data, target}) { target[0] data.src[index * 6 2] * 255; target[1] data.src[index * 6 3] * 255; target[2] data.src[index * 6 4] * 255; target[3] data.src[index * 6 5] * 255; return target; }, ... })这种方式要求编写一个访问器在每一个对象索引处从二进制 blob 中读取相关信息。属性更新时deck.gl 会分配一个内部缓冲并按numInstance的次数调用访问器来填充它因此在第 2、3 步上性能与使用普通 JavaScript 数组相当——第 1 步的解析代价被消除了但 CPU 打包仍然存在。方式二直接提供外部缓冲supplying external buffers directly// binaryData 在服务器端打包格式为 [x, y, r, g, b, a] const DATA { length: binaryData.length / 6, attributes: { instancePositions: {value: binaryData, size: 2, stride: 4 * 6, offset: 0}, instanceColors: {value: binaryData, size: 4, stride: 4 * 6, offset: 4 * 2, normalized: true} } }; new PointCloudLayer({ data: DATA, getNormal: [0, 0, 0] });这种方式完全跳过了第 2 步可能是 deck.gl 所能达到的最高吞吐路径。但它的代价是应用前端与后端必须知晓图层的内部实现细节——包括属性名如instancePositions、instanceColors、数组类型与内存布局——这些信息并未文档化且在小版本之间容易被破坏。三个术语Logical / Deck / Shader AttributeRFC 为讨论二进制支持定义了三个层次分明的概念Logical attribute逻辑属性用户提供的属性格式遵循图层文档中的说明Deck attribute由AttributeManager通过对逻辑属性应用预定义变换后创建的属性与 WebGL 缓冲一一对应Shader attribute着色器属性顶点着色器实际看到的属性。一个 Deck attribute 可以通过同一个 WebGL 缓冲配合不同的访问器映射到多个 shader attribute。两个例子可以直观说明这种分层Scatterplot 位置逻辑属性是格式为x0, y0, z0, x1, y1, z1, ...的Float64ArrayDeck attribute 是交错排列的Float32Array格式为x0, y0, z0, x0Low, y0Low, z0Low, ...64 位精度拆分由此 Deck attribute 派生出两个 shader attributeinstancePositions与instancePositions64Low。Polygon 位置逻辑属性是拍平了所有多边形顶点的数组Deck attribute 包含归一化后的positions数组可能为了闭合环而增补顶点以及由三角剖分产生的indices数组。与 v7 方案的关系v7.x 的 Binary Data RFC 中还给出了更多二进制数据使用方式可作为理解本文 RFC 的铺垫非可迭代数据对象向data传入一个带length字段的非数组对象让访问器自己解释缓冲布局还可通过getPickingInfo为拾取事件补全有效的object值变宽数据切片用pathStartIndices之类的起始索引数组把二进制缓冲切成变长消息供PathLayer等使用按图层分类的二进制格式总览点云/散点/线/弧线等一对一实例化图层1-to-1 instanced layers二进制表示最直接路径/多边形/GeoJSON 等自定义几何图层更复杂聚合类图层Hexagon/Grid/ScreenGrid当时仍未考虑二进制输入。从源码结构看v7 时代的直接属性缓冲方案要求用户使用instancePositions这类内部属性名这正是 RFC 指出的“依赖未文档化信息、小版本间易损坏”的痛点也是 v8.0 要解决的核心问题。目标让二进制数据成为一等公民进入 v8.x 后RFC 提出两个目标所有核心图层都接受二进制数据输入作为逻辑属性all core layers accept binary data inputs as logical attributes在可能的情况下把二进制数据直接上传到 GPU并将基础打包操作如位置交错、变换矩阵构造从 CPU 转移到 GPU 上执行。这是一个“中间步骤”式的设计它先引入对用户可见的 API 变更同时保持与最终 Arrow或其他通用二进制数据帧支持的兼容性——RFC 明确将其定位为“走向 v8.x 完整 Arrow/通用二进制数据帧支持的一个中间步骤”。提案以访问器名为键的属性描述符核心提案非常简洁允许data.attributes使用访问器 prop 名如getPosition作为键映射到一个 loaders.gl 兼容的“属性描述符”attribute descriptor对象。data.attributes中的每一对键值从访问器 prop 名映射到以下三种格式之一luma.glBuffer实例一个 typed array类型化数组一个包含可选字段的对象字段含义可参考 WebGL vertexAttribPointer 规范bufferBuffervalueTypedArraysizeNumber——每个顶点属性包含的元素个数offsetNumber——第一个顶点属性在缓冲中的字节偏移strideNumber——相邻顶点属性起始位置之间的字节间距。value数组表示一个扁平缓冲其中保存着原本由函数式访问器为每个对象返回的值。例如getPosition: d [d.x, d.y, d.z]等价于getPosition: {value: new Float64Array([x0, y0, z0, x1, y1, z1, ...])}这个缓冲可以通过类似data.flatMap(d [d.x, d.y, d.z])的方式构造。变宽数据与 startIndices如果属性包含变宽数据variable-width data例如路径与多边形还必须指定data.startIndices。它是一个数组保存每个对象的第一个顶点在缓冲中的索引。对大多数图层而言默认假设为[0, 1, 2, 3, ...]即每个对象一个顶点。在 layer-props.ts 中data类型被定义为attributes?: Recordstring, TypedArray | Buffer | BinaryAttribute;而 attribute.ts 中的BinaryAttribute类型则概括了上述描述符export type BinaryAttribute PartialBufferAccessor {value?: TypedArray; buffer?: Buffer};startIndices在 layer-props.ts 中被声明为NumericArray | null。在 layer.ts 中getStartIndices()会优先返回this.props.startIndices显式值否则回退到内部状态中保存的state.startIndices——后者通常由图层在数据预处理阶段例如从数据对象推断每个对象的顶点数生成。示例 1PointCloudLayer/* binaryData 在服务器端打包 原始数据: [ {x, y, r, g, b}, // d0 {x, y, r, g, b}, // d1 ... ] 二进制数据: { positionsAndColors: [d0x, d0y, d0r, d0g, d0b, d0a, d1x, d1y, ...] } */ new PointCloudLayer({ data: { length: binaryData.length / 6, attributes: { getPosition: {value: binaryData, size: 2, stride: 24, offset: 0}, getColor: {value: binaryData, size: 4, stride: 24, offset: 8, normalized: true}, getNormal: {value: [0, 0, 0], constant: true} } } });注意这里的关键点length必须显式给出因为它不等于binaryData.length每个对象占 6 个 float即 24 字节getPosition与getColor共享同一个交错缓冲interleaved buffer通过stride: 24与不同的offset描述各自的布局颜色以 0–1 的浮点表示normalized: true与 v7 示例中“手动乘 255”的方式相比把归一化工作交给 GPUgetNormal使用{value: [0, 0, 0], constant: true}表示常量访问器——即便没有原始数据对象也能正常工作。示例 2PathLayer变宽数据/* binaryData 在服务器端打包 原始数据: [ {path: [[x, y], [x, y], [x, y]]}, // p0 {path: [[x, y], [x, y]]}, // p1 ... ] 二进制数据: { positions: [p00x, p00y, p01x, p01y, p02x, p02y, p10x, p10y, ...], colors: [p00r, p00g, p00b, p01r, p01g, p01b, p02r, p02g, p02b, p10r, p10g, p10b, ...], startingIndices: [0, 3, 5, ...] } */ new PathLayer({ data: { length: binaryData.positions.length / 2, startIndices: binaryData.startingIndices, attributes: { getPath: {value: binaryData.positions, size: 2}, getColor: {value: binaryData.colors, size: 3} } } });路径是典型的变宽数据p0 有 3 个顶点、p1 有 2 个顶点……因此必须通过startIndices: [0, 3, 5, ...]告诉 deck.gl 每个对象从哪个顶点开始。getPath与getColor是两个相互独立非交错的扁平缓冲size分别为 2 与 3。实现机制AttributeManager 如何消费属性描述符RFC 给出了三条实现要点逻辑属性直传如果某个访问器名出现在data.attributes中AttributeManager会把它作为“逻辑属性”传给对应的Attribute实例自动更新属性的 GPU 直通对最常见的自动更新属性auto-updated attributes属性会直接把缓冲上传到 GPU跳过本地的 CPU 打包步骤需要 CPU 预处理的属性对于需要 CPU 处理的属性例如多边形归一化、路径细分 tessellation、图标映射缓冲会被当作“取值来源”自动更新器像逐个对象调用访问器一样从其中取值由Attribute类在内部完成变换对应用隐藏实现细节。源码印证从 layer.ts 可以看到图层更新属性时会把props.data.attributes整体作为buffers传给attributeManager.update(...)const numInstances this.getNumInstances(); const startIndices this.getStartIndices(); attributeManager.update({ data: props.data, numInstances, startIndices, props, transitions: props.transitions, // ts-ignore (TS2339) property attribute is not present on some acceptable data types buffers: props.data.attributes, context: this });在 contenteditable="false">【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考