#内存布局 #WGSL #缓冲区 #WGPU #数据 #GPU #需求

encase

提供了一种将数据布局到GPU缓冲区的机制,确保满足WGSL的内存布局要求

15个版本 (8个破坏性)

0.9.0 2024年6月22日
0.7.0 2024年1月2日
0.6.1 2023年5月9日
0.5.0 2023年3月4日
0.1.3 2022年3月16日

#2 in 渲染

Download history 17711/week @ 2024-05-03 16458/week @ 2024-05-10 16387/week @ 2024-05-17 18716/week @ 2024-05-24 18766/week @ 2024-05-31 17788/week @ 2024-06-07 19168/week @ 2024-06-14 17077/week @ 2024-06-21 17257/week @ 2024-06-28 21712/week @ 2024-07-05 20329/week @ 2024-07-12 21828/week @ 2024-07-19 23700/week @ 2024-07-26 18816/week @ 2024-08-02 26052/week @ 2024-08-09 20959/week @ 2024-08-16

93,131 每月下载量
用于 752 个crate(9个直接使用)

MIT-0 许可证

115KB
2.5K SLoC

提供了一种将数据布局到GPU缓冲区的机制,确保满足WGSL的内存布局要求。

功能

  • 支持所有WGSL 主机可共享类型 + 包装类型 (&T, &mut T, Box<T>, ...)
  • 支持来自众多crate的数据类型作为 功能
  • 涵盖了广泛的使用案例(见 示例

动机

手动将数据布局到GPU缓冲区可能非常繁琐且容易出错。如何确保缓冲区中的数据布局正确?如何强制执行以确保未来的更改不会破坏这种微妙的平衡?

encase 允许您在编译时确保您的类型将被正确布局。

设计

核心特质是 ShaderType,它主要包含有关给定类型的元数据。

WriteIntoReadFromCreateFrom 特质分别代表类型写入缓冲区、从缓冲区读取和从缓冲区创建的能力。

大多数数据类型可以通过各自的宏实现上述特质

结构体 UniformBufferStorageBufferDynamicUniformBufferDynamicStorageBuffer 是围绕底层原始缓冲区(实现 BufferRef 和/或 BufferMut 的类型)的包装器,这取决于所需的功能。它们简化了读写/创建操作。

示例

将仿射变换写入统一缓冲区

use encase::{ShaderType, UniformBuffer};

#[derive(ShaderType)]
struct AffineTransform2D {
    matrix: glam::Mat2,
    translate: glam::Vec2
}

let transform = AffineTransform2D {
    matrix: glam::Mat2::IDENTITY,
    translate: glam::Vec2::ZERO,
};

let mut buffer = UniformBuffer::new(Vec::<u8>::new());
buffer.write(&transform).unwrap();
let byte_buffer = buffer.into_inner();

// write byte_buffer to GPU

assert_eq!(&byte_buffer, &[0, 0, 128, 63, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 0, 0]);

通过从特定偏移量读取动态统一缓冲区来创建向量实例

use encase::DynamicUniformBuffer;

// read byte_buffer from GPU
let byte_buffer = [1u8; 256 + 8];

let mut buffer = DynamicUniformBuffer::new(&byte_buffer);
buffer.set_offset(256);
let vector: mint::Vector2<i32> = buffer.create().unwrap();

assert_eq!(vector, mint::Vector2 { x: 16843009, y: 16843009 });

从存储缓冲区写入和读取数据

use encase::{ShaderType, ArrayLength, StorageBuffer};

#[derive(ShaderType)]
struct Positions {
    length: ArrayLength,
    #[size(runtime)]
    positions: Vec<mint::Point2<f32>>
}

let mut positions = Positions {
    length: ArrayLength,
    positions: Vec::from([
        mint::Point2 { x: 4.5, y: 3.4 },
        mint::Point2 { x: 1.5, y: 7.4 },
        mint::Point2 { x: 4.3, y: 1.9 },
    ])
};

let mut byte_buffer: Vec<u8> = Vec::new();

let mut buffer = StorageBuffer::new(&mut byte_buffer);
buffer.write(&positions).unwrap();

// write byte_buffer to GPU

// change length on GPU side
byte_buffer[0] = 2;

// read byte_buffer from GPU

let mut buffer = StorageBuffer::new(&mut byte_buffer);
buffer.read(&mut positions).unwrap();

assert_eq!(positions.positions.len(), 2);

将不同数据类型写入动态存储缓冲区

use encase::{ShaderType, DynamicStorageBuffer};

let mut byte_buffer: Vec<u8> = Vec::new();

let mut buffer = DynamicStorageBuffer::new_with_alignment(&mut byte_buffer, 64);
let offsets = [
    buffer.write(&[5.; 10]).unwrap(),
    buffer.write(&vec![3u32; 20]).unwrap(),
    buffer.write(&glam::Vec3::ONE).unwrap(),
];

// write byte_buffer to GPU

assert_eq!(offsets, [0, 64, 192]);

也支持写入未初始化的内存。

use std::mem::MaybeUninit;
use encase::{ShaderType, DynamicStorageBuffer};

let mut uninit_buffer: Vec<MaybeUninit<u8>> = Vec::new();

let mut buffer = DynamicStorageBuffer::new_with_alignment(&mut uninit_buffer, 64);
let offsets = [
    buffer.write(&[5.; 10]).unwrap(),
    buffer.write(&vec![3u32; 20]).unwrap(),
    buffer.write(&glam::Vec3::ONE).unwrap(),
];

// SAFETY: Vec<u8> and Vec<MaybeUninit<u8>> share the same layout.
let byte_buffer: Vec<u8> = unsafe { 
    Vec::from_raw_parts(
        uninit_buffer.as_mut_ptr().cast(), 
        uninit_buffer.len(), 
        uninit_buffer.capacity()
    ) 
};

std::mem::forget(uninit_buffer);

// write byte_buffer to GPU

assert_eq!(offsets, [0, 64, 192]);

依赖关系

~0.5–4MB
~90K SLoC